【问题标题】:CTE Recursively join tableCTE 递归连接表
【发布时间】:2020-01-08 12:12:59
【问题描述】:

我有一个 id 表,每次该 id 有更新时,都会使用状态代码和时间戳进行更新,例如:

ID       STATUS     TIMESTAMP
------------------------------------
12345    10         2020-08-01 11:00:01
12345    20         2020-08-01 11:01:24
12345    30         2020-08-01 11:07:42

我希望将所有状态更改和时间放在一行中,所以我最终得到:

ID       STATUS     TIMESTAMP           STATUS     TIMESTAMP           STATUS     TIMESTAMP
-----------------------------------------------------------------------------------------------------
12345    10         2020-08-01 11:00:01 20         2020-08-01 11:01:24 30         2020-08-01 11:07:42

我可以通过对每个状态进行连接来做到这一点,所以我的查询如下所示:

with T1 as
(SELECT distinct
id as 'ID',
Status as 'STATUS',
Timestamp as 'Time10'
from StatusHistory S
where TimeStamp > '2020-01-07 11:00'
and Timestamp < '2020-01-07 11:10'
and Status = '10'),

T2 as
(SELECT distinct
id as 'ID',
Status as 'STATUS',
Timestamp as 'Time20'
from StatusHistory S
where TimeStamp > '2020-01-07 11:00'
and Timestamp < '2020-01-07 11:10'
and Status = '20')

Select * from T1
join T2 on T1.ID = T2.ID

这可行,但我必须为每个状态都这样做,并且大约有 12 种不同的状态代码。我已经阅读了几个进行草书连接的示例,并且理解了这个概念,但实际上将它应用于我的查询时遇到了很多麻烦。本质上,我想说递归位是Status = Previous Status + 10但如何实现这一点很麻烦。

我不明白如何获取最新状态并将其添加 10。

【问题讨论】:

  • SQL 查询具有固定数量的列。你知道你的结果集中的那些列是什么吗?
  • 抱歉,我不确定我是否完全理解这个问题。我想要的结果中的列是 ID、Status 和 Timestamp,并且是 statushistory 表中的列。
  • “我想要的结果中的列是 ID、Status 和 Timestamp” 但是您的预期结果集中有 3 个 status 列,而不是 1。有 2 (或更多)数据集中具有相同名称的列通常不是一个好主意。许多应用程序不处理它(很好)。如果您的预期输出 正确的,为什么要使用这种非规范化格式?如果某人在同一天有 10 个状态,那么您是否期望 21 列?状态数量的上限是多少?考虑到我们的样本是 7 分钟内 3 个,这是否意味着您可能需要 400 多列?
  • 啊,好吧,我明白了,时间戳只是一列,所以 ID 在上午 11 点达到状态 10,然后表格在上午 1:02 获得一个新行,状态为 20,然后在 11 点达到状态 30: 04am 等,直到它达到“完成”状态。我希望它水平显示的原因是为了从一个状态到下一个状态进行一些时间差分析,即从状态 10 到 30 或 10 到 50 等需要多长时间。
  • 对所用时间的分析最好按行而不是按列。您可以使用LEAD/LAG 轻松引用下一个/上一个时间的值。

标签: sql sql-server common-table-expression recursive-query


【解决方案1】:

您可以使用条件聚合:

select id,
       max(case when seqnum = 1 then status end) as status_1,
       max(case when seqnum = 1 then timestamp end) as timestamp_1,
       max(case when seqnum = 2 then status end) as status_2,
       max(case when seqnum = 2 then timestamp end) as timestamp_2,
       max(case when seqnum = 3 then status end) as status_3,
       max(case when seqnum = 3 then timestamp end) as timestamp_3
from (select sh.*,
             row_number() over (partition by id order by timestamp) as seqnum
      from StatusHistory sh
      where TimeStamp > '2020-01-07 11:00' and
            Timestamp < '2020-01-07 11:10'
     ) sh
group by id

【讨论】:

  • 这实际上效果很好,而且返回结果的速度出奇地快,但更好的是我实际上理解它。
【解决方案2】:

Gordon Linoff 的解决方案对我来说非常有效,但后来我了解了 PIVOT 功能,经过反复试验,发现下面给出了我所追求的结果表:

SELECT * FROM
(
    SELECT 
       id,
       status, 
       timestamp 
    FROM StatusHistory
       where TimeStamp > '2020-01-07 11:00'
       and Timestamp < '2020-01-07 11:10'
) dat
PIVOT
(
       MAX(Timestamp) FOR Status IN ([10], [20], [30], [40], [50], [60], [70], [80], [90], [100], [170]) 
) pvt
order by 10

这给了我这样的结果:

ID      10                    20                    30
12345   2020-01-08 11:00:01   2020-01-08 11:01:24   2020-01-08 11:07:42

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-02-21
    • 1970-01-01
    • 2022-01-20
    • 2020-04-11
    • 2017-01-02
    • 1970-01-01
    • 1970-01-01
    • 2014-08-09
    相关资源
    最近更新 更多