sql server中的cte
- 2019 年 10 月 3 日
- 筆記
從SQL Server 2005開始,提供了CTE(Common Table Expression,公用表表達式)的語法支援。
CTE是定義在SELECT、INSERT、UPDATE或DELETE語句中的臨時命名的結果集,同時CTE也可以用在視圖的定義中。
在CTE中可以包括對自身的引用,因此這種表達式也被稱為遞歸CTE。
CTE的優點
公用表表達式提供的功能其實和視圖差不多,但是它不像視圖一樣把SQL語句保存在我們的資料庫裡面。
微軟官方給出的使用CTE的優勢:
1.可以編寫一個遞歸查詢。
2.要使用一個類似視圖的功能,但是又不想把這個查詢SQL語句的定義保存到資料庫中。
3.要引用一個返回數據的SQL語句多次,只需要定義一次。
使用CTE可以把複雜的SQL語句按照邏輯分成簡單獨立的幾個公用表表達式(CTE),這樣的最大優勢就是能夠提高SQL語句的可讀性和可維護性。
總結就是,CTE主要可以用於樹結構的遞歸和簡化SQL語句,增加可讀性和可維護性。
CTE的使用場景
由於業務需要,我們經常會寫一些比較複雜的SQL語句,裡面可能會包含很多的JOIN或子查詢,要維護和理清這種N多個表的JOIN關係是一件非常頭疼的事情,而使用CTE就可以使維護和理解複雜的SQL語句變得簡單一些。
在開發的時候使用子查詢,一般是這種情況:需要從一個複雜的子查詢,甚至多級子查詢嵌套。在這種情況下,在整個SQL語句裡面,無論你是直接寫SQL語句還是把這段SQL語句包裝成子查詢然後用別名來訪問,當業務需求越來越變得複雜,你可能隨時需要修改這個長且複雜的SQL語句段,而維護這種複雜的、可讀性差的SQL語句簡直是噩夢。
有了CTE只有,我們就可以使用CTE來定義一個SQL語句,並且為這個SQL語句執行後返回的結果集定義一個別名,接下來就可以通過這個別名來引用這些預先執行返回的數據集,就像使用普通的表一樣。
CTE的語法
一個公用表表達式主要包含三個主要部分:
1.CET名稱(WITH後面,列名列之前)。
2.列名列(可選)。
3.CET查詢語句主體(AS後面括起來的內容)。
with expression_name (column_name, …) as ( -- cte_query_definition cte查詢語句定義 )
要注意的是,如果要定義多個表達式,需要用逗號分隔。
使用CTE進行多次查詢
CTE是可以在跟隨其後的查詢中多次引用的。
with tmp(id) as ( select id from users where name like '楊%'; ) select * from tmp; select * from orders where userId in (select id from tmp);
使用CTE遞歸查詢樹形記錄(向上查詢父節點或向下查詢子節點)
CTE有一個特性就是它是支援遞歸的,即在CTE的查詢語句主體中引用自身。這一特性常常被用在查詢樹形記錄。
with subqry(id, pid, name) as ( select cb.id, cb.pid, cb.namefrom cb where cb.id = '001001001' union all select cb.id, cb.pid, cb.namefrom cb, subqry where cb.id = subqry.pid ) select distinct * from subqry
with subqry(id, pid, name) as ( select cb.id, cb.pid, cb.name from cb where cb.id = '001' union all select cb.id, cb.pid, cb.name from cb, subqry where cb.pid = subqry.id ) select distinct * from subqry
使用CTE的注意事項
1.CTE後面必須緊跟著使用CTE的SQL語句,比如SELECT、INSERT和UPDATE等,否則CTE將失效(直接報錯)。
with tmp(id) as ( select id from users where favor = '牛奶'; ) select 1; select * from tmp;
像上面的語句就會報【定義了公用表表達式,但沒有使用】的錯。
2.CTE後面也可以跟其他的CTE,但是只能使用一個WITH,多個CTE中間用逗號【,】隔開。
with milk(id) as ( select id from users where favor = '牛奶'; ), apple(id) as ( select id from users where favor = '蘋果'; ) select id from milk, apple where milk.id = apple.id
3.如果CTE表達式名稱與某個實體表或者視圖重名,則緊跟在該CTE後面的SQL語句使用的仍然是CTE。
要注意的是,後面再使用該同名就是使用實體表或視圖了。
4.CTE可以引用自身,也可以引用在同一個WITH子句中預先定義的CTE,但是不允許前向引用(定義前使用)。
5.不能在CTE_QUERY_DEFINITION(CTE查詢語句定義)中使用以下子句:
(1)COMPUTE或COMPUTE BY
(2)ORDER BY(除非指定了TOP 子句)
(3)INTO
(4)帶有查詢提示的OPTION 子句
(5)FOR XML
(6)FOR BROWSE
6.如果將CTE用於批處理的一部分的語句中,那麼在它之前的語句必須以分號結尾。
declare @id int set @id = 0; -- 這裡必須以分號結尾 ;with tmp(name) as ( -- 在with前加上分號避免出錯 select name from users where unrequitedLove = '靜靜'; ) select * from tmp;
當然了,一個良好的習慣(技巧)是在寫CTE的時候統統在WITH前加上分號【;】,這樣就能避免出錯,比如上面的語句。
總結
如果經常寫查詢的話,比如一些統計分析或製作報表,CTE是會經常使用到的,因為使用起來十分方便,也幾乎不會有什麼副作用,在一定程度上能夠提高開發和維護的效率。另外,其遞歸屬性在樹形記錄的查詢中的應用十分廣泛,是一個要好好掌握的語法。
“我還是很喜歡你,像樵人薄暮,倦鳥歸棲。”