如何在多个条件下多次返回同一行

时间:2019-09-26 08:47:04

标签: sql sql-server date range overlap

我的知识非常基础,因此将非常感谢您的帮助。

我试图在满足条件时多次返回同一行(我只能选择查询)。

我有一个包含Customer ID, Start Date and End Date的500000条记录的表,其中end date可能是null

我正在尝试添加一个名为Week_No的新列,并相应列出所有行。例如,如果日期范围超过一个星期,则必须使用相应的星期数多次返回该行。另外,我想计算重叠的天数,即每行永远不超过7(周),然后使用第二张表计算不可用的天数。

下面的示例数据

t1

ID     | Start_Date | End_Date
000001 | 12/12/2017 | 03/01/2018
000002 | 13/01/2018 |
000003 | 02/01/2018 | 11/01/2018
...

t2

ID     | Unavailable
000002 | 14/01/2018
000003 | 03/01/2018
000003 | 04/01/2018
000003 | 08/01/2018
...

我无法通过添加星期几的阶段。我曾尝试使用CASEUNION ALL,但一直收到错误消息。

declare @week01start datetime = '2018-01-01 00:00:00'   
declare @week01end datetime = '2018-01-07 00:00:00' 
declare @week02start datetime = '2018-01-08 00:00:00'   
declare @week02end datetime = '2018-01-14 00:00:00' 
...
SELECT  
  ID,
  '01' as Week_No,
  '2018' as YEAR,
  Start_Date, 
  End_Date 

FROM t1 
WHERE (Start_Date <= @week01end and End_Date >= @week01start)
 or (Start_Date <= @week01end and End_Date is null)
UNION ALL   
SELECT
  ID,
  '02' as Week_No,
  '2018' as YEAR,
  Start_Date, 
  End_Date  
FROM t1 
WHERE (Start_Date <= @week02end and End_Date >= @week02start) 
 or (Start_Date <= @week02end and End_Date is null)
...

新表应如下所示

ID     | Week_No | Year | Start_Date | End_Date   | Overlap | Unavail_Days
000001 | 01      | 2018 | 12/12/2017 | 03/01/2018 | 3       | 
000002 | 02      | 2018 | 13/01/2018 |            | 2       | 1
000003 | 01      | 2018 | 02/01/2018 | 11/01/2018 | 6       | 2
000003 | 02      | 2018 | 02/01/2018 | 11/01/2018 | 4       | 1
...

1 个答案:

答案 0 :(得分:0)

从商业角度来看,我无法理解您要实现的目标。您可以使用以下代码来计算您的重叠天数等。我按照您的要求进行了操作,但是我建议您使用单独的表,例如Time维度,以产生“更清洁”的解决方案

/*sample data set in temp table*/
select '000001' as id, '2017-12-12'as start_dt, ' 2018-01-03' as end_dt into #tmp union
select '000002' as id, '2018-01-13 'as start_dt, null as end_dt union
select '000003' as id, '2018-01-02' as start_dt, '2018-01-11' as end_dt 

/*calculate week numbers and week diff according to dates*/
select *,
DATEPART(WK,start_dt) as start_weekNumber,
DATEPART(WK,end_dt) as end_weekNumber,
case 
    when DATEPART(WK,end_dt) - DATEPART(WK,start_dt) > 0 then  (DATEPART(WK,end_dt) - DATEPART(WK,start_dt)) +1
    else (52 - DATEPART(WK,start_dt)) + DATEPART(WK,end_dt)
end as WeekDiff 
into #tmp1
 from 
        (
        SELECT *,DATEADD(DAY, 2 - DATEPART(WEEKDAY, start_dt), CAST(start_dt AS DATE)) [start_dt_Week_Start_Date],
        DATEADD(DAY, 8 - DATEPART(WEEKDAY, start_dt), CAST(start_dt AS DATE)) [startdt_Week_End_Date],
        DATEADD(DAY, 2 - DATEPART(WEEKDAY, end_dt), CAST(end_dt AS DATE)) [end_dt_Week_Start_Date],
        DATEADD(DAY, 8 - DATEPART(WEEKDAY, end_dt), CAST(end_dt AS DATE)) [end_dt_Week_End_Date]
         from #tmp
        ) s

/*cte used to create duplicates when week diff is over 1*/
;with x as
(
 SELECT TOP (10) rn = ROW_NUMBER() --modify the max you want
  OVER (ORDER BY [object_id]) 
  FROM sys.all_columns 
  ORDER BY [object_id]
)  
/*final query*/
select  --*
            ID,
            start_weekNumber+ (r-1) as Week,
            DATEPART(YY,start_dt) as [YEAR],
            start_dt,
            end_dt,
            null as Overlap,
            null as unavailable_days
             from

(
        select *,
        ROW_NUMBER() over (partition by id order by id) r
         from
        (
            select d.* from x
            CROSS JOIN #tmp1 AS d
            WHERE x.rn <= d.WeekDiff
            union all
            select * from #tmp1
            where WeekDiff is null

        ) a
)a_ext
order by id,start_weekNumber

--drop table #tmp1,#tmp

以上将产生所需的结果,但重叠和不可用的列除外。我不只是计算周数,还使用start_dt添加了一年中的周数,但是如果您不喜欢它,可以更改它:

  ID    Week    YEAR    start_dt    end_dt  Overlap unavailable_days
000001  50  2017    2017-12-12   2018-01-03 NULL    NULL
000001  51  2017    2017-12-12   2018-01-03 NULL    NULL
000001  52  2017    2017-12-12   2018-01-03 NULL    NULL
000002  2   2018    2018-01-13        NULL  NULL    NULL
000003  1   2018    2018-01-02   2018-01-11 NULL    NULL
000003  2   2018    2018-01-02   2018-01-11 NULL    NULL