【问题标题】:How to display multiple rows, as one row, without using a MAX aggregation如何在不使用 MAX 聚合的情况下将多行显示为一行
【发布时间】:2021-06-09 05:37:58
【问题描述】:

我有一个包含很多行的表,其中有几个与大图相关的标识符,但在我当前的任务中,只有 3 列是重要的:

SiteIdentifier | SysTm | Signalet

SiteIdentifier 是一个数字,SysTm 是 DateTime,Signalet 是一个文本字段。 防爆数据:

587451 | 2021-03-01 00:00:00 | Left
587451 | 2021-03-04 07:12:17 | Joined
214537 | 2021-03-05 02:13:03 | Left
587451 | 2021-03-04 12:12:12 | Left
214537 | 2021-03-05 07:13:00 | Joined
587451 | 2021-03-08 01:04:07 | Joined

这是我想要实现的目标: 我想创建一个可以显示的查询:

SiteIdentifier | SysTm of last Signalet Left | SysTm of last Signalet Joined | DATEDIFF(hour, ...  between last Left and Joined

在示例数据行的情况下,结果将是: 获取每个 SiteIdentifier 的每个实例很重要

587451 | 2021-03-01 00:00:00 | 2021-03-04 07:12:17 | 79
214537 | 2021-03-05 02:13:03 | 2021-03-05 07:13:00 | 5
587451 | 2021-03-04 12:12:12 | 2021-03-08 01:04:07 | 84

(每个 SiteIdentifier 可以表示 NUMEROUS 次)

这些行都包含在一个表中,这让我很头疼...

我之前问过这个问题,得到了这个查询:

SELECT SiteIdentifier,
       MAX(CASE WHEN Signalet = 'Left' THEN SysTM END) as left_tm,
       MAX(CASE WHEN Signalet = 'Joined' THEN SysTM END) as Joined_tm,
       DATAEDIFF(hour,
                 MAX(CASE WHEN Signalet = 'Left' THEN SysTM END),
                 MAX(CASE WHEN Signalet = 'Joined' THEN SysTM END)
                ) as time_diff
FROM Table
WHERE Signalet IN ( 'Left', 'Joined')
GROUP BY SiteIdentifier
ORDER BY SiteIdentifier

此查询为我提供了每个 SiteIdentifier 的最新“行”,但不是所有结果。 我试图让结果是,正如我上面写的,一个 SiteIdentifier 可以被多次表示。

【问题讨论】:

  • 我建议为您的样例数据提供DDL+DML,方便我们协助。

标签: sql sql-server tsql merge


【解决方案1】:

如果您在SiteIdentifier 中为Signalet“左”和“已加入”中的每一个添加一个行号,然后加入匹配的行,您将获得所需的结果。

注意:添加了一个笨重的第二个左连接来处理第一行不是“左”记录的情况。

declare @Test table (SiteIdentifier int, SysTm datetime2(0), Signalet varchar(21));

insert into @Test (SiteIdentifier, SysTm, Signalet)
values
(587451, '2021-03-01 00:00:00', 'Left'),
(587451, '2021-03-04 07:12:17', 'Joined'),
(214537, '2021-03-05 02:13:03', 'Left'),
(587451, '2021-03-04 12:12:12', 'Left'),
(214537, '2021-03-05 07:13:00', 'Joined'),
(587451, '2021-03-08 01:04:07', 'Joined');

with cte as (
    select *
      , row_number() over (partition by SiteIdentifier, Signalet order by SysTm) rn
    from @Test
)
select C1.SiteIdentifier, C1.SysTm, coalesce(C2.SysTm, C3.SysTm), datediff(hour, C1.SysTm, coalesce(C2.SysTm, C3.SysTm))
from cte C1
left join cte C2 on C2.SiteIdentifier = C1.SiteIdentifier and C2.Signalet = 'Joined' and C2.rn = C1.rn and C2.SysTm > C1.SysTm
left join cte C3 on C3.SiteIdentifier = C1.SiteIdentifier and C3.Signalet = 'Joined' and C3.rn = C1.rn + 1 and C3.SysTm > C1.SysTm and C2.rn is null
where C1.Signalet = 'Left'
order by C1.SysTm asc;

返回:

SiteIdentifier SysTm (Left) SysTm (Joined) Diff
587451 2021-03-01 00:00:00 2021-03-04 07:12:17 79
587451 2021-03-04 12:12:12 2021-03-08 01:04:07 85
214537 2021-03-05 02:13:03 2021-03-05 07:13:00 5

请注意,如果您像我对您的问题所做的那样添加 DDL+DML,您可以更轻松地为人们提供帮助。

【讨论】:

  • 非常感谢您的回答!我不知道添加DDL和DML,但是我现在知道了,以后一定会这样做!目前我无法在实际的实时数据库上对此进行测试,但我在小提琴上进行了测试,它看起来肯定会返回我想要实现的一切。一旦有机会进行现场测试,我一定会接受您的回答。
  • 我刚刚有机会在实时数据上对此进行测试,只要第一个 Signalet 是“Left”,它似乎就可以工作,但是当在第一个之前发现“Joined”时“左”,有一个问题......我在规范中没有想到这一点,但有时“加入”可能是为 SiteIdentifier 找到的第一个 Signallet。有没有办法解决/解决这个问题?
  • @Geir 请用另一个数据集和预期结果更新您的问题以进行说明。
  • @Geir 你过得怎么样?确实给出的答案解决了所提出的问题,您应该接受它。但我已经为你添加了一个可能的修复方法。
  • @[Dale K] 我现在正在处理一个新问题,使用 DDL,所以应该更容易看到我的发现。非常感谢您到目前为止的帮助! :)
猜你喜欢
  • 2012-01-26
  • 1970-01-01
  • 2022-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-03
相关资源
最近更新 更多