微软BI开拓者数据库专区SQL Server开发 【挑战TSQL】父子结构数据的计算

1  /  2  页   12 跳转 查看:3107

【挑战TSQL】父子结构数据的计算

【挑战TSQL】父子结构数据的计算

示例数据如下(前面的01、02、03、04是SSMS里的顺序号,不用管):

雇员表

01.EmployeeID  FirstName      LastName        ReportsTo
02.----------- --------------- --------------- -----------
03.2          Andrew          Fuller          NULL
04.1          Nancy          Davolio        2
05.3          Janet          Leverling      2
06.4          Margaret        Peacock        2
07.5          Steven          Buchanan        2
08.8          Laura          Callahan        2
09.6          Michael        Suyama          5
10.7          Robert          King            5
11.9          Anne            Dodsworth      5


订单表(前面的01、02、03、04是SSMS里的顺序号,不用管)

01.OrderID    EmployeeID
02.----------- -----------
03.10258      1
04.10270      1
05.10275      1
06.10265      2
07.10277      2
08.10251      3
09.10253      3
10.10256      3
11.10250      4
12.10252      4
13.10248      5
14.10254      5
15.10249      6
16.10289      7
17.10303      7
18.10308      7
19.10262      8
20.10268      8
21.10276      8
22.10278      8
23.10255      9
24.10263      9





生成下面格式的输出结果(前面的01、02、03、04是SSMS里的顺序号,不用管):

01.Name                      Level by_self by_sub total
02.------------------------- ----- ------- ------ -----
03.Fuller, Andrew            0    2      20    22
04.    Buchanan, Steven      1    2      6      8
05.        Dodsworth, Anne  2    2      0      2
06.        King, Robert      2    3      0      3
07.        Suyama, Michael  2    1      0      1
08.    Callahan, Laura      1    4      0      4
09.    Davolio, Nancy        1    3      0      3
10.    Leverling, Janet      1    3      0      3
11.    Peacock, Margaret    1    2      0      2


要求:

1、使用基于集合的方式进行处理
2、不能使用表变量、临时表、while循环、自定义函数
3、可以使用CTE
4、输出结果顺序严格按照上面输出结果的顺序

示例数据说明:
1、ReportsTo表示对应的父节点的ID号


输出结果相关说明:
1、列level表示雇员的层次,最高层为0,次一层为1,每增加一层,名字前面就多加4个空格
2、列by_self表示自己销售的订单数,by_sub是下属销售的订单数,其中包括下属的下属一直到最下层,total表示by_self和by_sub的和


利用下面的语句产生示例数据:

DECLARE @emp TABLE (
EmployeeID INT,
FirstName VARCHAR(15),
LastName VARCHAR(15),
ReportsTo INT
)

DECLARE @ord TABLE (
OrderID INT,
EmployeeID INT
)

INSERT INTO @emp(EmployeeID, FirstName, LastName, ReportsTo)
SELECT 2,'Andrew','Fuller',NULL UNION ALL
SELECT 1,'Nancy','Davolio',2 UNION ALL
SELECT 3,'Janet','Leverling',2 UNION ALL
SELECT 4,'Margaret','Peacock',2 UNION ALL
SELECT 5,'Steven','Buchanan',2 UNION ALL
SELECT 8,'Laura','Callahan',2 UNION ALL
SELECT 6,'Michael','Suyama',5 UNION ALL
SELECT 7,'Robert','King',5 UNION ALL
SELECT 9,'Anne','Dodsworth',5
SELECT * FROM @emp

INSERT INTO @ord (OrderID, EmployeeID) 
SELECT 10258,1 UNION ALL
SELECT 10270,1 UNION ALL
SELECT 10275,1 UNION ALL
SELECT 10265,2 UNION ALL
SELECT 10277,2 UNION ALL
SELECT 10251,3 UNION ALL
SELECT 10253,3 UNION ALL
SELECT 10256,3 UNION ALL
SELECT 10250,4 UNION ALL
SELECT 10252,4 UNION ALL
SELECT 10248,5 UNION ALL
SELECT 10254,5 UNION ALL
SELECT 10249,6 UNION ALL
SELECT 10289,7 UNION ALL
SELECT 10303,7 UNION ALL
SELECT 10308,7 UNION ALL
SELECT 10262,8 UNION ALL
SELECT 10268,8 UNION ALL
SELECT 10276,8 UNION ALL
SELECT 10278,8 UNION ALL
SELECT 10255,9 UNION ALL
SELECT 10263,9 


SELECT * FROM @ord
虽有智慧,不如乘势;虽有鎡基,不如待时。
君子学以聚之,问以辨之,宽以居之,仁以行之。
独学而无友,则孤陋而寡闻。
 

回复:【挑战TSQL】父子结构数据的计算

DECLARE @emp TABLE (
EmployeeID INT,
FirstName VARCHAR(15),
LastName VARCHAR(15),
ReportsTo INT
)

DECLARE @ord TABLE (
OrderID INT,
EmployeeID INT
)

