SQL - 将几个日期记录转换为单个日期范围

时间:2018-05-02 09:57:54

标签: sql-server database gaps-and-islands

我有2个表名为请假请求和请假详细信息,它包含如下数据

Leave Request
Leave_Req_Id|start_date | end_date  |
------------+-----------+-----------+
  lvl10001  | 2013-01-05| 2013-01-08|
            |           |           |

Leave Request Detail
Req_Detail_Id |start_date | end_date   | canceled | 
--------------+-----------+------------+----------+-
    lvl10001  | 2013-01-05| 2013-01-05 |   no     | 
    lvl10001  | 2013-01-06| 2013-01-06 |   no     | 
    lvl10001  | 2013-01-07| 2013-01-07 |   yes    |   
    lvl10001  | 2013-01-08| 2013-01-08 |   no     | 

我想要的结果是排除取消的记录并将数据放入视图表中,如下所示

    Report_Id |start_date | end_date   | 
--------------+-----------+------------+-
    rep10001  | 2013-01-05| 2013-01-06 | 
    rep10002  | 2013-01-08| 2013-01-08 | 

If the cancelled record starts from the front or back of the range, the 
result should be like below
 Eg. Cancelled on 2013-01-05 only
    Report_Id |start_date | end_date   | 
--------------+-----------+------------+-
    rep10001  | 2013-01-06| 2013-01-08 | 
              |           |            | 

 Eg. Cancelled on 2013-01-08 only
    Report_Id |start_date | end_date   | 
--------------+-----------+------------+-
    rep10001  | 2013-01-05| 2013-01-07 | 
              |           |            | 


If there is no cancelled record, the result should be like below
    Report_Id |start_date | end_date   | 
--------------+-----------+------------+-
    rep10001  | 2013-01-05| 2013-01-08 | 
              |           |            | 

如何查询?

当前结果(2018年6月19日)基于马扎尔的方法。 我修改了你的查询abit

查询:

WITH cteX 
AS(
SELECT 
     Id = ROW_NUMBER()OVER(PARTITION BY D.request_no ORDER BY 
D.leave_starttime ASC)
    ,a.leave_code
    ,a.emp_id
    ,D.request_no
    ,D.leave_starttime
    ,D.leave_endtime
    ,D.cancelsts
FROM TTADLEAVEREQUEST a
left join TTADLEAVEREQUESTDETAIL D on a.request_no = d.request_no
where a.emp_id = 'emp1' and Year(a.leave_startdate) = 
2018
)
SELECT 
 ReportId = 'rep1000' +  CAST(ROW_NUMBER()OVER(ORDER BY (SELECT NULL) ) AS 
NVARCHAR(5))
,leave_starttime = MIN(Y.leave_starttime)
,leave_endtime = MAX(Y.leave_endtime)
,leave_code = Y.leave_code
,Count(Y.leave_starttime) as TOTAL_COUNT
FROM (
SELECT TOP 100 PERCENT
      Grp = Id - ROW_NUMBER()OVER( PARTITION BY X.cancelsts ORDER BY X.Id) 
     ,X.request_no
     ,X.leave_code
     ,X.leave_starttime
     ,X.leave_endtime
     ,X.cancelsts
FROM 
    cteX X
WHERE
    X.cancelsts = 'n'
ORDER BY
    X.emp_id
) Y
GROUP BY
Y.Grp,Y.leave_code

=============================================== ===========

LeaveRequest Table
request_no||leave_code||emp_id||leave_startdate ||leave_enddate ||  
==================================================================
1001      ||     AL   ||emp1  ||2018-01-29 00:00||2018-01-30 00:00
1002      ||     AL   ||emp1  ||2018-02-01 00:00||2018-02-02 00:00    
1003      ||     AL   ||emp1  ||2018-02-12 00:00||2018-02-13 00:00  

