【问题标题】:Conditionally increment previous value and propagate forward using Amazon Redshift (SQL)使用 Amazon Redshift (SQL) 有条件地递增先前的值并向前传播
【发布时间】:2018-02-08 11:54:38
【问题描述】:

使用 Amazon Redshift (SQL),我有一个时间戳表,当条目之间的时间超过某个阈值时,我想将其拆分为单独的阶段。

例如,为此输入使用 60 个单位的阈值:

  id  ts
1  a   1
2  a   4
3  a  12
4  a  90
5  a  94
6  a 101
7  a 404
8  a 412
9  a 413

我想退货:

  id  ts  dt phase
1  a   1  NA     1
2  a   4   3     1
3  a  12   8     1
4  a  90  78     2
5  a  94   4     2
6  a 101   7     2
7  a 404 303     3
8  a 412   8     3
9  a 413   1     3

这在 R(我最熟悉的)中很简单,使用简单的 for 循环和 ifelse 如果 dt > 60,则将先前的 phase 值增加 1:

# sample data
df <- data.frame(id = rep("a", 9),
                 ts = c(1, 4, 12, 90, 94, 101, 404, 412, 413)) %>%
  mutate(dt = c(NA, diff(ts)))

# add default minimum phase value, 1
df$phase<- 1
# for loop
for(i in 2:nrow(df)) {
  df$phase[i] <- ifelse(df$dt[i] > 60, df$phase[i-1] + 1, df$phase[i-1])
}

但是,我尝试在 SQL 中使用 lag 函数和 case / when 并没有成功。

-- sample data
CREATE TABLE sampledata (
  conversationid varchar(10), ts integer
);

INSERT INTO sampledata (conversationid, ts)
VALUES
  ('a', 1),
  ('a', 4),
  ('a', 12),
  ('a', 90),
  ('a', 94),
  ('a', 101),
  ('a', 404),
  ('a', 412),
  ('a', 413);

-- query
SELECT *,
  CASE
    WHEN dt > 60 THEN LAG(period) OVER (PARTITION BY conversationid ORDER BY ts) + 1
    ELSE LAG(period) OVER (PARTITION BY conversationid ORDER BY ts)
  END AS period
FROM (
  SELECT *,
    ts - LAG(ts) OVER (PARTITION BY conversationid ORDER BY ts) AS dt,
    1 AS period
  FROM sampledata
)
ORDER BY ts
;

-- output
id ts   dt  period period
a  1        1   
a  4    3   1      1
a  12   8   1      1
a  90   78  1      2
a  94   4   1      1
a  101  7   1      1
a  404  303 1      2
a  412  8   1      1
a  413  1   1      1

我能够在dt > 60 的行上增加相位值,但不能在后续行中传播增加的phase 值。

我猜这可能与 lag 函数同时在所有行上运行而不是逐行和/或无法即时更新原始 phase 值有关(第二列而是创建了phase)。

【问题讨论】:

    标签: sql for-loop amazon-redshift


    【解决方案1】:

    你很接近。您想要基于滞后差异的累积总和:

    SELECT sd.*,
           SUM(CASE WHEN diff > 60 THEN 1 ELSE 0 END) OVER (PARTITION BY conversationid ORDER BY ts) as period
    FROM (SELECT sd.*,
                 (ts - LAG(ts) OVER (PARTITION BY conversationid ORDER BY ts ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) ) AS diff
          FROM sampledata sd
         ) sd
    ORDER BY ts;
    

    作为旁注,我希望您使用ORDER BY conversationid, ts,而不仅仅是时间。

    最后,上面将在NULL 开始句点(它应该正确识别它们,只是笨拙地命名它们)。以下调整会根据您的具体要求进行枚举:

    SELECT sd.*,
           (1 + SUM(CASE WHEN diff < 60 THEN 0 ELSE 1 END) OVER (PARTITION BY conversationid ORDER BY ts ROW BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)) as period
    

    【讨论】:

    • 感谢您的回答 Gordon,看起来很有希望,但对我来说,上面的代码返回错误:Invalid operation: Aggregate window functions with an ORDER BY clause require a frame clause。我已经尝试在最外层查询(ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWINGBETWEEN 1 PRECEDING AND CURRENT ROW)中显式地向窗口函数添加一个框架子句,但在这两种情况下都不需要此输出。
    • @jogall 。 . .不考虑不遵守标准。我输入了正确的逻辑。
    • 啊,好吧,我以为你的答案是特定于 Redshift 的——我仍在尝试辨别我可以在他们的 SQL 中做什么和不能做什么!
    • @jogall 。 . .实际上,Redshift 没问题。它只是基于 Postgres 的一个古老版本。自那时以来,Amazon 所做的改进并没有像 Postgres 那样关注标准。
    猜你喜欢
    • 2017-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-07
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多