SQL Server 2005 Hierarchies WITH Common Table Expressions
代码
1 create table Employee
2 (
3 Id INT IDENTITY(1,1) PRIMARY KEY,
4 [Name] varchar(30) null,
5 JobTitle varchar(30) null,
6 Manager int null
7 )
8
9 insert Employee
10 select 'incf', 'IT Director',null union all
11 select 'inc3', 'Finance Director',null union all
12 select 'geovindu', 'assces',1 union all
13 select 'du', 'assces',1 union all
14 select 'fa','account',2 union all
15 select 'd','account',2
16
17 WITH OrganisationChart (Id, [Name], JobTitle, Manager) AS
18 (
19 SELECT
20 Id, [Name], JobTitle, Manager
21 FROM dbo.Employee
22 WHERE
23 Manager IS NULL
24 UNION ALL
25 SELECT emp.Id, emp.[Name], emp.JobTitle, emp.Manager
26 FROM dbo.Employee emp
27 INNER JOIN OrganisationChart ON
28 emp.Manager = OrganisationChart.Id
29 )
30 SELECT * FROM OrganisationChart
31
32
33 WITH OrganisationChart (Id, [Name], JobTitle, [Level], Manager) AS
34 (
35 SELECT
36 Id, [Name], JobTitle, 0, Manager
37 FROM dbo.Employee
38 WHERE
39 Manager IS NULL
40 UNION ALL
41 SELECT emp.Id, emp.[Name], emp.JobTitle, [Level] + 1, emp.Manager
42 FROM dbo.Employee emp
43 INNER JOIN OrganisationChart ON
44 emp.Manager = OrganisationChart.Id
45 )
46 SELECT * FROM OrganisationChart
47 ORDER BY [Level]
48
49
50 WITH OrganisationChart (Id, [Name], JobTitle, [Level], Manager, [Root]) AS
51 (
52 SELECT Id, [Name], JobTitle, 0, Manager, Id
53 FROM dbo.Employee
54 WHERE Manager IS NULL
55 UNION ALL
56 SELECT emp.Id,emp.[Name],emp.JobTitle,[Level] + 1,emp.Manager,[Root]
57 FROM dbo.Employee emp
58 INNER JOIN OrganisationChart ON
59 emp.Manager = OrganisationChart.Id
60 )
61 SELECT * FROM OrganisationChart
62 --WHERE [Name] = 'incf'
63 WHERE [Root] = 1
64
65
66
67 --示例数据库
68
69 /*
70 递归查询对于同一个表父子关系的计算提供了很大的方便,这个示例使用了SQL server 2005中的递归查询,使用的表是CarParts,这个表存储了一辆汽车的所有零件以及结构,part为零件单位,subpart为子零件,Qty为数量。
71
72 具体示例如下:
73
74 */
75 CREATE TABLE CarParts
76
77 (
78
79 CarID INT NOT NULL,
80
81 Part VARCHAR(15),
82
83 SubPart VARCHAR(15),
84
85 Qty INT
86
87 )
88
89 GO
90
91 INSERT CarParts VALUES (1, 'Body', 'Door', 4)
92
93 INSERT CarParts VALUES (1, 'Body', 'Trunk Lid', 1)
94
95 INSERT CarParts VALUES (1, 'Body', 'Car Hood', 1)
96
97 INSERT CarParts VALUES (1, 'Door', 'Handle', 1)
98
99 INSERT CarParts VALUES (1, 'Door', 'Lock', 1)
100
101 INSERT CarParts VALUES (1, 'Door', 'Window', 1)
102
103 INSERT CarParts VALUES (1, 'Body', 'Rivets', 1000)
104
105 INSERT CarParts VALUES (1, 'Door', 'Rivets', 100)
106
107 INSERT CarParts VALUES (1, 'Door', 'Mirror', 1)
108
109 INSERT CarParts VALUES (1, 'Mirror', 'small_Mirror', 4)
110
111 GO
112
113 SELECT * FROM CarParts
114
115 GO
116
117 /*
118
119 一辆汽车需要各个零件的数目
120
121 1个Body 需要4个Door
122
123 1个Door 需要1个Mirror
124
125 那么
126
127 1个body需要4个Mirror
128
129 结构很简单吧
130
131 */
132
133 WITH CarPartsCTE(SubPart, Qty)
134
135 AS
136
137 (
138
139 -- 固定成员 (AM):
140
141 -- SELECT查询无需参考CarPartsCTE
142
143 -- 递归从此处开始
144
145 SELECT SubPart, Qty
146
147 FROM CarParts
148
149 WHERE Part = 'Body'
150
151 UNION ALL
152
153 -- 递归成员 (RM):
154
155 -- SELECT查询参考CarPartsCTE
156
157 -- 使用现有数据往下一层展开
158
159 SELECT CarParts.SubPart, CarPartsCTE.Qty * CarParts.Qty
160
161 FROM CarPartsCTE
162
163 INNER JOIN CarParts ON CarPartsCTE.SubPart = CarParts.Part
164
165 WHERE CarParts.CarID = 1
166
167 )
168
169 SELECT SubPart,Qty AS TotalNUM
170
171 FROM CarPartsCTE
172
173 /*
174
175 注意看最下层的small_Mirror 位于 表最后的位置,
176
177 由此可以看出改递归不是开始就进行递归查询而是在1层完全展开后在根据该层展开下一层不是深度优先的递归
178
179 */
180
181 drop table CarParts
182
183
184
185 WITH DirectReports(groupid, member, EmployeeLevel,type) AS
186
187 (
188
189 SELECT groupid, member, 0,type AS EmployeeLevel
190
191 FROM groupinfo
192
193 WHERE groupid = 'finance_company'
194
195 UNION ALL
196
197 SELECT e.groupid, e.member, EmployeeLevel + 1,e.type
198
199 FROM groupinfo e
200
201 INNER JOIN DirectReports d
202
203 ON e.groupid = d.member
204
205 )
206
207 SELECT b.nickname,groupid, member, EmployeeLevel,type
208
209 FROM DirectReports,userbasicinfo b
210
211 where DirectReports.member=b.id
212
213 and type = 1
214
215
216 USE AdventureWorks;
217 GO
218 WITH DirReps(ManagerID, DirectReports) AS
219 (
220 SELECT ManagerID, COUNT(*)
221 FROM HumanResources.Employee AS e
222 WHERE ManagerID IS NOT NULL
223 GROUP BY ManagerID
224 )
225 SELECT ManagerID, DirectReports
226 FROM DirReps
227 ORDER BY ManagerID;
228 GO
229
230
231 WITH DirReps (Manager, DirectReports) AS
232 (
233 SELECT ManagerID, COUNT(*) AS DirectReports
234 FROM HumanResources.Employee
235 GROUP BY ManagerID
236 )
237 SELECT AVG(DirectReports) AS [Average Number of Direct Reports]
238 FROM DirReps
239 WHERE DirectReports>= 2 ;
240 GO
241
242
243
244
245 USE AdventureWorks;
246 GO
247 WITH Sales_CTE (SalesPersonID, NumberOfOrders, MaxDate)
248 AS
249 (
250 SELECT SalesPersonID, COUNT(*), MAX(OrderDate)
251 FROM Sales.SalesOrderHeader
252 GROUP BY SalesPersonID
253 )
254 SELECT E.EmployeeID, OS.NumberOfOrders, OS.MaxDate,
255 E.ManagerID, OM.NumberOfOrders, OM.MaxDate
256 FROM HumanResources.Employee AS E
257 JOIN Sales_CTE AS OS
258 ON E.EmployeeID = OS.SalesPersonID
259 LEFT OUTER JOIN Sales_CTE AS OM
260 ON E.ManagerID = OM.SalesPersonID
261 ORDER BY E.EmployeeID;
262 GO
263
264 ---管理員下的下屬
265 USE AdventureWorks;
266 GO
267 WITH DirectReports(ManagerID, EmployeeID, EmployeeLevel) AS
268 (
269 SELECT ManagerID, EmployeeID, 0 AS EmployeeLevel
270 FROM HumanResources.Employee
271 WHERE ManagerID IS NULL
272 UNION ALL
273 SELECT e.ManagerID, e.EmployeeID, EmployeeLevel + 1
274 FROM HumanResources.Employee e
275 INNER JOIN DirectReports d
276 ON e.ManagerID = d.EmployeeID
277 )
278 SELECT ManagerID, EmployeeID, EmployeeLevel
279 FROM DirectReports
280 WHERE EmployeeLevel <= 2 ;
281 GO
282
283 USE AdventureWorks;
284 GO
285 WITH DirectReports(Name, Title, EmployeeID, EmployeeLevel, Sort)
286 AS (SELECT CONVERT(varchar(255), c.FirstName + ' ' + c.LastName),
287 e.Title,
288 e.EmployeeID,
289 1,
290 CONVERT(varchar(255), c.FirstName + ' ' + c.LastName)
291 FROM HumanResources.Employee AS e
292 JOIN Person.Contact AS c ON e.ContactID = c.ContactID
293 WHERE e.ManagerID IS NULL
294 UNION ALL
295 SELECT CONVERT(varchar(255), REPLICATE ('| ' , EmployeeLevel) +
296 c.FirstName + ' ' + c.LastName),
297 e.Title,
298 e.EmployeeID,
299 EmployeeLevel + 1,
300 CONVERT (varchar(255), RTRIM(Sort) + '| ' + FirstName + ' ' +
301 LastName)
302 FROM HumanResources.Employee as e
303 JOIN Person.Contact AS c ON e.ContactID = c.ContactID
304 JOIN DirectReports AS d ON e.ManagerID = d.EmployeeID
305 )
306 SELECT EmployeeID, Name, Title, EmployeeLevel
307 FROM DirectReports
308 ORDER BY Sort;
309 GO
310
311 USE AdventureWorks;
312 GO
313 --Creates an infinite loop
314 WITH cte (EmployeeID, ManagerID, Title) as
315 (
316 SELECT EmployeeID, ManagerID, Title
317 FROM HumanResources.Employee
318 WHERE ManagerID IS NOT NULL
319 UNION ALL
320 SELECT cte.EmployeeID, cte.ManagerID, cte.Title
321 FROM cte
322 JOIN HumanResources.Employee AS e
323 ON cte.ManagerID = e.EmployeeID
324 )
325 --Uses MAXRECURSION to limit the recursive levels to 2
326 SELECT EmployeeID, ManagerID, Title
327 FROM cte
328 OPTION (MAXRECURSION 2);
329 GO
330
331
332
333 USE AdventureWorks;
334 GO
335 WITH cte (EmployeeID, ManagerID, Title)
336 AS
337 (
338 SELECT EmployeeID, ManagerID, Title
339 FROM HumanResources.Employee
340 WHERE ManagerID IS NOT NULL
341 UNION ALL
342 SELECT e.EmployeeID, e.ManagerID, e.Title
343 FROM HumanResources.Employee AS e
344 JOIN cte ON e.ManagerID = cte.EmployeeID
345 )
346 SELECT EmployeeID, ManagerID, Title
347 FROM cte;
348 GO
349
350
351 USE AdventureWorks;
352 GO
353 WITH Parts(AssemblyID, ComponentID, PerAssemblyQty, EndDate, ComponentLevel) AS
354 (
355 SELECT b.ProductAssemblyID, b.ComponentID, b.PerAssemblyQty,
356 b.EndDate, 0 AS ComponentLevel
357 FROM Production.BillOfMaterials AS b
358 WHERE b.ProductAssemblyID = 800
359 AND b.EndDate IS NULL
360 UNION ALL
361 SELECT bom.ProductAssemblyID, bom.ComponentID, p.PerAssemblyQty,
362 bom.EndDate, ComponentLevel + 1
363 FROM Production.BillOfMaterials AS bom
364 INNER JOIN Parts AS p
365 ON bom.ProductAssemblyID = p.ComponentID
366 AND bom.EndDate IS NULL
367 )
368 SELECT AssemblyID, ComponentID, Name, PerAssemblyQty, EndDate,
369 ComponentLevel
370 FROM Parts AS p
371 INNER JOIN Production.Product AS pr
372 ON p.ComponentID = pr.ProductID
373 ORDER BY ComponentLevel, AssemblyID, ComponentID;
374 GO
375
2 (
3 Id INT IDENTITY(1,1) PRIMARY KEY,
4 [Name] varchar(30) null,
5 JobTitle varchar(30) null,
6 Manager int null
7 )
8
9 insert Employee
10 select 'incf', 'IT Director',null union all
11 select 'inc3', 'Finance Director',null union all
12 select 'geovindu', 'assces',1 union all
13 select 'du', 'assces',1 union all
14 select 'fa','account',2 union all
15 select 'd','account',2
16
17 WITH OrganisationChart (Id, [Name], JobTitle, Manager) AS
18 (
19 SELECT
20 Id, [Name], JobTitle, Manager
21 FROM dbo.Employee
22 WHERE
23 Manager IS NULL
24 UNION ALL
25 SELECT emp.Id, emp.[Name], emp.JobTitle, emp.Manager
26 FROM dbo.Employee emp
27 INNER JOIN OrganisationChart ON
28 emp.Manager = OrganisationChart.Id
29 )
30 SELECT * FROM OrganisationChart
31
32
33 WITH OrganisationChart (Id, [Name], JobTitle, [Level], Manager) AS
34 (
35 SELECT
36 Id, [Name], JobTitle, 0, Manager
37 FROM dbo.Employee
38 WHERE
39 Manager IS NULL
40 UNION ALL
41 SELECT emp.Id, emp.[Name], emp.JobTitle, [Level] + 1, emp.Manager
42 FROM dbo.Employee emp
43 INNER JOIN OrganisationChart ON
44 emp.Manager = OrganisationChart.Id
45 )
46 SELECT * FROM OrganisationChart
47 ORDER BY [Level]
48
49
50 WITH OrganisationChart (Id, [Name], JobTitle, [Level], Manager, [Root]) AS
51 (
52 SELECT Id, [Name], JobTitle, 0, Manager, Id
53 FROM dbo.Employee
54 WHERE Manager IS NULL
55 UNION ALL
56 SELECT emp.Id,emp.[Name],emp.JobTitle,[Level] + 1,emp.Manager,[Root]
57 FROM dbo.Employee emp
58 INNER JOIN OrganisationChart ON
59 emp.Manager = OrganisationChart.Id
60 )
61 SELECT * FROM OrganisationChart
62 --WHERE [Name] = 'incf'
63 WHERE [Root] = 1
64
65
66
67 --示例数据库
68
69 /*
70 递归查询对于同一个表父子关系的计算提供了很大的方便,这个示例使用了SQL server 2005中的递归查询,使用的表是CarParts,这个表存储了一辆汽车的所有零件以及结构,part为零件单位,subpart为子零件,Qty为数量。
71
72 具体示例如下:
73
74 */
75 CREATE TABLE CarParts
76
77 (
78
79 CarID INT NOT NULL,
80
81 Part VARCHAR(15),
82
83 SubPart VARCHAR(15),
84
85 Qty INT
86
87 )
88
89 GO
90
91 INSERT CarParts VALUES (1, 'Body', 'Door', 4)
92
93 INSERT CarParts VALUES (1, 'Body', 'Trunk Lid', 1)
94
95 INSERT CarParts VALUES (1, 'Body', 'Car Hood', 1)
96
97 INSERT CarParts VALUES (1, 'Door', 'Handle', 1)
98
99 INSERT CarParts VALUES (1, 'Door', 'Lock', 1)
100
101 INSERT CarParts VALUES (1, 'Door', 'Window', 1)
102
103 INSERT CarParts VALUES (1, 'Body', 'Rivets', 1000)
104
105 INSERT CarParts VALUES (1, 'Door', 'Rivets', 100)
106
107 INSERT CarParts VALUES (1, 'Door', 'Mirror', 1)
108
109 INSERT CarParts VALUES (1, 'Mirror', 'small_Mirror', 4)
110
111 GO
112
113 SELECT * FROM CarParts
114
115 GO
116
117 /*
118
119 一辆汽车需要各个零件的数目
120
121 1个Body 需要4个Door
122
123 1个Door 需要1个Mirror
124
125 那么
126
127 1个body需要4个Mirror
128
129 结构很简单吧
130
131 */
132
133 WITH CarPartsCTE(SubPart, Qty)
134
135 AS
136
137 (
138
139 -- 固定成员 (AM):
140
141 -- SELECT查询无需参考CarPartsCTE
142
143 -- 递归从此处开始
144
145 SELECT SubPart, Qty
146
147 FROM CarParts
148
149 WHERE Part = 'Body'
150
151 UNION ALL
152
153 -- 递归成员 (RM):
154
155 -- SELECT查询参考CarPartsCTE
156
157 -- 使用现有数据往下一层展开
158
159 SELECT CarParts.SubPart, CarPartsCTE.Qty * CarParts.Qty
160
161 FROM CarPartsCTE
162
163 INNER JOIN CarParts ON CarPartsCTE.SubPart = CarParts.Part
164
165 WHERE CarParts.CarID = 1
166
167 )
168
169 SELECT SubPart,Qty AS TotalNUM
170
171 FROM CarPartsCTE
172
173 /*
174
175 注意看最下层的small_Mirror 位于 表最后的位置,
176
177 由此可以看出改递归不是开始就进行递归查询而是在1层完全展开后在根据该层展开下一层不是深度优先的递归
178
179 */
180
181 drop table CarParts
182
183
184
185 WITH DirectReports(groupid, member, EmployeeLevel,type) AS
186
187 (
188
189 SELECT groupid, member, 0,type AS EmployeeLevel
190
191 FROM groupinfo
192
193 WHERE groupid = 'finance_company'
194
195 UNION ALL
196
197 SELECT e.groupid, e.member, EmployeeLevel + 1,e.type
198
199 FROM groupinfo e
200
201 INNER JOIN DirectReports d
202
203 ON e.groupid = d.member
204
205 )
206
207 SELECT b.nickname,groupid, member, EmployeeLevel,type
208
209 FROM DirectReports,userbasicinfo b
210
211 where DirectReports.member=b.id
212
213 and type = 1
214
215
216 USE AdventureWorks;
217 GO
218 WITH DirReps(ManagerID, DirectReports) AS
219 (
220 SELECT ManagerID, COUNT(*)
221 FROM HumanResources.Employee AS e
222 WHERE ManagerID IS NOT NULL
223 GROUP BY ManagerID
224 )
225 SELECT ManagerID, DirectReports
226 FROM DirReps
227 ORDER BY ManagerID;
228 GO
229
230
231 WITH DirReps (Manager, DirectReports) AS
232 (
233 SELECT ManagerID, COUNT(*) AS DirectReports
234 FROM HumanResources.Employee
235 GROUP BY ManagerID
236 )
237 SELECT AVG(DirectReports) AS [Average Number of Direct Reports]
238 FROM DirReps
239 WHERE DirectReports>= 2 ;
240 GO
241
242
243
244
245 USE AdventureWorks;
246 GO
247 WITH Sales_CTE (SalesPersonID, NumberOfOrders, MaxDate)
248 AS
249 (
250 SELECT SalesPersonID, COUNT(*), MAX(OrderDate)
251 FROM Sales.SalesOrderHeader
252 GROUP BY SalesPersonID
253 )
254 SELECT E.EmployeeID, OS.NumberOfOrders, OS.MaxDate,
255 E.ManagerID, OM.NumberOfOrders, OM.MaxDate
256 FROM HumanResources.Employee AS E
257 JOIN Sales_CTE AS OS
258 ON E.EmployeeID = OS.SalesPersonID
259 LEFT OUTER JOIN Sales_CTE AS OM
260 ON E.ManagerID = OM.SalesPersonID
261 ORDER BY E.EmployeeID;
262 GO
263
264 ---管理員下的下屬
265 USE AdventureWorks;
266 GO
267 WITH DirectReports(ManagerID, EmployeeID, EmployeeLevel) AS
268 (
269 SELECT ManagerID, EmployeeID, 0 AS EmployeeLevel
270 FROM HumanResources.Employee
271 WHERE ManagerID IS NULL
272 UNION ALL
273 SELECT e.ManagerID, e.EmployeeID, EmployeeLevel + 1
274 FROM HumanResources.Employee e
275 INNER JOIN DirectReports d
276 ON e.ManagerID = d.EmployeeID
277 )
278 SELECT ManagerID, EmployeeID, EmployeeLevel
279 FROM DirectReports
280 WHERE EmployeeLevel <= 2 ;
281 GO
282
283 USE AdventureWorks;
284 GO
285 WITH DirectReports(Name, Title, EmployeeID, EmployeeLevel, Sort)
286 AS (SELECT CONVERT(varchar(255), c.FirstName + ' ' + c.LastName),
287 e.Title,
288 e.EmployeeID,
289 1,
290 CONVERT(varchar(255), c.FirstName + ' ' + c.LastName)
291 FROM HumanResources.Employee AS e
292 JOIN Person.Contact AS c ON e.ContactID = c.ContactID
293 WHERE e.ManagerID IS NULL
294 UNION ALL
295 SELECT CONVERT(varchar(255), REPLICATE ('| ' , EmployeeLevel) +
296 c.FirstName + ' ' + c.LastName),
297 e.Title,
298 e.EmployeeID,
299 EmployeeLevel + 1,
300 CONVERT (varchar(255), RTRIM(Sort) + '| ' + FirstName + ' ' +
301 LastName)
302 FROM HumanResources.Employee as e
303 JOIN Person.Contact AS c ON e.ContactID = c.ContactID
304 JOIN DirectReports AS d ON e.ManagerID = d.EmployeeID
305 )
306 SELECT EmployeeID, Name, Title, EmployeeLevel
307 FROM DirectReports
308 ORDER BY Sort;
309 GO
310
311 USE AdventureWorks;
312 GO
313 --Creates an infinite loop
314 WITH cte (EmployeeID, ManagerID, Title) as
315 (
316 SELECT EmployeeID, ManagerID, Title
317 FROM HumanResources.Employee
318 WHERE ManagerID IS NOT NULL
319 UNION ALL
320 SELECT cte.EmployeeID, cte.ManagerID, cte.Title
321 FROM cte
322 JOIN HumanResources.Employee AS e
323 ON cte.ManagerID = e.EmployeeID
324 )
325 --Uses MAXRECURSION to limit the recursive levels to 2
326 SELECT EmployeeID, ManagerID, Title
327 FROM cte
328 OPTION (MAXRECURSION 2);
329 GO
330
331
332
333 USE AdventureWorks;
334 GO
335 WITH cte (EmployeeID, ManagerID, Title)
336 AS
337 (
338 SELECT EmployeeID, ManagerID, Title
339 FROM HumanResources.Employee
340 WHERE ManagerID IS NOT NULL
341 UNION ALL
342 SELECT e.EmployeeID, e.ManagerID, e.Title
343 FROM HumanResources.Employee AS e
344 JOIN cte ON e.ManagerID = cte.EmployeeID
345 )
346 SELECT EmployeeID, ManagerID, Title
347 FROM cte;
348 GO
349
350
351 USE AdventureWorks;
352 GO
353 WITH Parts(AssemblyID, ComponentID, PerAssemblyQty, EndDate, ComponentLevel) AS
354 (
355 SELECT b.ProductAssemblyID, b.ComponentID, b.PerAssemblyQty,
356 b.EndDate, 0 AS ComponentLevel
357 FROM Production.BillOfMaterials AS b
358 WHERE b.ProductAssemblyID = 800
359 AND b.EndDate IS NULL
360 UNION ALL
361 SELECT bom.ProductAssemblyID, bom.ComponentID, p.PerAssemblyQty,
362 bom.EndDate, ComponentLevel + 1
363 FROM Production.BillOfMaterials AS bom
364 INNER JOIN Parts AS p
365 ON bom.ProductAssemblyID = p.ComponentID
366 AND bom.EndDate IS NULL
367 )
368 SELECT AssemblyID, ComponentID, Name, PerAssemblyQty, EndDate,
369 ComponentLevel
370 FROM Parts AS p
371 INNER JOIN Production.Product AS pr
372 ON p.ComponentID = pr.ProductID
373 ORDER BY ComponentLevel, AssemblyID, ComponentID;
374 GO
375
哲学管理(学)人生, 文学艺术生活, 自动(计算机学)物理(学)工作, 生物(学)化学逆境, 历史(学)测绘(学)时间, 经济(学)数学金钱(理财), 心理(学)医学情绪, 诗词美容情感, 美学建筑(学)家园, 解构建构(分析)整合学习, 智商情商(IQ、EQ)运筹(学)生存.---Geovin Du(涂聚文)