简体   繁体   中英

select a chain of records using mssql

I have a table with records in TimeLines, I need to get rows that form a chain of 45 minutes set.

1|2016-01-01 00:00
2|2016-01-01 00:30
3|2016-01-01 00:45
4|2016-01-01 01:00

How I can find 2nd row depending from it time, cause 2nd, 3rd and 4th rows are indissoluble 15 minutes chain of timeline for 45 min set? 1st and 2nd is not okay, cause interval between timelines is 30 min.

2nd, 3rd and 4th rows are consistent chain of timeline. 2nd row plus 15 min - okay. cause existed 3rd row with that time. 3rd row plus 15 min - okay. cause existed 4th row with that time. as result i have 45 min consistent timeline chain.

1row plus 15 min - not okay. cause 00:15 time with date not existed.

Try this

    DECLARE @Tbl TABLE (Id INT, StartDate DATETIME)
INSERT INTO @Tbl
VALUES 
(1,'2016-01-01 00:00'),
(2,'2016-01-01 00:30'),
(3,'2016-01-01 00:45'),
(4,'2016-01-01 01:00')


;WITH CTE
AS
(
    SELECT
        Id ,
        StartDate,
        ROW_NUMBER() OVER (ORDER BY Id) AS RowId
    FROM
    @Tbl
)


SELECT
    CurRow.*,   
    CASE 
        WHEN 
            DATEDIFF(MINUTE, CurRow.StartDate, NextRow.StartDate ) = 15  OR
            DATEDIFF(MINUTE, PrevRow.StartDate, CurRow.StartDate ) = 15  
            THEN '15 MIN'
        ELSE 'NO' END Flag
FROM
    CTE CurRow LEFT JOIN 
    (SELECT *, C.RowId - 1 AS TmpRowId  FROM CTE C) NextRow ON CurRow.RowId = NextRow.TmpRowId LEFT JOIN 
    (SELECT *, C.RowId + 1 AS TmpRowId  FROM CTE C) PrevRow ON CurRow.RowId = PrevRow.TmpRowId

OUTPUT:

Id  StartDate                   RowId   Flag
1   2016-01-01 00:00:00.000     1       NO
2   2016-01-01 00:30:00.000     2       15 MIN
3   2016-01-01 00:45:00.000     3       15 MIN
4   2016-01-01 01:00:00.000     4       15 MIN

If I understand you correctly, you can use LEAD/LAG:

WITH Src AS
(
    SELECT * FROM (VALUES 
    (1,'2016-01-01 00:00'),
    (2,'2016-01-01 00:30'),
    (3,'2016-01-01 00:45'),
    (4,'2016-01-01 01:00')) T(ID, [Date])
)
SELECT *, CASE WHEN LEAD([Date]) OVER (ORDER BY ID)=DATEADD(MINUTE, 15, [Date])
                 OR LAG([Date]) OVER (ORDER BY ID)=DATEADD(MINUTE, -15, [Date])
              THEN 'Chained' END [Status]
FROM Src

It produces:

ID  Date                Status
--  ----                ------
1   2016-01-01 00:00    NULL
2   2016-01-01 00:30    Chained
3   2016-01-01 00:45    Chained
4   2016-01-01 01:00    Chained

You can do this with OUTER APPLY and tricky ROW_NUMBER():

;WITH TimeLines AS (  --This CTE is similar to your table
SELECT *
FROM (VALUES
(1, '2016-01-01 00:00'),(2, '2016-01-01 00:30'),
(3, '2016-01-01 00:45'),(4, '2016-01-01 01:00'),
(5, '2016-01-01 01:05'),(6, '2016-01-01 01:07'),
(7, '2016-01-01 01:15'),(8, '2016-01-01 01:30'),
(9, '2016-01-01 01:45'),(10, '2016-01-01 02:00')
) as t(id, datum)
)
, cte AS (
SELECT  t.id,
        t.datum,
        CASE WHEN ISNULL(DATEDIFF(MINUTE,t1.datum,t.datum),0) != 15 THEN DATEDIFF(MINUTE,t.datum,t2.datum) ELSE 15 END as i
FROM TimeLines t  --in this cte with the help of
OUTER APPLY (     --OUTER APPLY we are getting next and previous dates to compare them
    SELECT TOP 1 * 
    FROM TimeLines 
    WHERE t.datum > datum 
    ORDER BY datum desc) t1
OUTER APPLY (
    SELECT TOP 1 * 
    FROM TimeLines 
    WHERE t.datum < datum 
    ORDER BY datum asc) t2
)

SELECT  *,  --this is final select to get rows you need with chaines
        (ROW_NUMBER() OVER (ORDER BY (SELECT 1))+2)/3 as seq
FROM cte
WHERE i = 15

Output:

id  datum               i   seq
2   2016-01-01 00:30    15  1
3   2016-01-01 00:45    15  1
4   2016-01-01 01:00    15  1
7   2016-01-01 01:15    15  2
8   2016-01-01 01:30    15  2
9   2016-01-01 01:45    15  2
10  2016-01-01 02:00    15  3

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM