我有以下问题:
表LIST
有5列:
此表中有100 000 rows
左右。还有表格part
和history
,它们都有part_number
列。
现在我需要update LIST.state column with value 1, 2 or 3 where LIST.refNo = some value
,具体取决于以下情况的结果:
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$
我认为这不是最好的方法,因为现在我必须将这个包含在循环中,因为有over 200 000 rows in history table
,因此逐个比较会非常慢。
也许我可以先选择部分表中存在的值并对它们进行查询以从历史记录中获取值?
答案 0 :(得分:2)
这里不需要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
替换为您的实际主键列。你没有指定。
您不应该像refNo
那样使用unquoted mixed case identifiers。使用refno
或"refNo"
。
最后WHERE
条件可避免state
已达到预期值的空更新。如果在您的情况下不能发生,请忽略。大多数时候,人们往往会忘记这种改善。
为了加快速度,您需要在相关列上建立索引。 PK列会自动编入索引。每part_number
,list.refNo
和history.hist_date
都需要一个。{。}
理想情况下您history
上的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
已具有预期值,也会更新该行。
对于(part_number)
中的history
上的重复项和(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;
可能有更有效的方法。取决于缺少信息。
答案 1 :(得分:1)
您可以使用三个单独的查询执行此操作(我确信有一种方法可以在一个方面执行,例如Erwins的答案),它会立即根据ref no
更新所有值。
像这样:
--First
UPDATE LIST
SET STATE = 1
WHERE NOT PART_NUM
IN (SELECT PART_NUM
FROM PART)
AND REF_no = 1000;
--Second
UPDATE LIST
SET STATE = 2
WHERE PART_NUM IN
(SELECT PART_NUM
FROM HISTORY
WHERE age(TIME, CURRENT_TIMESTAMP) > (INTERVAL '-7 days'))
AND REF_NO = 1000;
--Third
UPDATE LIST
SET STATE = 3
WHERE STATE NOT IN(1,2)
AND REF_NO = 1000
这不是很优雅,但应该完成它,不能确定它是否也是最快的方式。