資料分析領域中,SQL 扮演著不可或缺的角色。作為與關聯式資料庫溝通的標準語言,SQL 提供了豐富的內建函式,讓開發者能夠直接在資料庫層級進行複雜的資料處理與分析。相較於將資料匯出後再用程式語言處理,善用 SQL 函式不僅能大幅提升執行效率,更能減少資料傳輸的開銷。本文將深入探討 SQL 函式在資料分析中的各種應用,從基礎的數學運算到進階的條件判斷,協助讀者建立完整的 SQL 資料分析知識體系。
數學與統計函式的應用
數學函式是 SQL 中最基礎也最常用的函式類別。這些函式能夠對數值資料進行各種運算,從簡單的四則運算到複雜的統計分析,都能透過 SQL 函式直接在資料庫中完成。
基礎數學函式
SQL 提供了完整的數學運算函式,涵蓋了日常資料處理中常見的需求。以下範例展示了這些函式的基本用法:
-- 基礎數學函式示範
-- 這些函式在不同的資料庫系統中語法大致相同
-- 絕對值函式
-- ABS() 傳回數值的絕對值,常用於計算差異值
SELECT ABS(-15.7) AS absolute_value;
-- 結果:15.7
-- 四捨五入函式
-- ROUND() 將數值四捨五入到指定的小數位數
-- 第二個參數指定保留的小數位數
SELECT ROUND(3.14159, 2) AS rounded_value;
-- 結果:3.14
-- 無條件捨去
-- FLOOR() 傳回小於或等於該數值的最大整數
SELECT FLOOR(7.89) AS floor_value;
-- 結果:7
-- 無條件進位
-- CEILING() 或 CEIL() 傳回大於或等於該數值的最小整數
SELECT CEILING(7.01) AS ceiling_value;
-- 結果:8
-- 取餘數
-- MOD() 計算除法的餘數,也可以使用 % 運算子
SELECT MOD(17, 5) AS remainder;
-- 結果:2
-- 冪次運算
-- POWER() 計算指定數值的指定次方
SELECT POWER(2, 10) AS power_result;
-- 結果:1024
-- 平方根
-- SQRT() 計算數值的平方根
SELECT SQRT(144) AS square_root;
-- 結果:12
這些基礎數學函式在實際應用中經常被組合使用。例如,在處理貨幣金額時,通常需要將計算結果四捨五入到兩位小數;在計算折扣後價格時,可能需要使用無條件捨去來確保價格不會超出預算。
統計分析函式
統計函式是資料分析的核心工具,能夠幫助我們理解資料的分佈特性。SQL 提供了完整的統計函式,讓我們能夠直接在資料庫中進行統計分析:
-- 建立測試資料表
-- 這個資料表包含學生的考試成績
CREATE TABLE exam_scores (
-- 學生編號,主鍵
student_id INT PRIMARY KEY,
-- 學生姓名
student_name VARCHAR(50),
-- 科目名稱
subject VARCHAR(30),
-- 考試成績
score DECIMAL(5, 2),
-- 考試日期
exam_date DATE
);
-- 插入測試資料
INSERT INTO exam_scores VALUES
(1, 'Alice', 'Mathematics', 85.5, '2025-11-01'),
(2, 'Bob', 'Mathematics', 72.0, '2025-11-01'),
(3, 'Charlie', 'Mathematics', 91.5, '2025-11-01'),
(4, 'Diana', 'Mathematics', 68.0, '2025-11-01'),
(5, 'Eve', 'Mathematics', 88.5, '2025-11-01'),
(6, 'Frank', 'Mathematics', 79.0, '2025-11-01'),
(7, 'Grace', 'Mathematics', 95.0, '2025-11-01'),
(8, 'Henry', 'Mathematics', 82.5, '2025-11-01');
-- 基礎聚合函式
-- 計算成績的基本統計量
SELECT
-- COUNT() 計算資料筆數
COUNT(*) AS total_students,
-- SUM() 計算總和
SUM(score) AS total_score,
-- AVG() 計算平均值
AVG(score) AS average_score,
-- MIN() 找出最小值
MIN(score) AS lowest_score,
-- MAX() 找出最大值
MAX(score) AS highest_score
FROM exam_scores
WHERE subject = 'Mathematics';
-- 結果:total_students=8, total_score=662.0, average_score=82.75,
-- lowest_score=68.0, highest_score=95.0
-- 進階統計函式
-- 計算成績的離散程度
SELECT
-- 人口標準差
-- STDDEV_POP() 假設資料為母體全部資料
-- 計算公式:sqrt(sum((x - mean)^2) / n)
STDDEV_POP(score) AS population_stddev,
-- 樣本標準差
-- STDDEV_SAMP() 假設資料為樣本
-- 計算公式:sqrt(sum((x - mean)^2) / (n-1))
-- 樣本標準差會比人口標準差稍大
STDDEV_SAMP(score) AS sample_stddev,
-- 人口變異數
-- VAR_POP() 計算人口變異數
VAR_POP(score) AS population_variance,
-- 樣本變異數
-- VAR_SAMP() 計算樣本變異數
VAR_SAMP(score) AS sample_variance
FROM exam_scores
WHERE subject = 'Mathematics';
-- 分組統計
-- 按照不同維度進行統計分析
SELECT
subject AS 科目,
COUNT(*) AS 人數,
ROUND(AVG(score), 2) AS 平均分數,
ROUND(STDDEV_SAMP(score), 2) AS 標準差,
MIN(score) AS 最低分,
MAX(score) AS 最高分
FROM exam_scores
GROUP BY subject
ORDER BY 平均分數 DESC;
標準差和變異數是衡量資料離散程度的重要指標。人口標準差適用於你已經擁有完整資料的情況,例如分析全班同學的成績;樣本標準差則適用於你只有部分資料,需要推估母體特性的情況,例如透過抽樣調查來推估市場偏好。
三角函式與數學常數
對於需要進行科學計算或地理空間運算的應用,SQL 也提供了完整的三角函式:
-- 三角函式示範
-- 這些函式在地理資訊系統和科學計算中經常使用
-- 取得圓周率
SELECT PI() AS pi_value;
-- 結果:3.141592653589793
-- 正弦函式(輸入為弧度)
SELECT SIN(PI() / 2) AS sine_90_degrees;
-- 結果:1.0
-- 餘弦函式
SELECT COS(PI()) AS cosine_180_degrees;
-- 結果:-1.0
-- 正切函式
SELECT TAN(PI() / 4) AS tangent_45_degrees;
-- 結果:1.0(約等於)
-- 角度與弧度轉換
-- RADIANS() 將角度轉換為弧度
SELECT RADIANS(180) AS radians_value;
-- 結果:3.141592653589793
-- DEGREES() 將弧度轉換為角度
SELECT DEGREES(PI()) AS degrees_value;
-- 結果:180.0
-- 實際應用:計算兩點之間的距離
-- 使用 Haversine 公式計算地球表面兩點之間的距離
-- 這在地理資訊系統中非常常用
-- 假設我們有一個城市座標表
CREATE TABLE cities (
city_name VARCHAR(50),
latitude DECIMAL(10, 6), -- 緯度
longitude DECIMAL(10, 6) -- 經度
);
INSERT INTO cities VALUES
('Taipei', 25.033964, 121.564468),
('Tokyo', 35.689487, 139.691711),
('Seoul', 37.566536, 126.977966);
-- 計算台北到東京的距離(公里)
-- 地球半徑約為 6371 公里
SELECT
6371 * 2 * ASIN(
SQRT(
POWER(SIN(RADIANS(35.689487 - 25.033964) / 2), 2) +
COS(RADIANS(25.033964)) * COS(RADIANS(35.689487)) *
POWER(SIN(RADIANS(139.691711 - 121.564468) / 2), 2)
)
) AS distance_km;
-- 結果:約 2100 公里
單位轉換
在進行跨國或跨系統的資料分析時,單位轉換是一個常見的需求。以下範例展示如何使用 SQL 函式進行各種單位轉換:
-- 單位轉換範例
-- 建立一個包含各種測量值的資料表
CREATE TABLE measurements (
id INT PRIMARY KEY AUTO_INCREMENT,
measurement_name VARCHAR(50),
value_imperial DECIMAL(10, 4), -- 英制單位值
unit_imperial VARCHAR(20) -- 英制單位
);
INSERT INTO measurements (measurement_name, value_imperial, unit_imperial) VALUES
('Distance to Moon', 252088, 'miles'),
('Car Speed', 65, 'mph'),
('Room Temperature', 72, 'fahrenheit'),
('Body Weight', 150, 'pounds'),
('Fuel Capacity', 15, 'gallons');
-- 英制轉公制的轉換查詢
SELECT
measurement_name AS 測量項目,
value_imperial AS 英制數值,
unit_imperial AS 英制單位,
-- 根據不同單位進行轉換
CASE
-- 英里轉公里:1 英里 = 1.60934 公里
WHEN unit_imperial = 'miles' THEN
ROUND(value_imperial * 1.60934, 2)
-- 英里/小時轉公里/小時
WHEN unit_imperial = 'mph' THEN
ROUND(value_imperial * 1.60934, 2)
-- 華氏轉攝氏:(°F - 32) × 5/9
WHEN unit_imperial = 'fahrenheit' THEN
ROUND((value_imperial - 32) * 5 / 9, 2)
-- 磅轉公斤:1 磅 = 0.453592 公斤
WHEN unit_imperial = 'pounds' THEN
ROUND(value_imperial * 0.453592, 2)
-- 加侖轉公升:1 加侖 = 3.78541 公升
WHEN unit_imperial = 'gallons' THEN
ROUND(value_imperial * 3.78541, 2)
ELSE value_imperial
END AS 公制數值,
CASE
WHEN unit_imperial = 'miles' THEN 'kilometers'
WHEN unit_imperial = 'mph' THEN 'km/h'
WHEN unit_imperial = 'fahrenheit' THEN 'celsius'
WHEN unit_imperial = 'pounds' THEN 'kilograms'
WHEN unit_imperial = 'gallons' THEN 'liters'
ELSE unit_imperial
END AS 公制單位
FROM measurements;
-- 月球距離計算練習
-- 月球距離地球 252,088 英里
-- 轉換為公里並四捨五入到整數
SELECT
ROUND(252088 * 1.60934, 0) AS moon_distance_km;
-- 結果:405,696 公里
資料型別轉換函式
在資料處理過程中,經常需要將資料從一種型別轉換為另一種型別。SQL 提供了多種型別轉換函式,讓我們能夠靈活地處理不同格式的資料。
CAST 函式
CAST 函式是 SQL 標準中定義的型別轉換函式,語法清晰且可讀性高:
-- CAST 函式基本語法
-- CAST(值 AS 目標型別)
-- 數值轉字串
-- 在需要將數值與字串連接時很有用
SELECT CAST(12345 AS CHAR) AS string_value;
-- 結果:'12345'
-- 字串轉數值
-- 在進行數值運算前需要先轉換
SELECT CAST('456.78' AS DECIMAL(10, 2)) AS numeric_value;
-- 結果:456.78
-- 日期時間轉換
-- 從 DATETIME 中取出日期部分
SELECT CAST('2025-11-27 14:30:00' AS DATE) AS date_only;
-- 結果:2025-11-27
-- 從 DATETIME 中取出時間部分
SELECT CAST('2025-11-27 14:30:00' AS TIME) AS time_only;
-- 結果:14:30:00
-- 小數轉整數
-- 會直接截斷小數部分(不是四捨五入)
SELECT CAST(123.789 AS SIGNED) AS integer_value;
-- 結果:123
-- 實際應用場景
-- 訂單資料表中的日期時間處理
CREATE TABLE online_orders (
order_id INT PRIMARY KEY,
customer_name VARCHAR(100),
order_datetime DATETIME,
total_amount DECIMAL(10, 2)
);
INSERT INTO online_orders VALUES
(1001, 'Alice Chen', '2025-11-27 09:15:30', 1250.00),
(1002, 'Bob Wang', '2025-11-27 14:22:45', 3680.50),
(1003, 'Charlie Lin', '2025-11-27 18:05:10', 890.00),
(1004, 'Diana Wu', '2025-11-28 10:30:00', 2150.75);
-- 按日期統計每日訂單數量和金額
SELECT
-- 將 DATETIME 轉換為 DATE 進行分組
CAST(order_datetime AS DATE) AS order_date,
COUNT(*) AS order_count,
SUM(total_amount) AS daily_total,
AVG(total_amount) AS average_amount
FROM online_orders
GROUP BY CAST(order_datetime AS DATE)
ORDER BY order_date;
-- 格式化輸出
-- 組合字串和數值
SELECT
CONCAT(
customer_name,
' 的訂單金額為 NT$',
CAST(total_amount AS CHAR)
) AS order_summary
FROM online_orders;
CONVERT 函式
CONVERT 函式是 CAST 的替代方案,在 MySQL 中提供了一些額外的功能,特別是字元集轉換:
-- CONVERT 函式基本語法
-- CONVERT(值, 目標型別)
-- 或
-- CONVERT(值 USING 字元集)
-- 型別轉換
SELECT CONVERT('2025-11-27', DATE) AS converted_date;
-- 字元集轉換
-- 這在處理多語言資料時特別有用
SELECT CONVERT('中文字串' USING utf8mb4) AS utf8_string;
-- 二進位轉換
SELECT CONVERT('Hello' USING binary) AS binary_string;
空值處理函式
NULL 值在資料庫中代表「未知」或「不適用」,但在實際應用中,NULL 值可能會導致計算錯誤或顯示問題。SQL 提供了多種函式來處理 NULL 值。
COALESCE 函式
COALESCE 函式傳回參數列表中第一個非 NULL 的值,是處理空值最常用的函式:
-- COALESCE 基本用法
-- COALESCE(值1, 值2, ..., 值N)
-- 從左到右檢查,傳回第一個非 NULL 的值
SELECT COALESCE(NULL, NULL, 'Default') AS result;
-- 結果:'Default'
SELECT COALESCE(NULL, 'First', 'Second') AS result;
-- 結果:'First'
SELECT COALESCE('Actual', 'Backup', 'Default') AS result;
-- 結果:'Actual'
-- 實際應用:求職者資料表
CREATE TABLE job_candidates (
candidate_id INT PRIMARY KEY,
full_name VARCHAR(100),
current_employer VARCHAR(100), -- 可能為 NULL
preferred_salary DECIMAL(10, 2), -- 可能為 NULL
years_experience INT
);
INSERT INTO job_candidates VALUES
(1, 'Alice Chen', 'TechCorp', 85000, 5),
(2, 'Bob Wang', NULL, 70000, 3),
(3, 'Charlie Lin', 'DataInc', NULL, 7),
(4, 'Diana Wu', NULL, NULL, 2);
-- 使用 COALESCE 提供預設值
SELECT
full_name AS 姓名,
-- 如果目前雇主為 NULL,顯示「求職中」
COALESCE(current_employer, '求職中') AS 目前雇主,
-- 如果期望薪資為 NULL,顯示「面議」
COALESCE(
CAST(preferred_salary AS CHAR),
'面議'
) AS 期望薪資,
years_experience AS 年資
FROM job_candidates;
-- 結果:
-- 姓名 | 目前雇主 | 期望薪資 | 年資
-- Alice Chen | TechCorp | 85000 | 5
-- Bob Wang | 求職中 | 70000 | 3
-- Charlie Lin | DataInc | 面議 | 7
-- Diana Wu | 求職中 | 面議 | 2
-- 多層級預設值
-- 當有多個備選欄位時,COALESCE 特別有用
CREATE TABLE contact_info (
user_id INT,
mobile_phone VARCHAR(20),
home_phone VARCHAR(20),
work_phone VARCHAR(20),
email VARCHAR(100)
);
INSERT INTO contact_info VALUES
(1, '0912-345-678', '02-1234-5678', '02-8765-4321', '[email protected]'),
(2, NULL, '03-1234-5678', NULL, '[email protected]'),
(3, NULL, NULL, '04-1234-5678', '[email protected]'),
(4, NULL, NULL, NULL, '[email protected]');
-- 選擇優先的聯絡電話
SELECT
user_id,
-- 優先順序:手機 > 住家 > 公司
COALESCE(
mobile_phone,
home_phone,
work_phone,
'請使用 Email 聯絡'
) AS primary_phone,
email
FROM contact_info;
IFNULL 和 NULLIF 函式
除了 COALESCE,SQL 還提供了其他處理 NULL 值的函式:
-- IFNULL 函式(MySQL 特有)
-- IFNULL(值, 預設值)
-- 如果值為 NULL,傳回預設值
SELECT IFNULL(NULL, 'Default') AS result;
-- 結果:'Default'
-- IFNULL 與 COALESCE 的差異
-- IFNULL 只接受兩個參數,COALESCE 可以接受多個
SELECT IFNULL(current_employer, '求職中') AS employer
FROM job_candidates;
-- NULLIF 函式
-- NULLIF(值1, 值2)
-- 如果值1等於值2,傳回 NULL;否則傳回值1
-- 常用於避免除以零的錯誤
CREATE TABLE sales_data (
product_id INT,
revenue DECIMAL(10, 2),
units_sold INT
);
INSERT INTO sales_data VALUES
(1, 10000, 100),
(2, 5000, 50),
(3, 0, 0); -- 沒有銷售
-- 計算平均單價
-- 如果 units_sold 為 0,NULLIF 會傳回 NULL,避免除以零
SELECT
product_id,
revenue,
units_sold,
revenue / NULLIF(units_sold, 0) AS average_price
FROM sales_data;
-- product_id 3 的 average_price 會是 NULL 而不是錯誤
-- 實用範例:處理分母為零
-- 計算成長率時避免除以零
CREATE TABLE monthly_sales (
year_month VARCHAR(7),
sales_amount DECIMAL(12, 2)
);
INSERT INTO monthly_sales VALUES
('2025-09', 0),
('2025-10', 50000),
('2025-11', 75000);
-- 計算月成長率
SELECT
year_month,
sales_amount,
LAG(sales_amount) OVER (ORDER BY year_month) AS prev_month,
-- 使用 NULLIF 避免除以零
ROUND(
(sales_amount - LAG(sales_amount) OVER (ORDER BY year_month)) * 100 /
NULLIF(LAG(sales_amount) OVER (ORDER BY year_month), 0),
2
) AS growth_rate_percent
FROM monthly_sales;
條件判斷函式
條件判斷是資料處理中的核心功能,SQL 提供了多種方式來實現條件邏輯。
IF 函式
IF 函式是最簡單的條件判斷函式,適用於二元判斷:
-- IF 函式基本語法
-- IF(條件, 真值結果, 假值結果)
-- 基本範例
SELECT IF(10 > 5, 'Greater', 'Less or Equal') AS comparison;
-- 結果:'Greater'
-- 判斷及格與否
SELECT IF(85 >= 60, 'Pass', 'Fail') AS result;
-- 結果:'Pass'
-- 實際應用:考試成績判定
CREATE TABLE test_results (
student_id INT,
student_name VARCHAR(50),
subject VARCHAR(30),
score INT
);
INSERT INTO test_results VALUES
(1, 'Lisa', 'Math', 85),
(2, 'Bart', 'Math', 42),
(3, 'Nelson', 'Math', 28),
(4, 'Milhouse', 'Math', 61),
(5, 'Ralph', 'Math', 55);
-- 判定考試是否及格
SELECT
student_name AS 學生,
score AS 分數,
IF(score >= 60, '及格', '不及格') AS 判定結果
FROM test_results;
-- 結果:
-- 學生 | 分數 | 判定結果
-- Lisa | 85 | 及格
-- Bart | 42 | 不及格
-- Nelson | 28 | 不及格
-- Milhouse | 61 | 及格
-- Ralph | 55 | 不及格
-- 巢狀 IF
-- 雖然可行,但不建議使用過多層巢狀,改用 CASE 更清晰
SELECT
student_name,
score,
IF(score >= 90, '優秀',
IF(score >= 60, '及格', '不及格')
) AS grade
FROM test_results;
CASE 表達式
CASE 表達式是更強大的條件判斷工具,可以處理多個條件分支:
-- CASE 表達式有兩種形式
-- 1. 簡單 CASE:比較單一表達式與多個值
-- 2. 搜尋 CASE:評估多個布林表達式
-- 簡單 CASE 語法
SELECT
student_name,
score,
CASE score
WHEN 100 THEN '滿分'
WHEN 90 THEN '優秀'
WHEN 60 THEN '及格邊緣'
ELSE '其他'
END AS grade_note
FROM test_results;
-- 搜尋 CASE 語法(更常用)
SELECT
student_name AS 學生,
score AS 分數,
CASE
WHEN score >= 90 THEN '優秀'
WHEN score >= 80 THEN '良好'
WHEN score >= 70 THEN '中等'
WHEN score >= 60 THEN '及格'
ELSE '不及格'
END AS 等第
FROM test_results
ORDER BY score DESC;
-- 結果:
-- 學生 | 分數 | 等第
-- Lisa | 85 | 良好
-- Milhouse | 61 | 及格
-- Ralph | 55 | 不及格
-- Bart | 42 | 不及格
-- Nelson | 28 | 不及格
-- 實際應用:電工頭銜分配
CREATE TABLE electricians (
emp_id INT,
name VARCHAR(50),
years_experience INT
);
INSERT INTO electricians VALUES
(1, 'John', 3),
(2, 'Mary', 7),
(3, 'David', 12),
(4, 'Sarah', 5),
(5, 'Tom', 1);
-- 根據經驗年數分配頭銜
SELECT
name AS 姓名,
years_experience AS 年資,
CASE
-- 經驗少於 5 年:學徒
WHEN years_experience < 5 THEN 'Apprentice'
-- 經驗 5-10 年:技師
WHEN years_experience <= 10 THEN 'Journeyman'
-- 經驗超過 10 年:師傅
ELSE 'Master Electrician'
END AS 頭銜
FROM electricians
ORDER BY years_experience DESC;
-- CASE 在聚合函式中的應用
-- 這是一個非常強大的技巧,可以進行條件聚合
CREATE TABLE orders (
order_id INT,
order_date DATE,
amount DECIMAL(10, 2),
status VARCHAR(20)
);
INSERT INTO orders VALUES
(1, '2025-11-01', 1500, 'completed'),
(2, '2025-11-05', 2300, 'completed'),
(3, '2025-11-10', 800, 'cancelled'),
(4, '2025-11-15', 3200, 'completed'),
(5, '2025-11-20', 1100, 'pending'),
(6, '2025-11-25', 2800, 'completed');
-- 使用 CASE 進行條件聚合
SELECT
-- 計算各狀態的訂單數
COUNT(CASE WHEN status = 'completed' THEN 1 END) AS completed_count,
COUNT(CASE WHEN status = 'pending' THEN 1 END) AS pending_count,
COUNT(CASE WHEN status = 'cancelled' THEN 1 END) AS cancelled_count,
-- 計算各狀態的金額總計
SUM(CASE WHEN status = 'completed' THEN amount ELSE 0 END) AS completed_amount,
SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) AS pending_amount,
SUM(CASE WHEN status = 'cancelled' THEN amount ELSE 0 END) AS cancelled_amount
FROM orders;
CASE 表達式流程圖
以下是 CASE 表達式執行邏輯的視覺化呈現:
@startuml
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100
start
:取得待評估的值;
:檢查第一個 WHEN 條件;
if (條件1成立?) then (是)
:傳回結果1;
stop
else (否)
:檢查第二個 WHEN 條件;
endif
if (條件2成立?) then (是)
:傳回結果2;
stop
else (否)
:檢查下一個條件;
endif
if (還有更多條件?) then (是)
:繼續檢查;
else (否)
if (有 ELSE 子句?) then (是)
:傳回 ELSE 結果;
else (否)
:傳回 NULL;
endif
stop
endif
@enduml
系統資訊與實用函式
SQL 還提供了許多實用函式,用於取得系統資訊或進行特殊操作。
DISTINCT 與聚合函式的組合
DISTINCT 關鍵字用於去除重複值,可以與聚合函式結合使用:
-- 建立客戶資料表
CREATE TABLE customers (
customer_id INT,
customer_name VARCHAR(100),
country VARCHAR(50),
city VARCHAR(50)
);
INSERT INTO customers VALUES
(1, 'Alice', 'Taiwan', 'Taipei'),
(2, 'Bob', 'Taiwan', 'Kaohsiung'),
(3, 'Charlie', 'Japan', 'Tokyo'),
(4, 'Diana', 'Taiwan', 'Taipei'),
(5, 'Eve', 'Japan', 'Osaka'),
(6, 'Frank', 'Korea', 'Seoul');
-- 取得不重複的國家列表
SELECT DISTINCT country AS 國家
FROM customers
ORDER BY country;
-- 結果:
-- 國家
-- Japan
-- Korea
-- Taiwan
-- 計算不重複的國家數量
-- COUNT(DISTINCT column) 是非常常用的組合
SELECT COUNT(DISTINCT country) AS 國家數量
FROM customers;
-- 結果:3
-- 多欄位 DISTINCT
-- 傳回國家和城市的唯一組合
SELECT DISTINCT country, city
FROM customers
ORDER BY country, city;
-- DISTINCT 與聚合函式的進階應用
SELECT
country AS 國家,
COUNT(*) AS 客戶總數,
COUNT(DISTINCT city) AS 城市數量
FROM customers
GROUP BY country
ORDER BY 客戶總數 DESC;
-- 結果:
-- 國家 | 客戶總數 | 城市數量
-- Taiwan | 3 | 2
-- Japan | 2 | 2
-- Korea | 1 | 1
系統資訊函式
這些函式用於取得資料庫系統的相關資訊:
-- DATABASE() 函式
-- 傳回目前使用的資料庫名稱
USE company_db;
SELECT DATABASE() AS current_database;
-- 結果:company_db
-- VERSION() 函式
-- 傳回資料庫伺服器的版本
SELECT VERSION() AS mysql_version;
-- 結果:8.0.32(依實際版本而異)
-- USER() 和 CURRENT_USER() 函式
-- 傳回目前連線的使用者資訊
SELECT
USER() AS connected_user,
CURRENT_USER() AS authenticated_user;
-- CONNECTION_ID() 函式
-- 傳回目前連線的唯一識別碼
SELECT CONNECTION_ID() AS connection_id;
-- LAST_INSERT_ID() 函式
-- 傳回最近一次 AUTO_INCREMENT 產生的值
-- 這在插入資料後需要取得新記錄 ID 時很有用
CREATE TABLE products (
product_id INT PRIMARY KEY AUTO_INCREMENT,
product_name VARCHAR(100)
);
INSERT INTO products (product_name) VALUES ('Laptop');
SELECT LAST_INSERT_ID() AS new_product_id;
-- 查詢系統資訊的綜合範例
SELECT
DATABASE() AS 資料庫,
VERSION() AS 版本,
USER() AS 使用者,
CONNECTION_ID() AS 連線ID,
NOW() AS 目前時間;
綜合應用範例
以下是一個綜合運用多種 SQL 函式的實際案例,展示如何進行完整的資料分析:
-- 綜合案例:銷售資料分析報表
-- 建立銷售資料表
CREATE TABLE sales_records (
sale_id INT PRIMARY KEY AUTO_INCREMENT,
sale_date DATE,
product_name VARCHAR(100),
category VARCHAR(50),
quantity INT,
unit_price DECIMAL(10, 2),
discount_rate DECIMAL(3, 2),
salesperson VARCHAR(50),
region VARCHAR(50)
);
-- 插入測試資料
INSERT INTO sales_records
(sale_date, product_name, category, quantity, unit_price, discount_rate, salesperson, region)
VALUES
('2025-11-01', 'Laptop Pro', 'Electronics', 5, 35000, 0.10, 'Alice', 'North'),
('2025-11-03', 'Office Chair', 'Furniture', 10, 8000, 0.15, 'Bob', 'South'),
('2025-11-05', 'Laptop Pro', 'Electronics', 3, 35000, 0.10, 'Charlie', 'North'),
('2025-11-07', 'Desk Lamp', 'Electronics', 20, 1200, 0.05, 'Alice', 'East'),
('2025-11-10', 'Office Desk', 'Furniture', 8, 15000, 0.20, 'Diana', 'South'),
('2025-11-12', 'Monitor', 'Electronics', 15, 12000, 0.08, 'Bob', 'West'),
('2025-11-15', 'Keyboard', 'Electronics', 30, 2500, 0.00, 'Charlie', 'North'),
('2025-11-18', 'Mouse', 'Electronics', 50, 800, 0.00, 'Alice', 'East'),
('2025-11-20', 'Bookshelf', 'Furniture', 6, 5000, 0.10, 'Diana', 'South'),
('2025-11-22', 'Laptop Basic', 'Electronics', 8, 25000, 0.12, 'Bob', 'West');
-- 銷售分析報表
SELECT
-- 基本資訊
sale_id AS 銷售編號,
sale_date AS 銷售日期,
product_name AS 產品名稱,
-- 使用 CASE 分類產品價位
CASE
WHEN unit_price >= 20000 THEN '高價位'
WHEN unit_price >= 5000 THEN '中價位'
ELSE '低價位'
END AS 價位分類,
-- 計算原始金額
quantity * unit_price AS 原始金額,
-- 計算折扣金額
ROUND(quantity * unit_price * discount_rate, 2) AS 折扣金額,
-- 計算實際金額
ROUND(quantity * unit_price * (1 - discount_rate), 2) AS 實際金額,
-- 使用 COALESCE 處理可能的 NULL
COALESCE(salesperson, '未指定') AS 銷售人員,
-- 使用 IF 判斷是否達到業績目標
IF(quantity * unit_price * (1 - discount_rate) >= 100000,
'達標', '未達標') AS 業績判定
FROM sales_records
ORDER BY sale_date;
-- 按區域統計分析
SELECT
region AS 區域,
COUNT(*) AS 銷售筆數,
COUNT(DISTINCT salesperson) AS 銷售人員數,
COUNT(DISTINCT category) AS 產品類別數,
-- 金額統計
SUM(quantity * unit_price * (1 - discount_rate)) AS 總銷售額,
ROUND(AVG(quantity * unit_price * (1 - discount_rate)), 2) AS 平均銷售額,
-- 使用 CASE 計算各類別銷售額
SUM(CASE WHEN category = 'Electronics'
THEN quantity * unit_price * (1 - discount_rate)
ELSE 0 END) AS 電子產品銷售額,
SUM(CASE WHEN category = 'Furniture'
THEN quantity * unit_price * (1 - discount_rate)
ELSE 0 END) AS 傢俱銷售額,
-- 計算電子產品佔比
ROUND(
SUM(CASE WHEN category = 'Electronics'
THEN quantity * unit_price * (1 - discount_rate)
ELSE 0 END) * 100 /
NULLIF(SUM(quantity * unit_price * (1 - discount_rate)), 0),
2
) AS 電子產品佔比
FROM sales_records
GROUP BY region
ORDER BY 總銷售額 DESC;
-- 銷售人員績效排名
SELECT
salesperson AS 銷售人員,
COUNT(*) AS 銷售筆數,
SUM(quantity) AS 總銷售數量,
SUM(quantity * unit_price * (1 - discount_rate)) AS 總銷售額,
ROUND(AVG(discount_rate) * 100, 2) AS 平均折扣率,
-- 績效等級
CASE
WHEN SUM(quantity * unit_price * (1 - discount_rate)) >= 200000 THEN 'A'
WHEN SUM(quantity * unit_price * (1 - discount_rate)) >= 100000 THEN 'B'
ELSE 'C'
END AS 績效等級
FROM sales_records
GROUP BY salesperson
ORDER BY 總銷售額 DESC;
SQL 函式的效能考量
在使用 SQL 函式時,效能是一個重要的考量因素。以下是一些最佳實踐:
-- 效能考量範例
-- 不佳的做法:在 WHERE 子句中對欄位使用函式
-- 這會導致無法使用索引
SELECT * FROM orders
WHERE YEAR(order_date) = 2025; -- 無法使用 order_date 的索引
-- 較佳的做法:使用範圍條件
SELECT * FROM orders
WHERE order_date >= '2025-01-01'
AND order_date < '2026-01-01'; -- 可以使用索引
-- 不佳的做法:重複計算
SELECT
product_name,
quantity * unit_price AS subtotal,
quantity * unit_price * 0.05 AS tax,
quantity * unit_price * 1.05 AS total
FROM sales_records;
-- 較佳的做法:使用子查詢或 CTE 避免重複計算
WITH calculated AS (
SELECT
product_name,
quantity * unit_price AS subtotal
FROM sales_records
)
SELECT
product_name,
subtotal,
subtotal * 0.05 AS tax,
subtotal * 1.05 AS total
FROM calculated;
-- 使用適當的資料型別
-- CAST 操作會消耗資源,應該在資料庫設計時就使用正確的型別
-- 避免頻繁的型別轉換
SQL 函式是資料分析不可或缺的工具。透過本文的介紹,讀者應該能夠理解各類 SQL 函式的用途和使用方式,包括數學統計函式、資料型別轉換、空值處理和條件判斷。這些函式的靈活組合,能夠讓我們直接在資料庫層級完成複雜的資料處理和分析工作,大幅提升工作效率。在實際應用中,建議讀者根據具體需求選擇適當的函式,並注意效能影響,以建構高效能的資料分析解決方案。