总结数据,包括零数据存在的地方

问题描述 投票:0回答:2

我有一个包含年份列表的表格,从 2010 年开始,按照.

CREATE TABLE Years
    (YearName int)
;
    
INSERT INTO Years
    (YearName)
VALUES
    (2010),
    (2011),
    (2012),
    (2013),
    (2014),
    (2015),
    (2016),
    (2017),
    (2018),
    (2019),
    (2020),
    (2021),
    (2022),
    (2023),
    (2024),
    (2025)

我有第二张包含人的桌子,按照

CREATE TABLE People
    (PersonID int PRIMARY KEY, PersonName varchar(50))
;
    
INSERT INTO People
    (PersonID, PersonName)
VALUES
    (1, 'Bob'),
    (2, 'Kate'),
    (3, 'Jo'),
    (4, 'Fred')
;

我有一张表格,其中包含人们每年所做的各种类型的工作:

CREATE TABLE Workload
    (ID int PRIMARY KEY, PersonID int, YearName int, WorkType varchar(8), Hours int)
;
    
INSERT INTO Workload
    (ID, PersonID, YearName, WorkType, Hours)
VALUES
    (1, 1, 2014, 'Plumbing', 7),
    (2, 1, 2020, 'Washing', 9),
    (3, 1, 2020, 'Cooking', 10),
    (4, 1, 2020, 'Drawing', 4),
    (5, 1, 2021, 'Reading', 2),
    (6, 2, 2020, 'Washing', 9),
    (7, 2, 2021, 'Cooking', 10),
    (8, 2, 2022, 'Drawing', 4),
    (9, 3, 2014, 'Cooking', 4),
    (10, 3, 2014, 'Plumbing', 22),
    (11, 3, 2015, 'Washing', 7)
;

我想总结每个人每年完成的工作总量。所以,我以此为出发点:

SELECT PersonName, YearName, SUM(Hours) as WorkDone
FROM People p INNER JOIN Workload w ON p.PersonID=w.PersonID
WHERE YearName BETWEEN YEAR(GetDate())-9 AND YEAR(GETDATE())
GROUP BY PersonName, YearName

这很好用,但我想要的是一个输出,在他们没有完成这些任务的每一年中,每个人的价值为零:

工作量
鲍勃 2014 7
鲍勃 2015 0
鲍勃 2016 0
鲍勃 2017 0
鲍勃 2018 0
鲍勃 2019 0
鲍勃 2020 23
鲍勃 2021 0
鲍勃 2022 0
鲍勃 2023 0
凯特 2014 0
凯特 2015 0
凯特 2016 0
凯特 2017 0
凯特 2018 0
凯特 2019 0
凯特 2020 9
凯特 2021 10
凯特 2022 4
凯特 2023 0

...其他人和年份等等。

我如何最好地实现这一目标?我觉得我可能需要交叉应用这三个表,但似乎无法弄清楚如何这样做并获得我需要的结果。

sql sql-server subquery left-join aggregate-functions
2个回答
0
投票

您可以

cross join
年和人表生成所有可能的组合,然后将工作量表与
LEFT JOIN
- 这确保“缺失”的年/人元组不会被过滤掉。最后一步是聚合,
COALESCE()
在不匹配的元组上返回
0

SELECT p.PersonName, y.YearName, COALESCE(SUM(w.Hours), 0) as WorkDone
FROM People p 
CROSS JOIN Years y
LEFT JOIN Workload w ON w.PersonID = p.PersonID AND w.YearName = y.YearName
WHERE y.YearName BETWEEN YEAR(GetDate())-9 AND YEAR(GETDATE())
GROUP BY p.PersonID, p.PersonName, y.YearName
ORDER BY p.PersonName, y.YearName

注意在

group by
子句中包含person id会更安全;两个不同的人可能有相同的名字,您可能不希望将他们的工作负载组合在一起。

我们还可以使用相关子查询(或

apply
)来进行工作负载计算,这将避免外部聚合:

SELECT p.PersonName, y.YearName, w.*
FROM People p 
CROSS JOIN Years y
CROSS APPLY (
    SELECT COALESCE(SUM(w.Hours), 0) as WorkDone
    FROM Workload w 
    WHERE w.PersonID = p.PersonID AND w.YearName = y.YearName
) w
WHERE y.YearName BETWEEN YEAR(GetDate())-9 AND YEAR(GETDATE())
ORDER BY p.PersonName, y.YearName 

0
投票

通过使用此查询,提取每个人的最小年份,并根据不存在的天数设置零值

dbfiddle


;with _list as (

select *
        from (
                SELECT 
PersonName, YearName,sum(w.Hours) over(partition by p.PersonID,YearName) as Workload
                ,row_number() over(partition by p.PersonID,YearName order by  p.PersonID) as rw
                ,min(YearName) over(partition by p.PersonID order by  p.PersonID) as minYearName
                --, SUM(Hours) as WorkDone
                FROM People p 
                INNER JOIN Workload w ON p.PersonID=w.PersonID

        )a
        where a.rw=1  
)
select  
ISNULL(a.PersonName,b.PersonName) as PersonName ,b.YearName
,ISNULL(a.Workload,b.Workload) as Workload
from (
        select a.PersonName,b.YearName,0 Workload
        from Years b
        cross join (
                select PersonName,min(minYearName) as minYearName
                from _list
                group by PersonName
        )a
        where b.YearName between a.minYearName and  DATEPART(YEAR ,GETDATE())
)b
left join  _list a on  a.YearName=b.YearName 
and  a.PersonName=b.PersonName 


© www.soinside.com 2019 - 2024. All rights reserved.