我有责任将代码从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专家的帮助!
答案 0 :(得分:11)
3 挑战。
您的查询在表JOIN
和phones
之间没有 groups
条件,这实际上是有限的CROSS JOIN
- 其中你很可能不打算。即每个符合条件的手机都会与符合条件的每个组合在一起。如果您有100部手机和100组已经有10,000种组合。
插入(group_id, phone_name)
避免在表格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
此表单最大限度地减少了并发写操作的竞争条件。 如果您的表格有重度并发写入加载,您可能需要lock the table exclusively或使用serializable transaction isolation,这可以防止出现极不可能的情况在约束验证(行不存在)和查询中的写操作之间的微小时隙中由并发事务更改。
BEGIN ISOLATION LEVEL SERIALIZABLE;
INSERT ...
COMMIT;
如果事务以序列化错误回滚,请准备重复该事务。 有关该主题的更多信息,可以选择blog post by @depesz或此related question on 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中通常会更慢。
答案 1 :(得分: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的表现会更好。