INSERT INTO @emp(EmployeeID, FirstName, LastName, ReportsTo)
SELECT 2,'Andrew','Fuller',NULL UNION ALL
SELECT 1,'Nancy','Davolio',2 UNION ALL
SELECT 3,'Janet','Leverling',2 UNION ALL
SELECT 4,'Margaret','Peacock',2 UNION ALL
SELECT 5,'Steven','Buchanan',2 UNION ALL
SELECT 8,'Laura','Callahan',2 UNION ALL
SELECT 6,'Michael','Suyama',5 UNION ALL
SELECT 7,'Robert','King',5 UNION ALL
SELECT 9,'Anne','Dodsworth',7
UNION ALL
SELECT 10,'Liang','JiaHui',NULL UNION ALL
SELECT 11,'Tang','LanLan',10 UNION ALL
SELECT 12,'LanLan','JiaJia',11

INSERT INTO @ord (OrderID, EmployeeID)
SELECT 10258,1 UNION ALL
SELECT 10270,1 UNION ALL
SELECT 10275,1 UNION ALL
SELECT 10265,2 UNION ALL
SELECT 10277,2 UNION ALL
SELECT 10251,3 UNION ALL
SELECT 10253,3 UNION ALL
SELECT 10256,3 UNION ALL
SELECT 10250,4 UNION ALL
SELECT 10252,4 UNION ALL
SELECT 10248,5 UNION ALL
SELECT 10254,5 UNION ALL
SELECT 10249,6 UNION ALL
SELECT 10289,7 UNION ALL
SELECT 10303,7 UNION ALL
SELECT 10308,7 UNION ALL
SELECT 10262,8 UNION ALL
SELECT 10268,8 UNION ALL
SELECT 10276,8 UNION ALL
SELECT 10278,8 UNION ALL
SELECT 10255,9 UNION ALL
SELECT 10263,9
UNION ALL
SELECT 10254,10 UNION ALL
SELECT 12520,11

;WITH Liang AS
(
    SELECT A.*,B.[Count]
    FROM @emp AS A
        LEFT JOIN (SELECT EmployeeID,COUNT(*) AS [Count]
                    FROM @ord GROUP BY EmployeeID) AS B
    ON A.EmployeeID = B.EmployeeID
),
Liang2 AS
(
    SELECT EmployeeID,CAST(LastName+','+FirstName AS varchar(50)) AS Name,
        ReportsTo,[Count],
        CAST(ROW_NUMBER() OVER(ORDER BY EmployeeID) AS varbinary(MAX)) AS tree,
        level = 0
    FROM Liang
    WHERE ReportsTo IS NULL
    UNION ALL
    SELECT A.EmployeeID,CAST(LastName+','+FirstName AS varchar(50)) AS name,
        A.ReportsTo,A.[Count],
        CAST(B.tree+CAST(ROW_NUMBER() OVER(PARTITION BY A.ReportsTo
                                  ORDER BY B.EmployeeID ) AS binary(4)) AS varbinary(MAX)),
        B.level + 1
    FROM Liang AS A
        JOIN Liang2 AS B
    ON B.EmployeeID = A.ReportsTo
),
Liang3 AS
(
    SELECT A.EmployeeID AS flag ,B.EmployeeID AS empid,B.ReportsTo AS parentID,
        B.[Count]
    FROM Liang AS A
        JOIN Liang AS B
    ON A.EmployeeID = B.ReportsTo
    UNION ALL
    SELECT A.flag,B.EmployeeID,B.ReportsTo,
        ISNULL(B.[Count],0)
    FROM Liang3 AS A
        JOIN Liang AS B
    ON A.empid = B.ReportsTo
)
SELECT REPLICATE(' ',A.level*2)+'|--'+A.Name AS Name,
    level,
    by_self=ISNULL(A.[Count],0),
    by_sub=ISNULL(B.[Count],0),
    total=ISNULL(A.[Count],0)+ISNULL(B.[Count],0)
FROM Liang2 AS A
    LEFT JOIN (
        SELECT flag,SUM([Count]) AS [Count]
        FROM Liang3 GROUP BY flag
    ) AS B
ON A.EmployeeID=B.flag
ORDER BY tree;

/*
Name                                              level      by_self    by_sub      total
-------------------------------------------------- ----------- ----------- ----------- -----------
|--Fuller,Andrew                                  0          2          20          22
  |--Davolio,Nancy                                1          3          0          3
  |--Leverling,Janet                              1          3          0          3
  |--Peacock,Margaret                              1          2          0          2
  |--Buchanan,Steven                              1          2          6          8
    |--Suyama,Michael                              2          1          0          1
    |--King,Robert                                2          3          2          5
      |--Dodsworth,Anne                            3          2          0          2
  |--Callahan,Laura                                1          4          0          4
|--JiaHui,Liang                                    0          1          1          2
  |--LanLan,Tang                                  1          1          0          1
    |--JiaJia,LanLan                              2          0          0          0

(12 行受影响)
*/
 

回复:【挑战TSQL】父子结构数据的计算

