sql - 如何在某个值差距之前识别每组的行?

标签 sql postgresql sql-update window-functions gaps-and-islands

我想根据 PostgreSQL 中相邻行之间另一列值的差异来更新表中的特定列。

这是一个测试设置:

CREATE TABLE test(
   main INTEGER,
   sub_id INTEGER,
   value_t INTEGER);

INSERT INTO test (main, sub_id, value_t)
VALUES
    (1,1,8),
    (1,2,7),
    (1,3,3),
    (1,4,85),
    (1,5,40),
    (2,1,3),
    (2,2,1),
    (2,3,1),
    (2,4,8),
    (2,5,41);

我的目标是确定从 sub_id 1 开始的每个组 maindiff 中的哪个值超过特定阈值(例如 <10 或>-10) 通过按 sub_id 按升序检查。在达到阈值之前,我想通过用值填充列 newval 来标记每个通过的行AND条件为FALSE的一行例如1

enter image description here

我应该使用循环还是有更智能的解决方案?

伪代码中的任务描述:

FOR i in GROUP [PARTITION BY main ORDER BY sub_id]:
    DO until diff > 10 OR diff <-10
        SET newval = 1 AND LEAD(newval) = 1

最佳答案

基本 SELECT

尽快:

SELECT *, bool_and(diff BETWEEN -10 AND 10) OVER (PARTITION BY main ORDER BY sub_id) AS flag
FROM (
   SELECT *, value_t - lag(value_t, 1, value_t) OVER (PARTITION BY main ORDER BY sub_id) AS diff
   FROM   test
   ) sub;
细点
  • 你的思维模型围绕窗口函数发展 lead() 。但其对应的 lag() 出于此目的,效率更高一些,因为在包含大间隙之前的行时不会出现差一错误。 或者,使用 lead()具有反向排序顺序 ( ORDER BY sub_id DESC )。

  • 避免NULL对于分区中的第一行,提供 value_t默认为第三个参数,这使得 diff 0而不是 NULL。两者lead()lag()有这个能力。

  • diff BETWEEN -10 AND 10@diff < 11 稍快(也更清晰、更灵活)。 ( @ being the "absolute value" operator ,相当于 abs() function 。)

  • bool_or() or bool_and() 在外部窗口函数中,将所有行标记为大间隙可能是最便宜的。

您的UPDATE

Until the threshold is reached I would like to flag every passed row AND the one row where the condition is FALSE by filling column newval with a value e.g. 1.

再次强调,尽快。

UPDATE test AS t
SET    newval = 1
FROM  (
   SELECT main, sub_id
        , bool_and(diff BETWEEN -10 AND 10) OVER (PARTITION BY main ORDER BY sub_id) AS flag
   FROM (
      SELECT main, sub_id
           , value_t - lag(value_t, 1, value_t) OVER (PARTITION BY main ORDER BY sub_id) AS diff
      FROM   test
      ) sub
   ) u
WHERE (t.main, t.sub_id) = (u.main, u.sub_id)
AND    u.flag;
细点
  • 计算单个查询中的所有值通常比相关子查询快得多。

  • 添加的WHERE条件AND u.flag确保我们只更新实际需要更新的行。
    如果某些行可能已经在 newval 中具有正确的值,添加另一个子句以避免那些空更新:AND t.newval IS DISTINCT FROM 1 请参阅:

  • SET newval = 1分配一个常量(即使我们可以在这种情况下使用实际计算的值),这会便宜一些。

db<> fiddle here

关于sql - 如何在某个值差距之前识别每组的行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64810641/

相关文章:

sql - 在 SQL Server 2008 中插入/更新大量数据的最佳实践

mysql - 如何使用嵌套查询更新行

sql - 查询语句在 oracle 上执行时间很长

sql - 如何将一行插入到列中具有默认值的表中?

像 PEPSI 这样的 SQL SERVER PIVOT 按年划分,然后按月份划分等。(请参阅附图以获得清晰的想法)

database - 关于连接到 Postgres 数据库的首选方式的问题

sql - 树结构查询中逻辑或的完全外部连接的替代方法

java - 使用 JDBC 选择 "for update"?

mysql - 使用来自另一个表的另一个字段的值更新表中的字段

sql - 从一列定义的组中选择最小值/最大值作为另一列的子组 - SQL、HPVertica