【问题标题】:UPDATE rows with values from the same table更新具有来自同一表的值的行
【发布时间】:2012-11-10 08:41:18
【问题描述】:

我有一张这样的桌子:

+------+-------+
|ID    | value |
+------+-------+
| 1    | 150   |
| 2    |       |
| 3    |       |
| 4    |       |
| 5    | 530   |
| 6    | 950   |
| 7    | 651   |
+-------+------+

我想复制最后 3 个值,最后我的表格将如下所示:

+------+-------+
|ID    | value |
+------+-------+
| 1    | 150   |
| 2    | 530   |
| 3    | 950   |
| 4    | 651   |
| 5    | 530   |
| 6    | 950   |
| 7    | 651   |
+-------+------+

有可能吗?

【问题讨论】:

    标签: sql postgresql sql-update self-join


    【解决方案1】:

    使用自加入

    UPDATE mytable m
    SET    value = m0.value
    FROM   mytable m0
    WHERE  m.id = (m0.id - 3)   -- define offset
    AND    m.id BETWEEN 2 AND 4 -- define range to be affected
    AND    m.value IS NULL;     -- make sure only NULL values are updated
    

    如果ID 空间中存在间隙,请使用Windows 函数row_number() 获取可使用的无间隙ID。我在 CTE 中这样做,因为我将重复使用该表两次以进行自联接:

    WITH x AS (
       SELECT *, row_number() OVER (ORDER BY ID) AS rn
       FROM   mytable
       )
    UPDATE mytable m
    SET    value = y.value
    FROM   x
    JOIN   x AS y ON x.rn = (y.rn - 4567)   -- JOIN CTE x AS y with an offset
    WHERE  x.id = m.id                      -- JOIN CTE x to original
    AND    m.id BETWEEN 1235 AND 3455
    AND    m.value IS NULL;
    

    data-modifying CTEs 需要 PostgreSQL 9.1 或更高版本。

    【讨论】:

    • 您不需要 PostgreSQL 9.1 或更高版本来运行 WITH x AS ( SELECT...) UPDATE mytable SET ... FROM x 。数据修改 CTE 实际上意味着 UPDATE INSERT DELETEWITH 部分。类似WITH x as (DELETE FROM t1 ... RETURNING t1.id) INSERT INTO t2 SELECT x.id FROM x
    • @IgorRomanchenko:你的说法不正确,我的回答是正确的。好吧,术语“数据修改 CTE”似乎有点误导,因为 CTE 本身不修改数据。手册中的关键语句是:The sub-statements in WITH are executed concurrently with each other and with the main query. 无论哪种方式,您都需要 PostgreSQL 9.1 才能使用与 CTE 直接连接的任何数据修改命令。考虑这个演示:sqlfiddle.com/#!11/53063/1。
    • 对不起。我在生产中同时使用 9.2 和 8.4 并且很困惑。 + 我已阅读手册。在“7.8.2. WITH 中的数据修改语句”中,WITH 部分总是有UPDATE INSERT DELETE,所以我认为这是关于 CTE 本身的数据修改(WITH 部分)。
    【解决方案2】:

    对于这样的临时更新,可能没有比三个简单的更新语句更好的方法了:

    UPDATE mytable SET value = 530 WHERE id = 2;
    UPDATE mytable SET value = 950 WHERE id = 3;
    UPDATE mytable SET value = 651 WHERE id = 4;
    

    问题是,这是仅适用于该确切数据的临时更新,还是您希望为该表中的所有可能数据实施的一般更新规则的情况?如果是这样,那么我们需要更多细节。

    【讨论】:

    • no 查询有超过 10000 行。我不能这样做
    • 所以...我们需要更多信息。你说你想复制最后 3 个值。是否总是由id 排序的最后三行的值?您是否总是将它们复制到具有空值的行上?如何为每一行分配最后 3 个值之一?
    • 不,它不是 3. 至少 2000 条记录。是的,它们是按 id 排序的。是的,我正在复制到具有空值的行。
    【解决方案3】:

    硬编码的3 出现两次,将被替换为您想要的任意多行。它假定最后的3 记录实际上具有值。它采用这些值并将它们按顺序应用于具有空值的记录集。

    update a
      set value = x.value
      from (
    
            select nullRows.id, lastRows.value
    
              from ( select id, value
                           ,(row_number() over(order by id) - 1) % 3 + 1 AS key
                       from ( select id, value
                                from a
                                order by id desc
                                limit 3
                            ) x
                       order by 1
    
                   ) AS lastRows
    
                  ,( select id
                           ,(row_number() over(order by id) - 1) % 3 + 1 AS key
                       from a
                       where value is null
                       order by id
    
                   ) AS nullRows
    
             where lastRows.key = nullRows.key
    
          ) x
    
    where a.id = x.id
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-10-14
      • 1970-01-01
      • 1970-01-01
      • 2020-12-30
      • 1970-01-01
      • 2020-08-04
      • 2012-11-23
      相关资源
      最近更新 更多