这个顺序也不对。另请别自己添加数据,否则没法和测试数据进行比对。
虽有智慧,不如乘势;虽有鎡基,不如待时。
君子学以聚之,问以辨之,宽以居之,仁以行之。
独学而无友,则孤陋而寡闻。
 

回复:【挑战TSQL】父子结构数据的计算

你的排序也不对吧?

05.        Dodsworth, Anne  2    2      0      2
06.        King, Robert      2    3      0      3
07.        Suyama, Michael  2    1      0      1

King 有3个.Dodsworth有2个..King不是应该在Dods的前面?
 

回复:【挑战TSQL】父子结构数据的计算

DECLARE @emp TABLE (
EmployeeID INT,
FirstName VARCHAR(15),
LastName VARCHAR(15),
ReportsTo INT
)

DECLARE @ord TABLE (
OrderID INT,
EmployeeID INT
)

INSERT INTO @emp(EmployeeID, FirstName, LastName, ReportsTo)
SELECT 2,'Andrew','Fuller',NULL UNION ALL
SELECT 1,'Nancy','Davolio',2 UNION ALL
SELECT 3,'Janet','Leverling',2 UNION ALL
SELECT 4,'Margaret','Peacock',2 UNION ALL
SELECT 5,'Steven','Buchanan',2 UNION ALL
SELECT 8,'Laura','Callahan',2 UNION ALL
SELECT 6,'Michael','Suyama',5 UNION ALL
SELECT 7,'Robert','King',5 UNION ALL
SELECT 9,'Anne','Dodsworth',5
;

INSERT INTO @ord (OrderID, EmployeeID) 
SELECT 10258,1 UNION ALL
SELECT 10270,1 UNION ALL
SELECT 10275,1 UNION ALL
SELECT 10265,2 UNION ALL
SELECT 10277,2 UNION ALL
SELECT 10251,3 UNION ALL
SELECT 10253,3 UNION ALL
SELECT 10256,3 UNION ALL
SELECT 10250,4 UNION ALL
SELECT 10252,4 UNION ALL
SELECT 10248,5 UNION ALL
SELECT 10254,5 UNION ALL
SELECT 10249,6 UNION ALL
SELECT 10289,7 UNION ALL
SELECT 10303,7 UNION ALL
SELECT 10308,7 UNION ALL
SELECT 10262,8 UNION ALL
SELECT 10268,8 UNION ALL
SELECT 10276,8 UNION ALL
SELECT 10278,8 UNION ALL
SELECT 10255,9 UNION ALL
SELECT 10263,9 

;

with cte_ord as
(
select EmployeeID,Name=LastName+','+FirstName,Level=0
,oid=cast(LastName+','+FirstName+replicate(char(7),25-len(LastName+','+FirstName)) as varchar(200))
from @emp a where ReportsTo is null
union all
select a.EmployeeID,Name=a.LastName+','+a.FirstName,Level=b.Level+1
,oid=cast(b.oid+a.LastName+','+a.FirstName+replicate(char(7),25-len(a.LastName+','+a.FirstName)) as varchar(200))
from @emp a inner join cte_ord b on a.ReportsTo=b.EmployeeID
)

select Name=replicate(' ',Level*4)+a.Name,a.Level
,by_self=(select count(*) from @ord where EmployeeID=a.EmployeeID)
,by_sub=(select count(*) from @ord x inner join cte_ord y on x.EmployeeID=y.EmployeeID where y.oid like a.oid+'%' and y.oid<>a.oid)
,total=(select count(*) from @ord x inner join cte_ord y on x.EmployeeID=y.EmployeeID where y.oid like a.oid+'%')
from cte_ord a
order by oid

--result
Name    Level    by_self    by_sub    total
Fuller,Andrew    0    2    20    22
    Buchanan,Steven    1    2    6    8
        Dodsworth,Anne    2    2    0    2
        King,Robert    2    3    0    3
        Suyama,Michael    2    1    0    1
    Callahan,Laura    1    4    0    4
    Davolio,Nancy    1    3    0    3
    Leverling,Janet    1    3    0    3
    Peacock,Margaret    1    2    0    2
 

回复:【挑战TSQL】父子结构数据的计算

不是按数量排的序?
 

回复:【挑战TSQL】父子结构数据的计算

从示例看应该是标准tree的排序,同级子节点用名字来排序
 

回复: 【挑战TSQL】父子结构数据的计算



引用:
原帖由 zsforever 于 2009-12-18 16:34:00 发表
从示例看应该是标准tree的排序,同级子节点用名字来排序





嗯,对。
虽有智慧,不如乘势;虽有鎡基,不如待时。
君子学以聚之,问以辨之,宽以居之,仁以行之。
独学而无友,则孤陋而寡闻。
 

回复:【挑战TSQL】父子结构数据的计算

哦,那我想复杂了.
 

回复:【挑战TSQL】父子结构数据的计算

结合CTE应该不难,周一回公司试试
 
一错再错,继续错就对了。
  
 
1  /  2  页   12 跳转

版权所有 微软BI开拓者 

Powered by Discuz!NT 2.1.202    Copyright © 2001-2012 Comsenz Inc.
Processed in 1.2187812 second(s) , 3 queries.
返顶部