表結構與數據:https://github.com/XuePeng87/TSQLV4git
SELECT語句的用途的查詢表,經過一些邏輯操做來返回一個結果。例如:github
SELECT empid, YEAR(orderdate) AS orderyear, COUNT(*) AS numorders FROM Sales.Orders WHERE custid = 71 GROUP BY empid, YEAR(orderdate) HAVING COUNT(*) > 1 ORDER BY empid, orderyear;
此查詢的意義是:sql
篩選客戶71名下的訂單,按僱員和訂單年度進行分組,而且僅帥選大於1個訂單的「僱員和訂單」組。對於保留的分組,此查詢提供了僱員ID、訂單年度和訂單數量,並按照僱員ID和訂單年度進行排序。網絡
其SQL語句的邏輯順序爲:架構
爲了更易於閱讀,下面的各子句的功能:函數
FROM子句是羅計劃處理的第一個查詢子句,此子句制定要查詢的表名。性能
FROM Sales.Orders;
要在代碼中始終使用架構限定式的對象名稱。若是不顯示指定架構名稱,SQL Server必須基於其隱式名稱解析規則來肯定所屬架構,這會形成一些沒必要要的額外支出,而且會致使SQL Server選擇不一樣的對象,而不是所指望的對象。spa
在WHERE子句中,能夠定義一個謂詞或邏輯表達式來篩選由FROM階段返回的行。只有邏輯表達式計算結果爲TRUE的行,纔會返回到後面的邏輯查詢處理階段。code
SELECT * FROM Sales.Orders WHERE custid = 71;
表Sales.Orders中有830行數據,通過WHERE階段篩選客戶ID等於71後,只有31行。對象
在談到查詢性能時,WHERE子句具備重要的意義。基於篩選表達式,SQL Server將評估訪問請求數據要使用的索引。經過使用索引,相比全表掃描,SQL Server有時能夠用更少的工做得到所需的數據。相比返回全部可能行給調用者並在客戶端進行篩選的方式(返回表的全部數據,由程序代碼進行過濾),篩選也能夠減小網絡通訊量。
WHERE階段僅返回邏輯表達式計算結果爲TRUE的行。T-SQL使用三值謂詞邏輯,計算結果能夠爲:TRUE、FALSE或UNKNOWN。也就是說,WHERE階段不會返回計算結果爲FALSE或UNKONOWN的行。
GROUP BY階段容許用戶把前面邏輯查詢處理階段返回的行排列到組中。組是根據你在GROUP BY子句中制定的元素而肯定的。例如:
SELECT empid, YEAR(orderdate) AS orderyear, COUNT(*) AS numorders FROM Sales.Orders WHERE custid = 71 GROUP BY empid, YEAR(orderdate);
這意味着,GROUP BY階段對於WHERE階段返回的數據,會爲僱員ID與訂單年度值的每一個惟一組合生成一個組。表達式YEAR(orderdate)調用YEAR函數僅返回orderdate列的「年」部分。
WHERE階段返回了31行,其中有16個僱員ID與年度訂單值的惟一組合,這樣GROUP BY階段建立16個組,並將WHERE階段返回的31行,每行都關聯到相關組。
若是查詢使用了分組,那麼後續階段的HAVING、SELECT和ORDER BY都是對組進行操做,而不是對但各行進行操做了。
對於每一個GOURP BY的元素,在每一個組中智能有一個惟一匹配項。例如,在僱員ID8和訂單2007年的組中,只有一個惟一的僱員ID值和一個惟一的訂單年度值。所以,你能夠在GROUP BY以後的子句中(如SELECT)引用表達式empid和YEAR(orderdate),如上面例子中的寫法。
不參與到GROUP BY列表中的元素僅容許做爲一個組合函數的輸入,如COUNT、SUM、AVG、MIN或MAX。例如:
SELECT empid, YEAR(orderdate) AS orderyear, SUM(freight) AS totalfreight, COUNT(*) AS numorders FROM Sales.Orders WHERE custid = 71 GROUP BY empid, YEAR(orderdate);
表達式SUM(greight)返回每組中全部運費的和,函數COUNT(*)返回每組中的訂單數量。在GROUP BY子句以後處理的任何子句中,若是試圖引用一個未參與到GROUP BY列表中的屬性,而且該屬性不是做爲聚合函數的輸入,都會引起錯誤。
注意,除了COUNT(*)以外,全部聚合函數忽略NULL標記。例如,一個具備5行的組,數值爲「30、十、NULL、十、10」在qty列中。表達式COUNT(*)將返回5,而COUNT(qty)將返回4。若是想僅處理已知值中的非重複值,那麼能夠在聚合函數括號內製定DISTINCT關鍵字。例如COUNT(DISTINCT qty)將返回2。DISTINCT也能夠和其餘的聚合函數一塊兒使用,如SUM、AVG等等。
在HAVING子句中,能夠指定一個謂詞來篩選組,而不是篩選單個行,行是由WHERE篩選的。只有HAVING子句中邏輯表達式計算結果爲TRUE的組,會返回到下一個邏輯查詢處理階段,FALSE或UNKONWN的組會被過濾掉。
因爲HAVING子句是在行分組後被處理,因此能夠在邏輯表達式中引用聚合函數。
SELECT empid, YEAR(orderdate) AS orderyear, SUM(freight) AS totalfreight, COUNT(*) AS numorders FROM Sales.Orders WHERE custid = 71 GROUP BY empid, YEAR(orderdate) HAVING COUNT(*) > 1;
例子中的COUNT(*) > 1即是在HAVING子句中使用了聚合函數。執行這段SQL語句後,GOURP BY階段剩餘的16個分組會被過濾到9個分組。
SELECT子句是用戶指定要返回到查詢結果表中的列的地方。用戶能夠基於鎖查詢的表的列,在SELECT列表中創建表達式,屬性能夠有進一步的操做(可使用函數或定義別名),也能夠沒有。調用某些函數後須要定義別名,若是沒有定義別名,那麼在查詢結果中目標屬性將沒有名稱(沒有列名),可是在關係模型中不容許。強烈建議調用某些函數後加入別名,如例子中的YEAR(orderdate) AS orderyear同樣。
注意,SELECT是在FROM、WHERE、GROUP BY和HAVING以後處理的。這意味着分配的別名不能在以前的子句中使用。例子中的orderyear是YEAR(orderdate)的別名,若是在WHERE中使用orderyear > 2016會報錯。須要使用YEAR(orderdate) > 2016,無需擔憂YEAR(orderdate)會執行兩次,對於查詢中重複使用相同的表達式,此表達式繼續被計算一次。
關於SELECT *的寫法,大多數狀況下,不建議使用,只有在極少的狀況下例外。在使用星號時,爲了解決列的名稱可能須要一些額外的工做,雖然這是微不足道的額外開銷。
SELECT中不容許引用在一SELECT子句中建立的列別名,無論指定別名的表達式出現試圖引用它的表達式的左邊仍是右邊。
SELECT empid, YEAR(orderdate) AS orderyear, SUM(freight) AS totalfreight, COUNT(*) AS numorders FROM Sales.Orders WHERE custid = 71 GROUP BY empid, YEAR(orderdate) HAVING COUNT(*) > 1 ORDER BY empid, orderyear;
因爲ORDER BY子句是在SELECT以後,因此可使用別名。當但願按照升序(從小到大)排列時,能夠在表達式後面加入ASC關鍵字,若是什麼都不加,默認也是ASE排序。若是須要倒序(從大到小)排列時,能夠在表達式後面加入DESC關鍵字。
容許在ORDER BY中使用未出如今SELECT子句中的列,例如:
SELECT empid, firstname, lastname, country FROM HR.Employees ORDER BY hiredate;
TOP選項是一個專有的T-SQL功能,用戶限定查詢返回的行數或行的百分比。他依賴於兩個元素做爲規範的一部分,一個是要返回行的數目或百分比,另外一個是排序。例如,要從Orders表返回最近的5個訂單:
SELECT TOP(5) orderid, orderdate FROM Sales.Orders ORDER BY orderdate DESC;
ORDER BY子句是在SELECT所包含的DISTINCT選項以後計算的。這一樣適用於TOP,它要依靠ORDER BY爲其提供相關的篩選內容,也就是說,TOP篩選是在刪除重複行後計算的。
能夠爲TOP選項指定PERCENT關鍵字,這種狀況下,會基於限定行數的百分比計算要返回的行數,向上舍入。例如:查詢最近1%的訂單:
SELECT TOP(1) PERCENT orderid, orderdate FROM Sales.Orders ORDER BY orderdate DESC;
查詢結果有9行。Orders表中有830行,百分之一爲8.3,向上舍入後爲9。
若是SELECT中沒有定義主鍵或惟一約束,ORDER BY列表不是惟一的,在這種沒有加入決定屬性的狀況下,會出現重複的行。在這種狀況下SQL Server肯定行的順序是基於哪一個行首先被物理訪問到。若是但願查詢結果是肯定的,則須要確保ORDER BY列出的數據是惟一的。
出了在ORDER BY列表中添加決勝屬性,也能夠在SELECT列表中添加決勝屬性,能夠添加WITH TIES選項實現此目的,例如:
SELECT TOP(5) WITH TIES orderid, orderdate, custid, empid FROM Sales.Orders ORDER BY orderdate DESC;
結果會返回8行。SQL Server會首先返回基於orderdate DESC排序的TOP(5)行,而後是返回與檢索到的5行中最後一行orderdate值相同的其餘全部行。
TOP選項是一個很是實用的篩選類型,可是他有兩個缺陷,不是標準SQL,切不支持跳過功能。標準SQL定義的TOP相似的篩選稱爲OFFSET-FETCH,支持跳過功能,這對針對制定頁面的查詢很是有用。SQL Server2012引入了對OFFSET-FETCH篩選的支持。
OFFSET-FETCH篩選被視爲ORDER BY子句的一部分,一般用於實現按順序顯示效果。OFFSET子句指定要跳過的行數,FETCH子句指定在跳過的行數後要篩選的行數,例如:
SELECT orderid, orderdate, custid, empid FROM Sales.Orders ORDER BY orderdate, orderid OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY;
次查詢按照orderdate、orderid排序,並添加了決勝屬性排序Orders表中的行。基於此順序,跳過結果的前50行,篩選下面的25行。
使用OFFSET-FETCH的查詢必須具備ORDER BY子句,此外,FETCH子句不支持沒有OFFSET。若是不想跳過任何行,但但願使用FETCH,則應當使用OFFSET 0 ROWS來表示。不過,沒有FETCH的OFFSET是容許的,這種狀況是跳過指定的行數,並返回查詢結果中全部的剩餘行。
開窗函數的功能是:對於基本查詢中的每一行,按行的窗口(組)進行運算,並計算一個標量(單個)結果值。行的窗口使用OVER子句定義。例如:
SELECT orderid, custid, val, ROW_NUMBER() OVER(PARTITION BY custid ORDER BY val) AS rownum FROM Sales.OrderValues ORDER BY custid, val;
ROW_NUMBER函數對於查詢結果各個分區內的行,按照指定的排序,分配了惟1、連續、遞增的整數。示例中的OVER子句按照custid屬性劃分窗口,所以每一個客戶的行號是惟一的。同時,OVER定義了在窗口中按val屬性排序,這樣連續行號會在分區內按照val遞增。
注意,ROW_NUMBER函數必須在每一個分區內生成惟一值。這意味着即便排序值不增長,行號仍舊會遞增。所以,若是ROW_NUMBER函數的ORDER BY列表不是惟一的,就有可能存在多個正確結果(沒有決勝屬性)。