LeaveRequestDetail Table
request_no||    leave_starttime    ||  leave_endtime     ||  cancelsts
=====================================================
1001      ||     2018-01-29 08:00  ||  2018-01-29 17:00  ||    N
1001      ||     2018-01-30 08:00  ||  2018-01-30 17:00  ||    N
1002      ||     2018-02-01 08:00  ||  2018-02-01 17:00  ||    N
1002      ||     2018-02-02 08:00  ||  2018-02-02 17:00  ||    N
1003      ||     2018-02-12 08:00  ||  2018-02-12 17:00  ||    N
1003      ||     2018-02-13 08:00  ||  2018-02-13 17:00  ||    N

此查询的结果:

Result Table
ReportID  || leave_starttime  ||  leave_endtime || leave_code || TOTAL_COUNT
======================================================================
rep10001  ||    2018-01-30    ||  2018-01-30    ||      AL    ||         1
rep10002  ||     2018-02-02   ||  2018-02-02    ||      AL    ||         1
rep10003  ||     2018-02-12   ||  2018-02-13    ||      AL    ||         2
rep10004  ||     2018-02-01   ||  2018-02-01    ||      AL    ||         1
rep10005  ||     2018-01-29   ||  2018-01-29    ||      AL    ||         1

正如您所看到的,结果应该只有3行(例如,1月29日和1月30日以及2月1日和2月02日应该是单一记录)。真的很感激你可以提供帮助。感谢。

4 个答案:

答案 0 :(得分:0)

一种方法是使用row_numbers

的差异
select replace(min(rd.Req_Detail_Id), 'det', 'rep') as Report_Id,
       min(rd.start_date) as start_date, max(rd.end_date) as end_date
from (
    select *,
           row_number() over (order by Req_Detail_Id) Seq1,
           row_number() over (partition by Leave_Req_Id, canceled order by Req_Detail_Id) Seq2
    from LeaveRequest r
    inner join LeaveRequestDetail rd on rd.Leave_Req_Id = r.Leave_Req_Id
    where rd.canceled = 'no'
) t
group by (Seq1-Seq2);

答案 1 :(得分:0)

尝试以下脚本 样本数据

DECLARE @LeaveRequest AS TABLE (Leave_Req_Id varchar(100),[start_date] DATE , end_date DATE)
INSERT INTO @LeaveRequest
SELECT 'lvl10001'  , '2013-01-05', '2013-01-08'

DECLARE @LeaveRequestDetail AS TABLE(Req_Detail_Id varchar(100) ,[start_date] DATE, end_date DATE   , canceled varchar(5), Leave_Req_Id varchar(100))
INSERT INTO @LeaveRequestDetail
SELECT 'det10001'  , '2013-01-05', '2013-01-05' ,'no' ,  'lvl10001' UNION All
SELECT 'det10002'  , '2013-01-06', '2013-01-06' ,'no' ,  'lvl10001' UNION All
SELECT 'det10003'  , '2013-01-07', '2013-01-07' ,'yes',  'lvl10001' UNION All
SELECT 'det10004'  , '2013-01-08', '2013-01-08' ,'no' ,  'lvl10001'  

Sql脚本

;WITH CTE
AS
(
SELECT  Req_Detail_Id,
        [start_date],
        CASE WHEN canceled = 'NO' THEN LEAD(end_date,1)OVER(ORDER BY Req_Detail_Id)
        ELSE end_date END end_date 
FROM
(
    SELECT d.Req_Detail_Id,
            d.[start_date],
            d.end_date,
            d.canceled
    FROM @LeaveRequest l
    INNER JOIN @LeaveRequestDetail d
        ON d.Leave_Req_Id=l.Leave_Req_Id
    WHERE d.[start_date]>=l.[start_date] 
        AND d.end_date <=l.end_date
        AND d.canceled <>'Yes'
)dt
)SELECT REPLACE(Req_Detail_Id,'det','rep') AS Report_Id ,
        [start_date],
        end_date
FROM CTE
WHERE end_date IS NOT NULL

演示结果http://rextester.com/XVVG52381

Report_Id   start_date  end_date
----------------------------------
rep10001    2013-01-05  2013-01-06
rep10002    2013-01-06  2013-01-08

答案 2 :(得分:0)

这是一个差距和群岛问题。

测试数据 - 注意在正面和背面取消1001和1003

