SQL迭代没有游标的行

Posted

技术标签:

【中文标题】SQL迭代没有游标的行【英文标题】:SQL Iterate over row without a cursor 【发布时间】:2020-10-14 10:59:40 【问题描述】:

我有一些读取传感器状态的数据。传感器每 15 分钟报告一次状态。这有点像......

Sensor ID, Status, TimeStamp, **WhatIWantSessionID**
1, Off, 0345, **0**
1, Charge, 0400, **1**
1, Charge, 0415, **1**
1, Off, 0430, **0**
1, Charge, 0445, **2**
1, Off, 0500, **0** 

"WhatIWantSessionID" ...我想在每次 Charge 会话开始时设置一个递增的 sessionID。保持该 sessionID 直到状态切换状态。以下链接非常棒,“几乎”可以满足我的需要,但并不完全(因为我没有在会话中开始/停止的标志)。非常感谢帮助。 Iterate over rows using SQL

【问题讨论】:

感谢 a_horse ... Lag(),Lead() 看起来不错。 【参考方案1】:

必须有更好的方法来做到这一点,但下面的例子有效(丑陋,我相信有人会发布一些聪明而聪明的东西,但我今天还没有足够的咖啡来做聪明,并且有效)。

create or replace temporary table stacko_64351844 (
    id number,
    status varchar(20),
    ts     varchar(20),
    desired number);

INSERT INTO stacko_64351844 
VALUES 
(1, 'Off', '0345', 0),
(1, 'Charge', '0400', 1),
(1, 'Charge', '0415', 1),
(1, 'Off', '0430', 0),
(1, 'Charge', '0445', 2),
(1, 'Off', '0500', 0),
(1, 'Charge', '0515', 3),
(1, 'Charge', '0530', 3),
(1, 'Charge', '0545', 3),
(1, 'Charge', '0600', 3),
(1, 'Off', '0615', 0);

SELECT id,
       status, ts,
       desired,
       NVL(calc_desired, max(calc_desired) over (partition by id  order by id, ts rows between unbounded preceding and current row)) as calc_desired
FROM (
SELECT id, 
       status, 
       ts, 
       desired,
       tmp_desired,
       CASE
         WHEN status = 'Off' THEN 0
         WHEN (status != 'Charge' AND status = lag(status, 1, null)
              over (partition by id order by id, ts, status DESC) )
               THEN null
         ELSE
           sum(tmp_desired) over (partition by id, tmp_desired order by id, ts rows between unbounded preceding and current row)
       end as calc_desired
FROM (
  SELECT id, 
         status, 
         ts, 
         desired, 
         CASE
         WHEN status = 'Off' THEN 0
         WHEN status = lag(status, 1, null) 
              over (partition by id order by id, ts, status DESC) 
               THEN null
         ELSE
           1
       end  as tmp_desired
  FROM   stacko_64351844)
ORDER BY id, ts, status DESC);

RESULTS
ID  STATUS  TS  DESIRED CALC_DESIRED
1   Off     0345    0   0
1   Charge  0400    1   1
1   Charge  0415    1   1
1   Off     0430    0   0
1   Charge  0445    2   2
1   Off     0500    0   0
1   Charge  0515    3   3
1   Charge  0530    3   3
1   Charge  0545    3   3
1   Charge  0600    3   3
1   Off     0615    0   0

我希望这会有所帮助...丰富

附言如果这个(或另一个)答案对您有帮助,请花点时间“接受”有帮助的答案 通过单击答案旁边的复选标记将其从“灰色”切换为“已填充”。

【讨论】:

【参考方案2】:

MSSQL,但肯定/希望它能在 ansi 上运行。 感谢 Rich Murnane 提供数据提示

create TABLE  #stacko_64351844 (
    id int,
    status varchar(20),
    ts     varchar(20),
    desired int);

INSERT INTO #stacko_64351844 
VALUES 
(1, 'Off', '0345', 0),
(1, 'Charge', '0400', 1),
(1, 'Charge', '0415', 1),
(1, 'Off', '0430', 0),
(1, 'Charge', '0445', 2),
(1, 'Off', '0500', 0),
(1, 'Charge', '0515', 3),
(1, 'Charge', '0530', 3),
(1, 'Charge', '0545', 3),
(1, 'Charge', '0600', 3),
(1, 'Off', '0615', 0);

SELECT  CASE status WHEN 'off' THEN 0
 else SUM(CASE WHEN lg='off' THEN 1 ELSE 0 END) OVER(PARTITION BY id ORDER BY ts) END DesiredCalc       
,t.id,t.status,t.ts,t.desired FROM  (
    SELECT LAG(status)OVER(PARTITION BY id ORDER BY ts)lg,* FROM #stacko_64351844 
)t
ORDER BY ts

结果:

DesiredCalc id  status  ts  desired
0   1   Off 0345    0
1   1   Charge  0400    1
1   1   Charge  0415    1
0   1   Off 0430    0
2   1   Charge  0445    2
0   1   Off 0500    0
3   1   Charge  0515    3
3   1   Charge  0530    3
3   1   Charge  0545    3
3   1   Charge  0600    3
0   1   Off 0615    0

【讨论】:

我在 CTE 上的一个分区中使用了 Lag 和 Lead。这是上述优秀解决方案的组合,加上马的重新标记。我会在我回来后发布我用过的东西!我在这里的第一篇文章,看到我不能说“谢谢”,所以...内容 = 1。

以上是关于SQL迭代没有游标的行的主要内容,如果未能解决你的问题,请参考以下文章

使用游标时从游标中删除一行

Oracle SQL 中的帕累托分析,无需迭代过程和有效值的硬编码

如何将行附加到现有的 SYS_REFCURSOR?

选择价格没有变化的行

抓取两个日期时间之间的行并避免迭代

cursor游标(mysql)