5

我有责任将我们的代码从 sqlite 切换到 postgres。下面复制了我遇到的问题之一。

INSERT INTO group_phones(group_id, phone_name)
SELECT g.id, p.name 
FROM phones AS p, groups as g
WHERE g.id IN ($add_groups) AND p.name IN ($phones);

当有重复记录时,就会出现问题。在此表中,两个值的组合必须是唯一的。我在其他地方使用了一些 plpgsql 函数来执行更新或插入操作,但在这种情况下,我可以一次执行多个插入操作。我不确定如何为此编写存储例程。感谢所有 sql 专家提供的所有帮助!

4

2 回答 2

12

3个挑战。

  1. 您的查询在表和之间没有JOIN条件,这实际上是有限的- 您很可能不打算这样做。即每部符合条件的电话都与符合条件的每个组相结合。如果您有 100 部电话和 100 个组,则已经有 10,000 个组合。phonesgroupsCROSS JOIN

  2. 插入不同的组合(group_id, phone_name)

  3. 避免插入table中已经存在group_phones的行。

所有事情都认为它可能看起来像这样:

INSERT INTO group_phones(group_id, phone_name)
SELECT i.id, i.name
FROM  (
    SELECT DISTINCT g.id, p.name -- get distinct combinations
    FROM   phones p
    JOIN   groups g ON ??how are p & g connected??
    WHERE  g.id IN ($add_groups)
    AND    p.name IN ($phones)
    ) i
LEFT   JOIN group_phones gp ON (gp.group_id, gp.phone_name) = (i.id, i.name)
WHERE  gp.group_id IS NULL  -- avoid duping existing rows

并发

这种形式最大限度地减少了并发写操作出现竞争条件的机会。如果您的表有 大量并发写入负载,您可能希望独占锁定表或使用可序列化事务隔离,这可以防止在约束验证(行不存在)和查询中的写操作。

BEGIN ISOLATION LEVEL SERIALIZABLE;
INSERT ...
COMMIT;

如果它因序列化错误而回滚,请准备好重复该事务。有关该主题的更多信息,好的起点可能是@depesz 的这篇博客文章关于 SO 的相关问题

但是,通常情况下,您甚至不必为此烦恼。

表现

LEFT JOIN tbl ON right_col = left_col WHERE right_col IS NULL

通常是右表中具有不同列的最快方法。如果您在列中有欺骗(特别是如果有很多),

WHERE NOT EXISTS (SELECT 1 FROM tbl WHERE right_col = left_col)

可能会更快,因为它可以在找到第一行后立即停止扫描。

您也可以使用IN,就像 @dezso 演示的那样,但在 PostgreSQL 中它通常较慢。

于 2012-08-09T21:10:15.597 回答
2

尝试以下操作:

INSERT INTO group_phones(group_id, phone_name)
SELECT DISTINCT g.id, p.name 
FROM phones AS p, groups as g
WHERE 
    g.id IN ($add_groups) 
    AND p.name IN ($phones)
    AND (g.id, p.name) NOT IN (
        SELECT group_id, phone_name
        FROM group_phones
    )
;

DISTINCT您可以确保将插入唯一的行,并使用该子句NOT IN排除已经存在的行。

注意虽然这个解决方案可能更容易理解,但在大多数情况下,Erwin 的性能会更好。

于 2012-08-09T21:09:16.937 回答