sql - 从查询其他 2 个表的结果更新表

标签 sql postgresql sql-update case

我有以下问题: 有包含 5 列的 LIST 表:

  1. 列表项
  2. 产品编号
  3. 引用号
  4. 部件号
  5. 数量
  6. 状态

此表中大约有 100 000 行。还有表 parthistory,它们都有 part_number 列。

现在我需要用值 1、2 或 3 更新 LIST.state 列,其中 LIST.refNo = 一些值,具体取决于以下情况的结果:

If LIST.part_number does not exist in table part.part_number
    set LIST.state to 1.
If it does, if this part_number and corresponding date in history is less than a week,
    set LIST.State to 2. Else set it to 3.

我正在使用 postgreSQL。任何帮助将不胜感激。到目前为止,我知道如何使用这个简单的 IF ELSE 语句:

DO
$BODY$
BEGIN
    IF EXISTS (SELECT part_number FROM part WHERE refNo=7000) THEN

    ELSE 
        UPDATE LIST set state = 1
    END IF;
END;
$BODY$

我认为这不是最好的方法,因为现在我必须用循环将此 if 括起来,并且因为历史表中有超过 200 000 行,所以一一比较会 super 慢。

也许我可以先选择部分表中存在的值,然后对它们进行查询以从历史记录中获取值?

最佳答案

这里不需要plpgsql。

一次有很多行:

UPDATE list l
SET    state = u.state
FROM (
   SELECT li.pk_col
        , CASE WHEN h.hist_date > (now() - interval '1 week') THEN 2
               WHEN p.part_number IS NULL THEN 1
               ELSE 2
          END AS state
   FROM   list         li
   LEFT   JOIN part    p USING (part_number)
   LEFT   JOIN history h USING (part_number)
   WHERE  li.refNo = <some value>
   ) u
WHERE l.pk_col = u.pk_col   -- insert actual pk column
AND   l.state IS DISTINCT FROM u.state;

或者,更快,但没有子查询更冗长:

UPDATE list l
SET    state = CASE WHEN h.hist_date > (now() - interval '1 week') THEN 2
                    WHEN p.part_number IS NULL THEN 1
                    ELSE 2 END
FROM   list         li
LEFT   JOIN part    p USING (part_number)
LEFT   JOIN history h USING (part_number)
WHERE  li.refNo = <some value>
AND    l.pk_col = li.pk_col   -- insert actual pk column
AND    l.state IS DISTINCT FROM 
       CASE WHEN h.hist_date > (now() - interval '1 week') THEN 2
            WHEN p.part_number IS NULL THEN 1
            ELSE 2 END

我假定列 history.hist_date,因为您没有指定。

pk_col 替换为您实际的主键列。你没有指定。

你不应该使用 unquoted mixed case identifiersrefNo。使用 refno"refNo"

最后一个 WHERE 条件避免了空更新,其中 state 已经有了预期的值。如果您的情况不会发生,请省略。大多数情况下,这是人们往往会忘记的性能改进。

为了加快速度,您需要在相关列上建立索引。 PK 列自动编入索引。对于每个 part_numberlist.refNohistory.hist_date,您还需要一个。
理想情况下你有一个multicolumn index关于历史,例如:

CREATE INDEX h_multi_idx ON history (part_number, hist_date DESC);

在这种情况下,每一列都胜过一个索引。

几行的简单版本

另一种方法是相关子查询 - 更简单地更新单个或几行:

UPDATE list l
SET    state = COALESCE((
   SELECT CASE WHEN h.hist_date > (now() - interval '1 week') THEN 2 ELSE 3 END
   FROM   part p
   LEFT   JOIN history h USING (part_number)
   WHERE  p.part_number = l.part_number
   ), 1)
WHERE  refNo = <some value>;

对于同时处理大量行,相关子查询的扩展性不好。
请注意,即使 state 已经具有预期值,这也会更新该行。

评论更新

对于 history(part_number) 的重复项和(假设)(part_number) 中的没有重复项partlist,这是(许多可能的)解决方案之一:

UPDATE list l
SET    state = u.state
FROM (
   SELECT DISTINCT ON (h.part_number)
          li.pk_col
        , CASE WHEN h.hist_date > (now() - interval '1 week') THEN 2
               WHEN p.part_number IS NULL THEN 1
               ELSE 2
          END AS state
   FROM   list         li
   LEFT   JOIN part    p USING (part_number)
   LEFT   JOIN history h USING (part_number)
   WHERE  li.refNo = <some value>
   ORDER  BY h.part_number, h.hist_date DESC NULLS LAST
   ) u
WHERE l.pk_col = u.pk_col   -- insert actual pk column
AND   l.state IS DISTINCT FROM u.state;

可能有更有效的方法。取决于缺失的信息。

关于sql - 从查询其他 2 个表的结果更新表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17667021/

相关文章:

mysql - 计数器列增量一致吗?

php - 如何为用户 ID 启动 session

java - 带列表的 HibernateTemplate 查询

sql - 列出类别/子类别树并在同一行显示其子类别

mysql - 先获取最后一条记录,其余按升序排列

ruby-on-rails - windows环境无法识别postgres 'psql'命令

sql - 在 oracle 中使用 getDate() 更新行时出错

sql - 未创建行但递增主键

python - 如何使用 django_postgres_copy 将 headless csv 导入 postgresql?

mysql - 如何使用触发器更新 MySQL 中的不同表?