SQL查询将特定行转换为列

时间:2019-10-08 07:29:37

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

假设数据

(def results (jdbc/plan @datasource2 ["select * from cabs"]))

(into [] (map :name) results) => ["a", "b"]

(into [] (map identity) results) => gives an error - [..... missing `map` or `reduce` {row}]

转换为几段时间,这是从最小登机到最大登机的时间

我使用迭代来获得期望的结果,但想要优化查询(设置基准或CTE)以提高性能,

这就是我想要的

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

3 个答案:

答案 0 :(得分:2)

这是一个基于空缺的问题。

首先,可以使用以下查询生成连续记录组:

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

Demo on DB Fiddle

答案 1 :(得分:0)

您可以使用CTE-

尝试以下选项

您可以检查DEMO HERE

Warning message:
In system(cmd, wait = FALSE) : system call failed: Cannot allocate memory

答案 2 :(得分:0)

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';

Here是db <>小提琴。

之所以提供此功能,是因为仅使用lag() / lead()而不使用任何聚合或join可能会比其他方法具有更好的性能。