![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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