名詞解釋
SQL: Structured Query Language,結(jié)構(gòu)化查詢語(yǔ)言,是一種在關(guān)系型數(shù)據(jù)庫(kù)中用于管理數(shù)據(jù)的標(biāo)準(zhǔn)語(yǔ)言。SQL是一種聲明式編程語(yǔ)言,即只需表明需要什么而無(wú)需關(guān)注實(shí)現(xiàn)細(xì)節(jié)(C#中的LINQ也是如此)。
SQL方言:在SQL標(biāo)準(zhǔn)的基礎(chǔ)上延伸的其它語(yǔ)言,如SQL Server中所使用的T-SQL。注意SQL方言未必完全支持所有的SQL標(biāo)準(zhǔn)。
T-SQL:Transact-SQL,微軟公司提供的用于SQL Server數(shù)據(jù)庫(kù)的SQL方言(擴(kuò)展)。
SQL表達(dá)式運(yùn)算
謂詞
SQL中謂詞是指運(yùn)算結(jié)果為T(mén)rue,False或Unknown的邏輯表達(dá)式。T-SQL中的謂詞有IN,BETWEEN,LIKE等。
使用LIKE可以做模糊匹配,支持正則表達(dá)式:
-- 查找以x開(kāi)頭的name
SELECT name FROM WJChi.dbo.UserInfo WHERE name LIKE 'x%';
-- 查找以兩個(gè)x開(kāi)頭的name,_表示匹配單個(gè)字符
SELECT name FROM WJChi.dbo.UserInfo WHERE name LIKE '_x%';
需要注意的是,LIKE模糊匹配若以%開(kāi)頭,則無(wú)法使用索引。如:LIKE '%x'
運(yùn)算符
SQL中的運(yùn)算符與高級(jí)編程語(yǔ)言(C#,JAVA)類似。當(dāng)多個(gè)運(yùn)算符出現(xiàn)在同一表達(dá)式中時(shí),SQL Server會(huì)按照運(yùn)算符的優(yōu)先級(jí)進(jìn)行計(jì)算。當(dāng)搞不清楚優(yōu)先級(jí)就使用括號(hào),對(duì)比下面兩句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運(yùn)算符的優(yōu)先級(jí)高于OR,所以上述兩句SQL在邏輯上等價(jià)。但,很明顯第二句的WHERE條件邏輯上更清晰。
三值邏輯
SQL中表達(dá)式的運(yùn)算結(jié)果有三種情況:True,False 與 Unknown。
在查詢篩選中,只返回條件表達(dá)式(WHERE、HAVING、ON)運(yùn)算結(jié)果為T(mén)rue的數(shù)據(jù)。
CHECK約束,返回表達(dá)式運(yùn)算結(jié)果不為False的結(jié)果。
兩值邏輯
與T-SQL中的大多數(shù)謂詞不同,EXISTS使用兩值邏輯(True/False),而不是三值邏輯;
在EXISTS( SELECT * FROM T_A WHERE Id=12)中,EXISTS謂語(yǔ)只關(guān)心匹配行是否存在,而不管SELECT中指定的屬性,就像整個(gè)SELECT子句是多余的一樣。SQL Server引擎在優(yōu)化查詢時(shí)會(huì)忽略SELECT子句。所以,SELECT子句中的星號(hào)(*)對(duì)于性能沒(méi)有任何負(fù)面影響。
為節(jié)省微不足道的與星號(hào)(*)解析相關(guān)的額外成本,而犧牲代碼可讀性是不值得的。
NULL & Unknown
NULL表示值是Unknown狀態(tài),SQL中不同的語(yǔ)言元素對(duì)于NULL有著不同的處理方式。
在使用NULL值時(shí)應(yīng)注意以下幾點(diǎn):
將NULL與其它值進(jìn)行比較,不管該值是否為NULL,結(jié)果均為Unknown
應(yīng)使用IS NULL或IS NOT NULL來(lái)判斷值是否為NULL
INSERT未給列指定值則插入NULL
GROUP BY和ORDER BY子句會(huì)將多個(gè)NULL值視為相等
標(biāo)準(zhǔn)SQL的UNIQUE約束認(rèn)為NULL是為彼此不同
T-SQL中的UNIQUE約束認(rèn)為多個(gè)NULL是相等的
-
COUNT(*)的特殊性
若列名為tag的例中存在
a,NULL,c,d幾行數(shù)據(jù),那么COUNT(*)返回4而COUNT(tag)則返回3
NULL參與的邏輯運(yùn)算結(jié)果很可能是Unknown(三值邏輯也是引發(fā)應(yīng)用錯(cuò)誤的重要原因),除非運(yùn)算結(jié)果不依賴于Unknown,示例如下。
Unknown參與AND運(yùn)算結(jié)果:
| Expression 1 | Expression 2 | Result |
|---|---|---|
| TRUE | UNKNOWN | UNKNOWN |
| UNKNOWN | UNKNOWN | UNKNOWN |
| FALSE | UNKNOWN | FALSE |
Unknown參與OR預(yù)算結(jié)果:
| Expression 1 | Expression 2 | Result |
|---|---|---|
| TRUE | UNKNOWN | TRUE |
| UNKNOWN | UNKNOWN | UNKNOWN |
| FALSE | UNKNOWN | UNKNOWN |
查詢
SQL中的查詢是指,SELECT語(yǔ)句經(jīng)過(guò)一些列邏輯處理而獲取數(shù)據(jù)的過(guò)程。
幾條建議:
SQL中的關(guān)鍵字均使用大寫(xiě)字母
SQL語(yǔ)句均使用分號(hào)結(jié)尾
SQL中使用對(duì)象的完全限定名,如:DbName.dbo.TableName
查詢語(yǔ)句執(zhí)行順序
SQL中查詢語(yǔ)句的邏輯處理過(guò)程與實(shí)際查詢過(guò)程(物理查詢過(guò)程)是有差異的,即,SELECT語(yǔ)句的執(zhí)行順序與書(shū)寫(xiě)順序是有差異的。按照SELECT語(yǔ)法規(guī)定書(shū)寫(xiě)的SQL語(yǔ)句較為符合英語(yǔ)語(yǔ)法習(xí)慣(對(duì)人類友好),但SELECT語(yǔ)句的實(shí)際執(zhí)行則按照如下順序進(jìn)行(對(duì)機(jī)器友好):
- FROM
- JOIN ON
- WHERE
- GROUP BY
- HAVING
- SELECT
- 表達(dá)式
- DISTINCT
- ORDER BY
- TOP/OFFSET FETCH
OFFSET FETCH可以看作是ORDER BY子句的一部分
SQL基于集合理論,查詢結(jié)果集(表結(jié)果)是無(wú)順尋的(雖然看起來(lái)結(jié)果集像按照某種順序排列),除非顯式的使用ORDER BY子句指定順尋,但使用ORDER BY字句后結(jié)果集將被作為游標(biāo)對(duì)待,而非表結(jié)果。
FROM子句用于指定需要查詢的數(shù)據(jù)源,WHERE語(yǔ)句對(duì)數(shù)據(jù)源中的數(shù)據(jù)做基于行的篩選。通常WHERE子句可以決定查詢是否使用索引,及使用哪些索引,對(duì)于查詢優(yōu)化有著重要意義。
GROUP BY子句用于對(duì)查詢結(jié)果集進(jìn)行分組,GROUP BY之后的所有操作都是對(duì)組而非行的操作。在查詢結(jié)果中,每組最終由一個(gè)單行來(lái)表示。這意味著,GROUP BY之后的所有子句中指定的表達(dá)式必須對(duì)每組返回一個(gè)標(biāo)量(單個(gè)值)。
HAVING用于對(duì)GROUP BY產(chǎn)生的組進(jìn)行篩選。
SELECT語(yǔ)句用于指定返回到查詢結(jié)果集中的列,生成查詢結(jié)果表。注意,在SELECT子句之前執(zhí)行的子句無(wú)法使用SELECT子句中的列的別名,否則會(huì)返回Invalid column name錯(cuò)誤。
TOP不是標(biāo)準(zhǔn)SQL,是T-SQL專有功能,用于限制查詢返回的指定行數(shù)或百分比:
-- 返回Table中的10條數(shù)據(jù)
SELECT TOP(10) * FROM Table;
-- 返回Table中10%的數(shù)據(jù)
SELECT TOP(10) PERCENT * FROM Table;
OFFSET-FETCH有著與TOP類似的功能,但它是標(biāo)準(zhǔn)SQL,可用于分頁(yè)查詢:
-- 取第51至60行的10條數(shù)據(jù)
SELECT * FROM Table
ORDER BY Id DESC
OFFSET 50 ROWS FETCH NEXT 10 ROWS ONLY;
注意SQL SERVER中,OFFSET-FETCH要與ORDER BY結(jié)合使用,否則會(huì)報(bào)錯(cuò):
Invalid usage of the option NEXT in the FETCH statement.
同時(shí)操作
SQL中有all-at-once operations(同時(shí)操作)的概念,即出現(xiàn)在同一邏輯處理階段的所有表達(dá)式在同一時(shí)間進(jìn)行邏輯計(jì)算。
因?yàn)橥瑫r(shí)操作的原因,下面示例中orderyear+1中的oderyear是無(wú)效的,SQL會(huì)報(bào)錯(cuò):Invalid column name 'orderyear':
SELECT orderid,YEAR(orderdate) AS orderyear,orderyear+1 AS nextyear FROM Sales.dbo.Orders;
同樣,由于同時(shí)操作的原因,SQL Server不支持短路操作。如,WHERE子句中的多個(gè)表達(dá)式的計(jì)算并沒(méi)有確定的順序。
CASE...WHEN...
CASE表達(dá)式是標(biāo)量表達(dá)式,返回一個(gè)符合條件的值。注意,CASE是表達(dá)式,不是語(yǔ)句,與COUNT類似。
CASE表達(dá)式有兩種使用方式:
-
CASE后面帶有列名
這種情況下,
WHEN子句中只能使用標(biāo)量或返回標(biāo)量的表達(dá)式,這種形式稱為簡(jiǎn)單格式。
SELECT
Name,
CASE Age
WHEN 50 THEN '知天命'
WHEN 1+1 THEN ''
ELSE '未成年'
END
FROM WJChi.dbo.UserInfo;
-
CASE后面不帶列名
這種情況下,
WHEN子句中只能使用邏輯表達(dá)式,這種形式稱為搜索格式。
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表達(dá)式中若未指定ELSE的返回值,則默認(rèn)為ELSE NULL。
查詢分類
查詢可分為:
-
單表查詢
查詢中最簡(jiǎn)單的一種形式。高并發(fā),分布式系統(tǒng)中常用。通常單表查詢僅需一句SELECT語(yǔ)句即可,簡(jiǎn)單且數(shù)據(jù)庫(kù)
-
聯(lián)接查詢
INNER JOIN、LEFT JOIN、RIGHT JOIN、CROSS JOIN
-
子查詢
SQL可以在一個(gè)查詢語(yǔ)句中編寫(xiě)另外一個(gè)查詢語(yǔ)句,即嵌套查詢。最外面的查詢結(jié)果集返回給調(diào)用者,稱為外部查詢。內(nèi)部查詢的結(jié)果集被用于外部查詢,稱為子查詢。
-
表表達(dá)式
派生表、公用表表達(dá)式、視圖等
聚合函數(shù)
聚合函數(shù)對(duì)多行數(shù)據(jù)進(jìn)行運(yùn)算后返回標(biāo)量(聚合),只有SELECT、HAVING、ORDER BY語(yǔ)句中可以使用聚合函數(shù);
開(kāi)窗函數(shù)
開(kāi)窗函數(shù)是對(duì)基本查詢中的每一行按組(窗口)進(jìn)行運(yùn)算,并得到一個(gè)標(biāo)量。行的窗口使用OVER子句定義。
鎖與事務(wù)隔離級(jí)別
SQL Server默認(rèn)情況下,查詢語(yǔ)句會(huì)申請(qǐng)共享鎖。共享鎖可以阻止對(duì)數(shù)據(jù)進(jìn)行修改,詳細(xì)信息可參閱:SQL Server中鎖與事務(wù)隔離級(jí)別
小結(jié)
相較于增刪改而言,查詢是比較復(fù)雜的,也是數(shù)據(jù)庫(kù)優(yōu)化的關(guān)注重點(diǎn)。本文主要介紹了T-SQL查詢的基礎(chǔ)知識(shí),對(duì)于較為復(fù)雜的查詢,如:關(guān)聯(lián)、表表達(dá)式、集合運(yùn)算等將在后續(xù)文章中介紹。
書(shū)籍推薦
《SQL SERVER 2012 T-SQL 基礎(chǔ)教程》
《SQL SERVER 性能優(yōu)化與管理的藝術(shù)》
《SQL SERVER基礎(chǔ)教程》