我有以下问题:
有包含 5 列的 LIST
表:
- 列表项
- 产品编号
- 引用号
- 部件号
- 数量
- 状态
此表中大约有 100 000 行
。还有表 part
和 history
,它们都有 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 identifiers像 refNo
。使用 refno
或 "refNo"
。
最后一个 WHERE
条件避免了空更新,其中 state
已经有了预期的值。如果您的情况不会发生,请省略。大多数情况下,这是人们往往会忘记的性能改进。
为了加快速度,您需要在相关列上建立索引。 PK 列自动编入索引。对于每个 part_number
、list.refNo
和 history.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)
中的没有重复项part
或 list
,这是(许多可能的)解决方案之一:
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/