名詞解釋
SQL: Structured Query Language,結構化查詢語言,是一種在關系型資料庫中用于管理資料的标準語言。SQL是一種聲明式程式設計語言,即隻需表明需要什麼而無需關注實作細節(C#中的LINQ也是如此)。
SQL方言:在SQL标準的基礎上延伸的其它語言,如SQL Server中所使用的T-SQL。注意SQL方言未必完全支援所有的SQL标準。
T-SQL:Transact-SQL,微軟公司提供的用于SQL Server資料庫的SQL方言(擴充)。
SQL表達式運算
謂詞
SQL中謂詞是指運算結果為True,False或Unknown的邏輯表達式。T-SQL中的謂詞有IN,BETWEEN,LIKE等。
使用LIKE可以做模糊比對,支援正規表達式:
-- 查找以x開頭的name
SELECT name FROM WJChi.dbo.UserInfo WHERE name LIKE 'x%';
-- 查找以兩個x開頭的name,_表示比對單個字元
SELECT name FROM WJChi.dbo.UserInfo WHERE name LIKE '_x%';
需要注意的是,LIKE模糊比對若以%開頭,則無法使用索引。如:
LIKE '%x'
運算符
SQL中的運算符與進階程式設計語言(C#,JAVA)類似。當多個運算符出現在同一表達式中時,SQL Server會按照運算符的優先級進行計算。當搞不清楚優先級就使用括号,對比下面兩句SQL:
SELECT orderid FROM Sales.dbo.Orderes
WHERE
custid=1 AND empid IN (1,2,3)
OR
custid=85 AND empid IN (4,5,6)
SELECT orderid FROM Sales.dbo.Orderes
WHERE
(custid=1 AND empid IN (1,2,3))
OR
(custid=85 AND empid IN (4,5,6))
AND運算符的優先級高于OR,是以上述兩句SQL在邏輯上等價。但,很明顯第二句的WHERE條件邏輯上更清晰。
三值邏輯
SQL中表達式的運算結果有三種情況:
True,False 與 Unknown
。
在查詢篩選中,隻傳回條件表達式(WHERE、HAVING、ON)運算結果為True的資料。
CHECK限制,傳回表達式運算結果不為False的結果。
兩值邏輯
與T-SQL中的大多數謂詞不同,EXISTS使用兩值邏輯(True/False),而不是三值邏輯;
在
EXISTS( SELECT * FROM T_A WHERE Id=12)
中,EXISTS謂語隻關心比對行是否存在,而不管SELECT中指定的屬性,就像整個SELECT子句是多餘的一樣。SQL Server引擎在優化查詢時會忽略SELECT子句。是以,SELECT子句中的星号(*)對于性能沒有任何負面影響。
為節省微不足道的與星号(*)解析相關的額外成本,而犧牲代碼可讀性是不值得的。
NULL & Unknown
NULL表示值是Unknown狀态,SQL中不同的語言元素對于NULL有着不同的處理方式。
在使用NULL值時應注意以下幾點:
- 将NULL與其它值進行比較,不管該值是否為NULL,結果均為Unknown
- 應使用IS NULL或IS NOT NULL來判斷值是否為NULL
- INSERT未給列指定值則插入NULL
- GROUP BY和ORDER BY子句會将多個NULL值視為相等
- 标準SQL的UNIQUE限制認為NULL是為彼此不同
- T-SQL中的UNIQUE限制認為多個NULL是相等的
- COUNT(*)的特殊性
若列名為tag的例中存在
a,NULL,c,d
幾行資料,那麼COUNT(*)傳回4而COUNT(tag)則傳回3
NULL參與的邏輯運算結果很可能是Unknown(三值邏輯也是引發應用錯誤的重要原因),除非運算結果不依賴于Unknown,示例如下。
Unknown參與AND運算結果:
Expression 1 | Expression 2 | Result |
---|---|---|
TRUE | UNKNOWN | |
FALSE |
Unknown參與OR預算結果:
查詢
SQL中的查詢是指,SELECT語句經過一些列邏輯處理而擷取資料的過程。
幾條建議:
- SQL中的關鍵字均使用大寫字母
- SQL語句均使用分号結尾
- SQL中使用對象的完全限定名,如:DbName.dbo.TableName
查詢語句執行順序
SQL中查詢語句的邏輯處理過程與實際查詢過程(實體查詢過程)是有差異的,即,SELECT語句的執行順序與書寫順序是有差異的。按照SELECT文法規定書寫的SQL語句較為符合英語文法習慣(對人類友好),但SELECT語句的實際執行則按照如下順序進行(對機器友好):
- FROM
- JOIN ON
- WHERE
- GROUP BY
- HAVING
- SELECT
- 表達式
- DISTINCT
- ORDER BY
- TOP/OFFSET FETCH
OFFSET FETCH可以看作是ORDER BY子句的一部分
SQL基于集合理論,查詢結果集(表結果)是無順尋的(雖然看起來結果集像按照某種順序排列),除非顯式的使用ORDER BY子句指定順尋,但使用ORDER BY字句後結果集将被作為遊标對待,而非表結果。
FROM子句用于指定需要查詢的資料源,WHERE語句對資料源中的資料做基于行的篩選。通常WHERE子句可以決定查詢是否使用索引,及使用哪些索引,對于查詢優化有着重要意義。
GROUP BY子句用于對查詢結果集進行分組,GROUP BY之後的所有操作都是對組而非行的操作。在查詢結果中,每組最終由一個單行來表示。這意味着,GROUP BY之後的所有子句中指定的表達式必須對每組傳回一個标量(單個值)。
HAVING用于對GROUP BY産生的組進行篩選。
SELECT語句用于指定傳回到查詢結果集中的列,生成查詢結果表。注意,在SELECT子句之前執行的子句無法使用SELECT子句中的列的别名,否則會傳回
Invalid column name
錯誤。
TOP不是标準SQL,是T-SQL專有功能,用于限制查詢傳回的指定行數或百分比:
-- 傳回Table中的10條資料
SELECT TOP(10) * FROM Table;
-- 傳回Table中10%的資料
SELECT TOP(10) PERCENT * FROM Table;
OFFSET-FETCH有着與TOP類似的功能,但它是标準SQL,可用于分頁查詢:
-- 取第51至60行的10條資料
SELECT * FROM Table
ORDER BY Id DESC
OFFSET 50 ROWS FETCH NEXT 10 ROWS ONLY;
注意SQL SERVER中,OFFSET-FETCH要與ORDER BY結合使用,否則會報錯:
Invalid usage of the option NEXT in the FETCH statement.
同時操作
SQL中有all-at-once operations(同時操作)的概念,即出現在同一邏輯處理階段的所有表達式在同一時間進行邏輯計算。
因為同時操作的原因,下面示例中orderyear+1中的oderyear是無效的,SQL會報錯:
Invalid column name 'orderyear'
:
SELECT orderid,YEAR(orderdate) AS orderyear,orderyear+1 AS nextyear FROM Sales.dbo.Orders;
同樣,由于同時操作的原因,SQL Server不支援短路操作。如,WHERE子句中的多個表達式的計算并沒有确定的順序。
CASE...WHEN...
CASE表達式是标量表達式,傳回一個符合條件的值。注意,CASE是表達式,不是語句,與COUNT類似。
CASE表達式有兩種使用方式:
- CASE後面帶有列名
這種情況下,
WHEN
子句中隻能使用标量或傳回标量的表達式,這種形式稱為簡單格式。
SELECT
Name,
CASE Age
WHEN 50 THEN '知天命'
WHEN 1+1 THEN ''
ELSE '未成年'
END
FROM WJChi.dbo.UserInfo;
- CASE後面不帶列名
WHEN
子句中隻能使用邏輯表達式,這種形式稱為搜尋格式。
SELECT
Name,
CASE
WHEN Age BETWEEN 60 AND 100 THEN '老年'
WHEN Age>=18 THEN '成年'
WHEN Name='雪飛鴻' THEN '666'
WHEN 1+1=2 THEN ''
ELSE '你猜'
END
FROM WJChi.dbo.UserInfo;
CASE表達式中若未指定ELSE的傳回值,則預設為
ELSE NULL
查詢分類
查詢可分為:
- 單表查詢
查詢中最簡單的一種形式。高并發,分布式系統中常用。通常單表查詢僅需一句SELECT語句即可,簡單且資料庫
- 聯接查詢
INNER JOIN、LEFT JOIN、RIGHT JOIN、CROSS JOIN
- 子查詢
SQL可以在一個查詢語句中編寫另外一個查詢語句,即嵌套查詢。最外面的查詢結果集傳回給調用者,稱為外部查詢。内部查詢的結果集被用于外部查詢,稱為子查詢。
- 表表達式
派生表、公用表表達式、視圖等
聚合函數
聚合函數對多行資料進行運算後傳回标量(聚合),隻有SELECT、HAVING、ORDER BY語句中可以使用聚合函數;
開窗函數
開窗函數是對基本查詢中的每一行按組(視窗)進行運算,并得到一個标量。行的視窗使用OVER子句定義。
鎖與事務隔離級别
SQL Server預設情況下,查詢語句會申請共享鎖。共享鎖可以阻止對資料進行修改,詳細資訊可參閱:
SQL Server中鎖與事務隔離級别小結
相較于增删改而言,查詢是比較複雜的,也是資料庫優化的關注重點。本文主要介紹了T-SQL查詢的基礎知識,對于較為複雜的查詢,如:關聯、表表達式、集合運算等将在後續文章中介紹。
書籍推薦
《SQL SERVER 2012 T-SQL 基礎教程》
《SQL SERVER 性能優化與管理的藝術》
《SQL SERVER基礎教程》