SQL按类型和ID分组

时间:2016-09-14 13:53:00

标签: sql database

我有一个发布设备的表记录日期,如:

CREATE TABLE myTable
    (`device_id` int, `datetime` datetime, `action` varchar(3))
;

INSERT INTO myTable
    (`device_id`, `datetime`, `action`)
VALUES
    (1, '2015-08-01 11:00', 'out'),
    (1, '2015-08-01 11:05', 'out'),
    (1, '2015-08-01 11:10', 'out'),
    (1, '2015-08-01 11:15', 'out'),
    (1, '2015-08-01 11:20', 'out'),
    (2, '2015-08-01 11:25', 'out'),
    (3, '2015-08-01 11:22', 'out'),
    (4, '2015-08-01 11:45', 'out'),
    (1, '2015-08-01 12:00', 'in'),
    (1, '2015-08-01 12:01', 'in'),
    (1, '2015-08-01 12:02', 'in'),
    (1, '2015-08-01 12:03', 'in'),
    (1, '2015-08-01 12:04', 'in'),
    (1, '2015-08-01 12:05', 'in'),
    (1, '2015-08-01 12:10', 'out'),
    (2, '2015-08-01 12:12', 'in'),
    (3, '2015-08-01 12:12', 'in'),
    (3, '2015-08-01 12:22', 'out'),
    (4, '2015-08-01 12:23', 'in'),
    (1, '2015-08-01 08:00', 'in'),
    (3, '2015-08-01 09:12', 'in')
;

请参阅sqlfiddle

上的示例

如何分组" out"和" in"设备到一排?像:

  datetime_out   |   datetime_in    | device_id |
-----------------+------------------+-----------+
2015-08-01 11:00 | 2015-08-01 12:05 |    1      |
2015-08-01 11:25 | 2015-08-01 12:12 |    2      |
2015-08-01 11:22 | 2015-08-01 12:12 |    3      |
2015-08-01 11:45 | 2015-08-01 12:23 |    4      |
2015-08-01 12:10 | 2015-09-03 08:00 |    1      |
2015-08-01 12:22 | 2015-09-03 09:12 |    3      |

UPD:

任何例子,DBMS都不重要。

如果我有多个" out"或" in"依次具有相同device_id的行然后选择具有最大日期时间的行"在"和#34; out"的最小日期时间,例如:

   datetime      | device_id | action |
-----------------+-----------+--------+
2015-08-01 11:00 |     1     |  out   | <- Out 1 device
2015-08-01 11:05 |     1     |  out   | <- ignore
2015-08-01 11:10 |     1     |  out   | <- ignore
2015-08-01 11:15 |     1     |  out   | <- ignore
2015-08-01 11:20 |     1     |  out   | <- ignore
2015-08-01 11:25 |     2     |  out   |
2015-08-01 11:22 |     3     |  out   |
2015-08-01 11:45 |     4     |  out   |
2015-08-01 12:00 |     1     |  in    | <- ignore
2015-08-01 12:01 |     1     |  in    | <- ignore
2015-08-01 12:02 |     1     |  in    | <- ignore
2015-08-01 12:03 |     1     |  in    | <- ignore
2015-08-01 12:04 |     1     |  in    | <- ignore
2015-08-01 12:05 |     1     |  in    | <- In 1 device
2015-08-01 12:10 |     1     |  out   | <- Out 1 device
2015-08-01 12:12 |     2     |  in    |
2015-08-01 12:12 |     3     |  in    |
2015-08-01 12:22 |     3     |  out   |
2015-08-01 12:23 |     4     |  in    |
2015-08-03 08:00 |     1     |  in    | <- In 1 device
2015-08-03 09:12 |     3     |  in    |

1 个答案:

答案 0 :(得分:1)

我会使用游标来实现这一目标。如果你没有太多行并且不需要即时结果,那就没问题了。如果不是,我会定期填写表格,或者保留一个&#34;最后更新日期&#34;某处只使用新信息更新表格。

请参阅此处的示例:http://rextester.com/JRW52552

以下是它的样子:

CREATE TABLE #Results (
    device_id INT,
    datetime_out DATETIME,
    datetime_in DATETIME
)

DECLARE device_cursor CURSOR FOR   
    SELECT DISTINCT device_id  
    FROM myTable

DECLARE @device_id INT
OPEN device_cursor  

FETCH NEXT FROM device_cursor INTO @device_id
WHILE @@FETCH_STATUS = 0
BEGIN

    DECLARE @DeviceLastInDatetime DATETIME = '1901-01-01'

    WHILE 1=1 --infinit loop to go through all out/in combinations
    BEGIN

        DECLARE @FirstOut DATETIME --find first out time after @DeviceLastInDatetime
        SELECT @FirstOut = MIN(datetime) 
        FROM myTable 
        WHERE device_id = @device_id AND action = 'out' AND datetime > @DeviceLastInDatetime
        PRINT '@FirstOut ' + CONVERT(VARCHAR, @FirstOut) --DEBUG

        IF @FirstOut IS NULL --if no further out found, break infinit loop
        BEGIN
            BREAK
        END

        DECLARE @NextIn DATETIME --Find @NextIn --to go pass all next out datetime
        SELECT @NextIn = MIN(datetime) 
        FROM myTable 
        WHERE device_id = @device_id AND action = 'in' AND datetime > @FirstOut
        PRINT '@NextIn ' + CONVERT(VARCHAR, @NextIn) --DEBUG

        DECLARE @NextOutAfterNextIn DATETIME --Find @NextOutAfterNextIn --to go pass all next in datetime
        SELECT @NextOutAfterNextIn = ISNULL(MIN(datetime) , '2099-01-01')
        FROM myTable 
        WHERE device_id = @device_id AND action = 'out' AND datetime > @NextIn
        PRINT '@NextOutAfterNextIn ' + CONVERT(VARCHAR, @NextOutAfterNextIn) --DEBUG

        DECLARE @FinalInDate DATETIME --last first 'in' before @NextOutAfterNextIn
        SELECT @FinalInDate = MAX(datetime) 
        FROM myTable 
        WHERE device_id = @device_id AND action = 'in' AND datetime < @NextOutAfterNextIn
        PRINT '@FinalInDate ' + CONVERT(VARCHAR, @FinalInDate) --DEBUG

        INSERT INTO #Results VALUES (@device_id, @FirstOut, @FinalInDate)

        SET @DeviceLastInDatetime = ISNULL(@FinalInDate, '2099-01-01')
    END 


    --fetch next device
    FETCH NEXT FROM device_cursor INTO @device_id
END --device_cursor


CLOSE device_cursor;  
DEALLOCATE device_cursor;

SELECT * 
FROM #Results
ORDER BY device_id, datetime_out

DROP TABLE #Results