CREATE TABLE LeaveRequest
    ([request_no] int, [leave_code] varchar(2), [emp_id] varchar(4), [leave_startdate] datetime, [leave_enddate] datetime)
; 
INSERT INTO LeaveRequest
    ([request_no], [leave_code], [emp_id], [leave_startdate], [leave_enddate])
VALUES
    (1001, 'AL', 'emp1', '2018-01-28 00:00:00', '2018-01-30 00:00:00'),
    (1002, 'AL', 'emp1', '2018-02-01 00:00:00', '2018-02-02 00:00:00'),
    (1003, 'AL', 'emp1', '2018-02-12 00:00:00', '2018-02-14 00:00:00')
;

CREATE TABLE LeaveRequestDetail
    ([request_no] int, [leave_starttime] datetime, [leave_endtime] datetime, [cancelsts] varchar(1))
;

INSERT INTO LeaveRequestDetail
    ([request_no], [leave_starttime], [leave_endtime], [cancelsts])
VALUES
    (1001, '2018-01-28 08:00:00', '2018-01-28 17:00:00', 'Y'),
    (1001, '2018-01-29 08:00:00', '2018-01-29 17:00:00', 'N'),
    (1001, '2018-01-30 08:00:00', '2018-01-30 17:00:00', 'N'),
    (1002, '2018-02-01 08:00:00', '2018-02-01 17:00:00', 'N'),
    (1002, '2018-02-02 08:00:00', '2018-02-02 17:00:00', 'N'),
    (1003, '2018-02-12 08:00:00', '2018-02-12 17:00:00', 'N'),
    (1003, '2018-02-13 08:00:00', '2018-02-13 17:00:00', 'N'),
    (1003, '2018-02-14 08:00:00', '2018-02-14 17:00:00', 'Y')
;

我通过使用递增ID并将其与ROW_NUMBER窗口函数相结合来处理类似的情况 - 注意对ROW_NUMBER函数的更改

;WITH cteX 
AS(
    SELECT 
         Id = ROW_NUMBER()OVER(ORDER BY D.request_no, D.leave_starttime ASC)
        ,D.request_no
        ,a.leave_code
        ,a.emp_id
        ,D.leave_starttime
        ,D.leave_endtime
        ,D.cancelsts
    FROM LeaveRequest a
    left join LeaveRequestDetail D on a.request_no = d.request_no
    where a.emp_id = 'emp1' and Year(a.leave_startdate) = 2018
)
SELECT TOP 100 PERCENT
         Grp = Id - ROW_NUMBER()OVER( PARTITION BY X.request_no, X.cancelsts ORDER BY X.Id) 
        ,X.request_no
        ,X.leave_code
        ,X.leave_starttime
        ,X.leave_endtime
        ,X.cancelsts
FROM 
    cteX X
WHERE
    X.cancelsts = 'n'
ORDER BY
    X.request_no,X.emp_id

这给出了这个结果集,请注意Grp列

Grp request_no  leave_code  leave_starttime         leave_endtime           cancelsts
1   1001        AL          2018-01-29 08:00:00.000 2018-01-29 17:00:00.000 N
1   1001        AL          2018-01-30 08:00:00.000 2018-01-30 17:00:00.000 N
3   1002        AL          2018-02-01 08:00:00.000 2018-02-01 17:00:00.000 N
3   1002        AL          2018-02-02 08:00:00.000 2018-02-02 17:00:00.000 N
5   1003        AL          2018-02-12 08:00:00.000 2018-02-12 17:00:00.000 N
5   1003        AL          2018-02-13 08:00:00.000 2018-02-13 17:00:00.000 N

然后与CTE或派生表结合,您可以获得预期的输出

;WITH cteX 
AS(
    SELECT 
         Id = ROW_NUMBER()OVER(ORDER BY D.request_no, D.leave_starttime ASC)
        ,a.leave_code
        ,a.emp_id
        ,D.request_no
        ,D.leave_starttime
        ,D.leave_endtime
        ,D.cancelsts
    FROM LeaveRequest a
    left join LeaveRequestDetail D on a.request_no = d.request_no
    where a.emp_id = 'emp1' and Year(a.leave_startdate) = 2018
)
SELECT 
     ReportId = 'rep1000' +  CAST(ROW_NUMBER()OVER(ORDER BY (SELECT NULL) ) AS NVARCHAR(5))
    ,leave_starttime = MIN(Y.leave_starttime)
    ,leave_endtime = MAX(Y.leave_endtime)
    ,leave_code = Y.leave_code
    ,Count(Y.leave_starttime) as TOTAL_COUNT
FROM
(
    SELECT TOP 100 PERCENT
          Grp = Id - ROW_NUMBER()OVER( PARTITION BY X.request_no, X.cancelsts ORDER BY X.Id) 
         ,X.request_no
         ,X.leave_code
         ,X.leave_starttime
         ,X.leave_endtime
         ,X.cancelsts
    FROM 
        cteX X
    WHERE
        X.cancelsts = 'n'
    ORDER BY
        X.request_no,X.emp_id
) Y
GROUP BY
    Y.Grp,Y.leave_code

输出

ReportId    leave_starttime         leave_endtime           leave_code  TOTAL_COUNT
rep10001    2018-01-29 08:00:00.000 2018-01-30 17:00:00.000 AL          2
rep10002    2018-02-01 08:00:00.000 2018-02-02 17:00:00.000 AL          2
rep10003    2018-02-12 08:00:00.000 2018-02-13 17:00:00.000 AL          2

SQLFiddle

答案 3 :(得分:0)

CREATE table #result (Report_id VARCHAR(50), [start_date] DATE, [end_date] DATE)
DECLARE
    @st VARCHAR(20),
    @en DATE,
    @can VARCHAR(5),
    @id_re VARCHAR(20),
    @lenght INT ,
    @id VARCHAR(20)

DECLARE find_records CURSOR FOR 
SELECT 
    start_date,
    end_date ,
    canceled,
    Leave_Req_Id
FROM [Leave Request Detail]
OPEN find_records
FETCH NEXT FROM find_records INTO @st, @en, @can, @id_re
WHILE @@fetch_status <> -1
BEGIN

SET @lenght = (LEN((SELECT MAX(Report_id) FROM #result))-3)
SET @id = RIGHT((SELECT MAX(Report_id) FROM #result), @lenght)

        IF @can ='yes'
        BEGIN       
            INSERT INTO #result
            (
               Report_id, 
               start_date, 
               end_date
             )
             VALUES
             (
                CASE WHEN @lenght IS NULL THEN 'rep10001'
                     ELSE 'rep'+ CAST((CAST(@id AS INT) + 1) AS VARCHAR(10)) END, 
                (SELECT MIN(start_date) FROM [Leave Request Detail] WHERE start_date <=@st and canceled='no' and Leave_Req_Id=@id_re), 
                @en
            )
        END

        FETCH NEXT FROM find_records INTO @st, @en, @can, @id_re
    END
    CLOSE find_records
    DEALLOCATE find_records

    DECLARE missing_resords CURSOR FOR 
    SELECT 
        start_date,
        end_date ,
        canceled,
        Leave_Req_Id
    FROM [Leave Request Detail]
    WHERE canceled != 'yes'
    OPEN missing_resords
    FETCH NEXT FROM missing_resords INTO @st, @en, @can, @id_re
    WHILE @@fetch_status <> -1
    BEGIN

        SET @lenght = (LEN((SELECT MAX(Report_id) FROM #result))-3)

        IF (@st> (SELECT MIN(end_date) from #result))
        BEGIN
            INSERT INTO #result
            (
                Report_id, 
                start_date, 
                end_date
             )
             VALUES
             (
                CASE WHEN @lenght IS NULL THEN 'rep10001'
                     ELSE 'rep'+ CAST((CAST(@id AS INT) + 1) AS VARCHAR(10)) END, 
                @st, 
                @en
              )
        END

            FETCH NEXT FROM missing_resords INTO @st, @en, @can, @id_re
        END
        CLOSE missing_resords
        DEALLOCATE missing_resords

    select * from #result

演示: http://rextester.com/KJUB88564 1