【问题标题】:SQL: Count of records by consecutive date, even when no records exist for a dateSQL:按连续日期计算记录数,即使某个日期不存在记录
【发布时间】:2017-08-17 11:00:24
【问题描述】:

我使用的是 SQL Server 2008 R2。

我正在查询医院预约空档表,并尝试返回一个列表,其中列出了标记为已预订的特定医生的预约空档数量,按周数/年分组。

有些情况下几周还没有任何预约,但我希望结果列出所有未来几周,即使预约的预约空档数为零。

我正在寻找的输出是这样的:

-------------------------------------------
Year | Week Number | Number of Booked Slots
-------------------------------------------
2017 |     48      |        10            
2017 |     49      |         0
2017 |     50      |         4
2017 |     51      |         2
2017 |     52      |         0
2018 |      1      |         5

我知道,聚合结果的标准选择不会显示记录数为零的那几周,因为没有什么可以返回 - 所以我试图通过使用 cte 首先生成一个列表来解决这个问题未来几周。

但是,尽我所能,我无法让查询显示零周...

我已经看到了许多类似问题的解决方案,但尽管进行了实验,但我无法将它们应用于我的特定问题(包括 SQL Count to include zero values

这是迄今为止我编写的查询的最新迭代。

    WITH CTE_Dates AS
    (
    SELECT DISTINCT Slot_Start_Date AS cte_date
    FROM [Outpatients.vw_OP_Clinic_Slots WITH (NOLOCK)
    )

SELECT 
    DATEPART(year,OPCS.Slot_Start_Date) [Year]
    ,DATEPART(week,OPCS.Slot_Start_Date) [Week Number]
    ,count(OPCS.Slot_Start_Date) [Number of Booked Slots]

FROM 
    Outpatients.vw_OP_Clinic_Slots OPCS WITH (NOLOCK)
    LEFT OUTER JOIN CTE_Dates ON OPCS.Slot_Start_Date=CTE_Dates.cte_date
    LEFT OUTER JOIN Outpatients.vw_OP_Clinics CLIN ON OPCS.Clinic_Code=CLIN.Clinic_Code

WHERE
    OPCS.Slot_Start_Date >= '14/08/2017'
    AND OPCS.Booked_Flag = 'Y'
    AND CLIN.Lead_Healthcare_Professional_Name = 'Dr X'

GROUP BY
    DATEPART(year,OPCS.Slot_Start_Date)
    ,DATEPART(week,OPCS.Slot_Start_Date)

ORDER BY 
    DATEPART(year,OPCS.Slot_Start_Date)asc
    ,DATEPART(week,OPCS.Slot_Start_Date)asc

它返回给我的结果是正确的,但我只需要它在列表中包含计数为零的那些周。

请谁能解释我哪里出错了?我猜我没有正确加入 cte,但我已经尝试了产生相同结果的右连接和左连接。我还尝试通过交换上述查询语句和 cte 来反转查询,但这也不起作用。

感谢任何人可以提供的任何指导。

【问题讨论】:

    标签: sql sql-server sql-server-2008 tsql


    【解决方案1】:

    只需 RIGHT JOIN @ListOfWeeks 表到您拥有的结果集:

    DECLARE @ListOfWeeks TABLE ([Week_No] int, [Year_Number] int);
    
    DECLARE @i tinyint = 1, @y int = 2010;
    
    WHILE @i <= 52 AND @y < 2018
    BEGIN
        INSERT INTO @ListOfWeeks([Week_No], [Year_Number]) VALUES (@i, @y);
    
        IF @i = 52 BEGIN
           SET @i = 0
           SET @y +=1
           END
         SET @i += 1
    END
    
    SELECT * FROM @ListOfWeeks
    
    WITH [Your_Part] AS(
    SELECT 
        DATEPART(year,OPCS.Slot_Start_Date) [Year]
        ,DATEPART(week,OPCS.Slot_Start_Date) [Week Number]
        ,count(OPCS.Slot_Start_Date) [Number of Booked Slots]
    
    FROM 
        Outpatients.vw_OP_Clinic_Slots OPCS WITH (NOLOCK)
        LEFT OUTER JOIN CTE_Dates ON OPCS.Slot_Start_Date=CTE_Dates.cte_date
        LEFT OUTER JOIN Outpatients.vw_OP_Clinics CLIN ON OPCS.Clinic_Code=CLIN.Clinic_Code
    
    WHERE
        OPCS.Slot_Start_Date >= '14/08/2017'
        AND OPCS.Booked_Flag = 'Y'
        AND CLIN.Lead_Healthcare_Professional_Name = 'Dr X'
    
    GROUP BY
        DATEPART(year,OPCS.Slot_Start_Date)
        ,DATEPART(week,OPCS.Slot_Start_Date)
    ),
    SELECT xxx.[Year_Number], xxx.[Week_No], yp.[Number of Booked Slots]
    FROM [Your_Part] yp
    RIGHT JOIN @ListOfWeeks xxx ON  yp.[Year] = xxx.[Year_Number] AND yp.[Week Number] = xxx.[Week_No]
    

    【讨论】:

    • 感谢您的帮助 @Bartosz X。不幸的是,这对我不起作用 - 我收到错误 Operand type clash: date is incompatible with int 也许我的连接语法不正确?我用过:RIGHT JOIN @ListOfWeeks ON OPCS.Slot_Start_Date=Week_No这个对吗?
    • 不,改为在 week_number 上加入
    • 抱歉@Bartosz_X 我仍然无法完成这项工作。我正在尝试:RIGHT JOIN @ListOfWeeks ON DATEPART(week,OPCS.Slot_Start_Date)=Week_No 但它仍然会在没有零周的情况下返回相同的结果。我尝试加入 [Week_Number] 但这会产生错误:Incorrect syntax near 'Week_Number'. 我假设因为 [Week Number] 是一个别名列,您不能加入它?感谢您的帮助 - 请您更具体地帮助我理解吗?
    • 我刚刚为你添加了一个完整的解决方案,享受:)
    • @Bartosz_X 非常感谢您的帮助,效果很好。为了后代,对上述脚本进行了一些非常小的调整以使其正确运行:在END 之后添加分号,注释掉第一个SELECT * FROM @ListOfWeeks 语句以抑制输出,从较早的迭代:LEFT OUTER JOIN CTE_Dates ON OPCS.Slot_Start_Date=CTE_Dates.cte_date 我只是在末尾添加了一个ORDER BY 以添加按时间顺序显示的升序。完美运行。再次感谢您的帮助。
    【解决方案2】:
    IF OBJECT_ID(N'tempdb..##cal_weeks_temp', N'U') IS NOT NULL   
    DROP TABLE ##cal_weeks_temp;  
    create table ##cal_weeks_temp (date_of_week date, week_num int)
    declare @start_date date 
    declare @end_date date 
    set @start_date='01/01/2017'
    set @end_date='12/31/2018'
    while @start_date<@end_date
    begin
        set @start_date=dateadd(day,1,@start_date)  
        insert into ##cal_weeks_temp values (@start_date,DATEPART(week,@start_date))
    end
    select YEAR(t1.date_of_week) 'YEAR',t1.week_num,
    sum(case convert(varchar,t2.BookedTime,105) when convert(varchar,t1.date_of_week,105)  then 1 else 0 end) 'count'
     from ##cal_weeks_temp t1
    left join  Your_Table t2
    on convert(varchar,t2.BookedTime,105)=convert(varchar,t1.date_of_week,105) 
    group by YEAR(t1.date_of_week) ,t1.week_num
    order by YEAR(t1.date_of_week) ,t1.week_num
    

    【讨论】:

    • @Javlon_Ismatov 非常感谢您的帮助。不幸的是,这对我不起作用,尽管我可能没有正确应用查询。我假设t2.BookedTime 指的是我的变量Slot_Start_Date - 这是正确的吗?我已经相应地修改了BookedTime 的所有实例并且查询运行。但是,它返回的结果计数不正确(当我对源数据进行交叉检查时)。此外,它没有显示预订量为零的周数,结果集在第 47 周结束(我原以为它会显示到第 52 周)
    • 我已经修改了我的查询,希望对您有所帮助。对于永久解决方案,您需要为日历年创建新表,它应该包括日期、年份、周数等以获得更好的性能。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-17
    • 2016-05-18
    • 1970-01-01
    • 2017-04-30
    • 2021-05-25
    相关资源
    最近更新 更多