2010-06-25 58 views
3

我知道两种插入方式,没有重复。首先是使用WHERE NOT EXISTS条款:为避免插入重复行,哪种SQL模式更快?

INSERT INTO table_name (col1, col2, col3) 
SELECT %s, %s, %s 
WHERE NOT EXISTS (
    SELECT * FROM table_name AS T 
    WHERE T.col1 = %s 
     AND T.col2 = %s) 

另一种是做一个LEFT JOIN

INSERT INTO table_name (col1, col2, col3) 
SELECT %s, %s, %s 
FROM (SELECT %s, %s, %s) A 
LEFT JOIN table_name B 
ON B.COL1 = %s 
AND B.COL2 = %s 
WHERE B.id IS NULL 
LIMIT 1 

有没有为一个比另一个更快的一般规则,或者它取决于表?有没有比两者更好的方法?

+0

您确定要在第一个示例中使用SELECT *吗?选择所有列?一般来说,一个wouuldn't做到这一点,但我不是Postgres的专家。 – DOK 2010-06-25 18:05:36

+0

你能用EXPLAIN运行这两个查询并发布吗?分析工具可帮助您根据具体情况回答这些问题。 – Freiheit 2010-06-25 18:05:38

回答

5

我建议在需要唯一的列上定义一个UNIQUE约束(col1 & col2,在这种情况下),然后执行INSERT。根据需要处理异常。


重新您关于异常苛刻回滚评论,PostgreSQL的解决办法是,你尝试可能会导致异常插入之前设置事务保存点。如果您遇到异常,请回滚到保存点。

参见:

+0

我已经有了独特的限制。但是,当它们被违反时,我必须提交或回滚到目前为止的整个交易。尽管我的每笔交易都需要多次插入,但我不想部分提交它们中的一些。有没有办法以更好的方式处理异常,这不会弄乱游标/事务? (通过Python,使用sqlobject或psycopg2来完成) – Claudiu 2010-06-25 18:21:33

+0

是:*捕获异常*。如果这是重复的密钥违规,那么您可以忽略它,因为这意味着您的行已经存在。如果是另一种类型的异常,则无论如何您都会遇到该问题(例如,SQL语法错误,磁盘已满,网络连接断开)。 – 2010-06-25 18:34:18

+0

如果我捕获异常,然后尝试执行另一个查询与相同的游标,我得到一个'psycopg2.InternalError:当前事务中止;忽略动作,直到事务块结束,或者类似的东西 – Claudiu 2010-06-25 18:41:46

-1

我认为使用EXISTS是更有效的你可以这样做:!

if exists(select 1 from table_name where col1 = %s and col2 = %s) then 
    insert into table_name (col1, col2, col3) 
    select %s, %s, %s; 
end if; 

下测试,使用EXISTS是比使用NOT EXISTS快50倍。

另一种方法是使用EXCEPT。

INSERT INTO table_name (col1, col2, col3) 
SELECT %s, %s, %s 
except 
select col1, col2, col3 from table_name 

在测试中,使用EXCEPT比使用NOT EXISTS快了约3倍。