首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

跟我學 MS SQL Server(二):SELECT 進階選項 — 排序、去重、彙總與分組完全指南

     編輯:關於SqlServer

一、SELECT 子句與選項架構

SELECT [ALL | DISTINCT] 
FROM 
[WHERE ]
[GROUP BY  [, ...n]]
[HAVING ]
[ORDER BY  [ASC | DESC] [, ...n]]

二、ORDER BY:多層次排序規則

基本語法

-- 單欄位升序 (預設)
SELECT * FROM Contacts ORDER BY FirstName;

-- 多欄位:優先級由左至右遞減
SELECT * FROM Contacts ORDER BY Company, LastName, FirstName;

-- 混合排序:降序 + 升序
SELECT * FROM Sales ORDER BY NetAmount DESC, LastName ASC, FirstName ASC;

進階技巧

  • 排序欄位不必在 SELECT 列表:`SELECT Company FROM Sales ORDER BY StartDate` — 合法且常用
  • 運算式排序:`ORDER BY YEAR(OrderDate), MONTH(OrderDate)`
  • NULL 排序:`ORDER BY Col ASC` → NULL 在前;`DESC` → NULL 在後 (可用 `ORDER BY CASE WHEN Col IS NULL THEN 1 ELSE 0 END, Col` 控制)
  • 動態排序:`ORDER BY CASE @SortCol WHEN 'Name' THEN Name WHEN 'Date' THEN CAST(OrderDate AS VARCHAR(20)) END`
  • 分頁 (2012+):`ORDER BY Id OFFSET @PageSize * (@PageNum-1) ROWS FETCH NEXT @PageSize ROWS ONLY`

三、DISTINCT:去重邏輯與陷阱

核心規則

  • 適用於**整個 SELECT 列表**,非單一欄位
  • 比較所有輸出欄位值,**完全相同才視為重複**
  • 執行順序:`WHERE` → `GROUP BY` → `HAVING` → `SELECT DISTINCT` → `ORDER BY`

範例與陷阱

-- 僅回傳不重複公司名
SELECT DISTINCT Company FROM Sales;

-- 公司+姓名組合去重 (同一公司不同人不重複)
SELECT DISTINCT Company, LastName, FirstName FROM Sales;

-- 陷阱:DISTINCT + ORDER BY 不在 SELECT 列表
SELECT DISTINCT Company FROM Sales ORDER BY StartDate; -- 合法但語義模糊

-- 陷阱:大型資料集 DISTINCT 極慢 (需排序/雜湊)
-- 改用 GROUP BY (可利用索引) 或應用層去重

四、彙總函數:COUNT、SUM、AVG、MIN、MAX

函數語法忽略 NULL回傳型別典型用途
`COUNT(*)``COUNT(*)`否 (計算列數)`INT`列計數,含 NULL 列
`COUNT(col)``COUNT(Column)`是`INT`非 NULL 值計數
`COUNT(DISTINCT col)``COUNT(DISTINCT Column)`是`INT`唯一值計數
`SUM``SUM(Column)`是同欄位/數值數值加總
`AVG``AVG(Column)`是數值/十進制平均值
`MIN``MIN(Column)`是同欄位最小值
`MAX``MAX(Column)`是同欄位最大值

關鍵行為

  • 彙總函數**自動忽略 NULL** (除 `COUNT(*)`)
  • `SUM`/`AVG` 字串型別隱式轉換 → 錯誤,需 `CAST`
  • `AVG` 整數欄位回傳整數 (截斷),建議 `AVG(CAST(Col AS DECIMAL(10,2)))`
  • `MIN`/`MAX` 適用於所有可比較型別 (含字串、日期)

五、GROUP BY:分組邏輯與進階用法

標準分組

SELECT Company, COUNT(*) AS OrderCount, SUM(NetAmount) AS TotalSales
FROM Sales
WHERE OrderDate >= '2023-01-01'
GROUP BY Company
HAVING SUM(NetAmount) > 10000
ORDER BY TotalSales DESC;

進階分組 (2008+)

-- ROLLUP:層級小計 (Company → 總計)
SELECT Company, YEAR(OrderDate) AS Yr, SUM(NetAmount) AS Total
FROM Sales
GROUP BY ROLLUP (Company, YEAR(OrderDate));

-- CUBE:所有維度組合小計
SELECT Company, Region, SUM(NetAmount) AS Total
FROM Sales
GROUP BY CUBE (Company, Region);

-- GROUPING SETS:自定義組合
SELECT Company, Region, SUM(NetAmount) AS Total
FROM Sales
GROUP BY GROUPING SETS ((Company, Region), (Company), (Region), ());

GROUPING 函數識別小計列

SELECT
    CASE WHEN GROUPING(Company) = 1 THEN 'ALL' ELSE Company END AS Company,
    CASE WHEN GROUPING(Region) = 1 THEN 'ALL' ELSE Region END AS Region,
    SUM(NetAmount) AS Total
FROM Sales
GROUP BY CUBE (Company, Region);

六、HAVING vs WHERE:篩選時機差異

子句執行階段可用彙總典型用途
`WHERE`分組前 (列級)否基礎資料篩選:日期、狀態、代碼
`HAVING`分組後 (群組級)是彙總條件:總額>閾值、筆數>N
-- 正確:WHERE 篩選原始列,HAVING 篩選群組
SELECT Company, COUNT(*) AS Cnt
FROM Sales
WHERE OrderDate >= '2023-01-01'  -- 列級篩選
GROUP BY Company
HAVING COUNT(*) > 100;           -- 群組級篩選

七、綜合實戰:銷售儀表板查詢

WITH MonthlySales AS (
    SELECT
        Company,
        YEAR(OrderDate) AS SalesYear,
        MONTH(OrderDate) AS SalesMonth,
        SUM(NetAmount) AS MonthlyTotal,
        COUNT(*) AS OrderCount
    FROM Sales
    WHERE OrderDate >= DATEADD(YEAR, -2, GETDATE())
    GROUP BY Company, YEAR(OrderDate), MONTH(OrderDate)
),
Ranked AS (
    SELECT
        *,
        ROW_NUMBER() OVER (PARTITION BY Company ORDER BY SalesYear DESC, SalesMonth DESC) AS RN
    FROM MonthlySales
)
SELECT Company, SalesYear, SalesMonth, MonthlyTotal, OrderCount
FROM Ranked
WHERE RN <= 12  -- 每公司最近 12 個月
ORDER BY Company, SalesYear DESC, SalesMonth DESC;

八、常見錯誤修正速查

錯誤修正
`SELECT Col1, COUNT(*) FROM T GROUP BY Col1` 但 SELECT 有 Col2SELECT 列表所有非彙總欄位必須在 GROUP BY 中
`WHERE SUM(Amt) > 1000`彙總條件必須用 `HAVING`
`DISTINCT` 配合大型 TEXT/NTEXT 欄位不支援,改用 `GROUP BY` 或 `CAST` 為 `VARCHAR(MAX)`
`AVG(IntCol)` 回傳整數截斷`AVG(CAST(IntCol AS DECIMAL(10,4)))`
`ORDER BY` 使用別名但在舊版不支援重複運算式或用位置編號 `ORDER BY 2` (不推薦)
`COUNT(DISTINCT Col)` 在大表極慢建立索引 `CREATE INDEX IX_Col ON T(Col)` 或近似演算法 (HyperLogLog)

下一篇將講解 SQL 基礎入門:資料庫/資料表/欄位/列/欄位/結果集概念、四大基礎查詢 `SELECT/INSERT/DELETE/UPDATE`、WHERE 條件運算符、字串/日期/數值字面值語法。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved