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

SQL Server 簡單查詢語句邏輯處理順序與 SELECT 1 vs SELECT * 效能迷思破解

     編輯:關於SqlServer

前言

SQL 查詢的書寫順序(SELECT-FROM-WHERE...)與邏輯處理順序(FROM-WHERE-GROUP BY-HAVING-SELECT-ORDER BY)並不相同。本文釐清邏輯處理流程,並透過多維度實測破解「SELECT 1 比 SELECT * 快」的流傳迷思。

查詢子句邏輯處理順序

-- 書寫順序
SELECT empid, YEAR(orderdate) AS orderyear, COUNT(*) AS numbers
FROM Sales.Orders
WHERE custid = '71'
GROUP BY empid, YEAR(orderdate)
HAVING COUNT(*) > 1
ORDER BY empid, orderyear

-- 邏輯處理順序(優化器實際執行流程)
FROM Sales.Orders
WHERE custid = 71
GROUP BY empid, YEAR(orderdate)
HAVING COUNT(*) > 1
SELECT empid, YEAR(orderdate) AS orderyear, COUNT(*) AS numberorders
ORDER BY empid, orderyear

理解邏輯順序有助於:

  • 理解為何 WHERE 不能用別名、HAVING 可用彙總函數。
  • 優化查詢:盡早過濾(WHERE)、減少 JOIN 前資料量。
  • 正確使用窗口函數(邏輯在 SELECT 之後)。

SELECT 1 vs SELECT * 效能實測六大場景

測試環境:TSQL2012 範例資料庫,啟用「包含實際執行計劃」(Ctrl+M)。

場景 1:整表 EXISTS 子查詢

IF EXISTS(SELECT 1 FROM Sales.Orders) SELECT 'SELECT 1'
IF EXISTS(SELECT * FROM Sales.Orders) SELECT 'SELECT *'

✅ 執行計劃完全一致,優化器識別出僅需存在性檢查,忽略投影列表。

場景 2:索引列等值查詢

CREATE INDEX ix_shipname ON Sales.Orders(shipname)
IF EXISTS(SELECT 1 FROM Sales.Orders WHERE shipname = 'Ship to 85-B') SELECT 'SELECT 1'
IF EXISTS(SELECT * FROM Sales.Orders WHERE shipname = 'Ship to 85-B') SELECT 'SELECT *'

✅ 計劃一致,索引查找 + 存在性檢查,無差異。

場景 3:彙總函數 COUNT

IF (SELECT 1 FROM Sales.Orders WHERE shipname = 'Ship to 85-B') = 1 SELECT 'SELECT 1'
IF (SELECT COUNT(*) FROM Sales.Orders WHERE shipname = 'Ship to 85-B') = 1 SELECT 'SELECT *'

✅ 計劃一致,COUNT(*) 與常數 1 比較同樣觸發索引查找。

場景 4:非索引列 COUNT

IF (SELECT COUNT(1) FROM Sales.Orders WHERE freight = '41.3400') = 1 SELECT 'SELECT 1'
IF (SELECT COUNT(*) FROM Sales.Orders WHERE freight = '41.3400') = 1 SELECT 'SELECT *'

✅ 計劃一致,全表掃描 + 計數,COUNT(1) 與 COUNT(*) 無差異。

場景 5:相關子查詢 EXISTS

SELECT custid, companyname FROM Sales.Customers AS C
WHERE country = N'USA' AND EXISTS (SELECT * FROM Sales.Orders AS O WHERE O.custid = C.custid)

SELECT custid, companyname FROM Sales.Customers AS C
WHERE country = N'USA' AND EXISTS (SELECT 1 FROM Sales.Orders AS O WHERE O.custid = C.custid)

✅ 計劃一致,半連接最佳化,投影列表被忽略。

場景 6:視圖查詢(唯一出現差異處)

CREATE VIEW SaleOdersView AS
SELECT shipaddress, shipname,
       (SELECT unitprice FROM Sales.OrderDetails AS sod WHERE sod.orderid = so.orderid) as tc3
FROM Sales.Orders AS so

SELECT 1 FROM dbo.SaleOdersView
SELECT * FROM dbo.SaleOdersView

⚠️ 視圖中 SELECT * 成本 97% vs SELECT 1 成本 3%!原因:視圖定義包含標量子查詢,SELECT * 強制評估所有列含昂貴子查詢,SELECT 1 經優化器裁剪僅驗證存在性。

專家觀點與最佳實踐

「在 EXISTS 子查詢中,SELECT * 與 SELECT 1 效能完全等價,優化器足夠智能忽略投影列表。SELECT * 唯一可接受的生產代碼場景就是 EXISTS 子句。」
—— SQL Server 社群共識

建議準則

  1. EXISTS 子查詢: 習慣用 SELECT 1(語意清晰)或 SELECT *(無效能差異)。
  2. 一般查詢: 嚴禁 SELECT *,明確列出欄位 —— 減少網路傳輸、利用覆蓋索引、避免 Schema 變更破壞應用。
  3. 視圖/衍生表: 若定義含昂貴運算,外層查詢務必投影所需欄位,勿用 SELECT *。
  4. COUNT(*) vs COUNT(1): 無效能差異,習慣用 COUNT(*) 符合 ANSI 標準。

總結

邏輯處理順序決定查詢語意;優化器對 EXISTS 子查詢投影列表有完善裁剪機制。SELECT 1 vs SELECT * 效能差異僅存在於視圖/衍生表含昂貴運算列時。日常開發應專注於「明確欄位列表」與「覆蓋索引設計」,而非糾結 1 與 *。

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