SQL-запрос для преобразования отдельных строк в столбец - PullRequest
0 голосов
/ 08 октября 2019

Предположим, что данные

ID          Date                    Mode
1           2019-09-20 09:28      IN
2           2019-09-20 19:00      IN
3           2019-09-20 19:00      IN
4           2019-09-20 19:00      IN
5           2019-09-20 19:01      IN
6           2019-09-20 19:01      IN
7           2019-09-20 19:01      Out
8           2019-09-20 20:28      IN
9           2019-09-20 20:35      IN
10          2019-09-20 20:50      Out
11          2019-09-20 20:55      Out
12          2019-09-20 21:30      IN

конвертируются в строки периода, который является минимальной регистрацией для максимальной регистрации

Я использовал итерацию для достижения желаемого результата, но хочу оптимизировать запрос (установить базу или CTE) для увеличения производительности,

Это то, что я хочу

ID      DateIN              ID          DateOut
01      2019-09-20 09:28    07          2019-09-20 19:01
08      2019-09-20 20:28    11          2019-09-20 20:55

Ответы [ 3 ]

2 голосов
/ 08 октября 2019

Это проблема с пробелами и островами.

Для начала вы можете сгенерировать группы последовательных записей с помощью следующего запроса:

SELECT [Mode], MIN(id) min_id, MAX(id) max_id, MIN([Date]) min_date, MAX([Date]) max_date
FROM (
    SELECT
        id,
        [Date],
        [Mode],
        ROW_NUMBER() OVER(ORDER BY [Date]) rn1,
        ROW_NUMBER() OVER(PARTITION BY [Mode] ORDER BY [Date]) rn2
    FROM mytable
) x
GROUP BY [Mode], (rn1 - rn2)

Это дает:

Mode | min_id | max_id | min_date         | max_date        
:--- | -----: | -----: | :--------------- | :---------------
IN   |      1 |      6 | 2019-09-20 09:28 | 2019-09-20 19:01
Out  |      7 |      7 | 2019-09-20 19:01 | 2019-09-20 19:01
IN   |      8 |      9 | 2019-09-20 20:28 | 2019-09-20 20:35
Out  |     10 |     11 | 2019-09-20 20:50 | 2019-09-20 20:55
IN   |     12 |     12 | 2019-09-20 21:30 | 2019-09-20 21:30

Затем вы можете превратить этот запрос в cte и самостоятельно присоединиться к нему для генерации ожидаемого набора результатов:

WITH cte AS (
    SELECT [Mode], MIN(id) min_id, MAX(id) max_id, MIN([Date]) min_date, MAX([Date]) max_date
    FROM (
        SELECT
            id,
            [Date],
            [Mode],
            ROW_NUMBER() OVER(ORDER BY [Date]) rn1,
            ROW_NUMBER() OVER(PARTITION BY [Mode] ORDER BY [Date]) rn2
        FROM mytable
    ) x
    GROUP BY [Mode], (rn1 - rn2)
)
SELECT c1.min_id IdIn, c1.min_date DateIN, c2.max_id IdOut, c2.max_date DateOut
FROM cte c1
INNER JOIN cte c2 
    ON  c1.mode = 'IN'
    AND c2.mode = 'Out'
    AND c2.min_id = c1.max_id + 1

Вывод:

IdIn | DateIN           | IdOut | DateOut         
---: | :--------------- | ----: | :---------------
   1 | 2019-09-20 09:28 |     7 | 2019-09-20 19:01
   8 | 2019-09-20 20:28 |    11 | 2019-09-20 20:55

Демонстрация на DB Fiddle

0 голосов
/ 08 октября 2019

SQL Server 2008 больше не поддерживается. Все поддерживаемые версии SQL Server поддерживают lag() и lead().

. Вы можете легко использовать эти функции для этой цели. Есть простое наблюдение:

  • Вы хотите 'IN' запись, когда предыдущая запись 'OUT' (или не существует).
  • Вы хотите 'OUT'запись, когда следующая запись 'IN' (или не существует)

После того как вы отфильтровали эти записи, вы можете просто взять каждую запись 'OUT' и предыдущие значения 'IN' дляокончательный результат:

select prev_id, prev_dt as date_in, id, dt as date_out
from (select io.*,
             lag(dt) over (order by dt) as prev_dt,
             lag(id) over (order by dt) as prev_id
      from (select t.*,
                   lag(mode) over (order by dt) as prev_mode,
                   lead(mode) over (order by dt) as next_mode
            from t
           ) io
      where mode = 'IN' and (prev_mode is null or prev_mode = 'OUT') or
            mode = 'OUT' and (next_mode is null or next_mode = 'IN')
     ) io
where mode = 'OUT';

Здесь - это дБ <> скрипка.

Я предлагаю это, потому что просто использую lag() / lead() без агрегирования илиjoin s, вероятно, будет иметь лучшую производительность, чем альтернативы.

0 голосов
/ 08 октября 2019

Вы можете попробовать эту опцию ниже, используя CTE-

Вы можете проверить ДЕМО ЗДЕСЬ

WITH CTE AS(
    SELECT A.id,A.Date,A.Mode
    FROM your_table A
    LEFT JOIN your_table  B ON A.ID = B.ID - 1
    WHERE A.Mode <> B.Mode OR (A.Mode = 'In' AND B.Mode = 'In')
)

SELECT 
(
    SELECT MIN (ID) 
    FROM CTE
    WHERE ID > (SELECT ISNULL(MAX(ID),0) FROM CTE WHERE ID < A.ID AND mode = 'Out') 
    AND ID < A.ID
) [ID],
(
    SELECT MIN (Date) 
    FROM CTE 
    WHERE ID > (SELECT ISNULL(MAX(ID),0) FROM CTE WHERE ID < A.ID AND mode = 'Out') 
    AND ID < A.ID
) [In],
A.ID,A.Date [Out]
FROM CTE A
WHERE A.Mode = 'Out'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...