1

我有以下问题:有LIST5 列的表:

  1. 项目清单
  2. product_id
  3. 参考编号
  4. 零件号
  5. 数量
  6. 状态

100 000 rows在这张桌子周围有。还有表parthistory它们都有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一个一个比较会非常慢。

也许我可以首先选择零件表中存在的值并对它们进行查询以从历史中获取值?

4

2 回答 2

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

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

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

您不应该使用不带引号的混合大小写标识符,例如refNo. 使用refno"refNo"

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

为此,您需要相关列上的索引。PK 列会自动编制索引。您还需要一个 for each part_number、 forlist.refNo和 for history.hist_date
理想情况下,您应该有一个多列索引history例如:

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已经具有预期值,这也会更新该行。

更新评论

对于 in 上的重复项(part_number)history(假设)在or上没有重复项,这是(许多可能的)解决方案之一:(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;

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

于 2013-07-16T03:29:01.240 回答
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

这不是很优雅,但应该完成它,也不能确定它是否是最快的方式。

这是SQLFiddle

于 2013-07-16T03:38:46.187 回答