哪种 SQL 模式可以更快地避免插入重复行?
Posted
技术标签:
【中文标题】哪种 SQL 模式可以更快地避免插入重复行?【英文标题】:Which SQL pattern is faster to avoid inserting duplicate rows? 【发布时间】:2010-06-25 18:00:30 【问题描述】:我知道两种不重复插入的方法。第一个是使用WHERE NOT EXISTS
子句:
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
对于一个比另一个快,是否有一个一般规则,或者它取决于表?有没有比两者都更好的不同方法?
【问题讨论】:
您确定要在第一个示例中使用 SELECT * 吗?选择所有列?一般来说,不会这样做,但我不是 Postgres 专家。 你能用 EXPLAIN 运行这两个查询并发布吗?分析工具可帮助您逐案回答这些问题。 【参考方案1】:我建议在您需要唯一的列上定义一个 UNIQUE 约束(在本例中为 col1 和 col2),然后执行 INSERT。根据需要处理异常。
关于您对要求回滚的异常的评论,PostgreSQL 的解决方案是在您尝试可能导致异常的插入之前设置事务保存点。如果遇到异常,回滚到保存点。
见:
http://www.postgresql.org/docs/current/static/sql-savepoint.html http://www.postgresql.org/docs/current/static/sql-rollback-to.html【讨论】:
我已经有了独特的约束。但是,当它们被违反时,到目前为止,我必须提交或回滚整个事务。不过,我的每笔交易都需要多次插入,我不想部分提交其中的一些。有没有办法以更好的方式处理异常,不会弄乱游标/事务? (从 Python 执行此操作,使用 sqlobject 或 psycopg2) 是:捕获异常。如果它是重复键违规,那么您可以忽略它,因为这意味着您的行已经存在。如果它是另一种类型的异常,无论如何你都会遇到这个问题(例如 SQL 语法错误、磁盘已满、网络连接中断)。 如果我捕捉到异常,然后尝试使用相同的光标执行另一个查询,我得到一个psycopg2.InternalError: current transaction aborted; ignoring actions until end of transaction block
,或者类似的东西
这里是确切的错误消息:psycopg2.InternalError: current transaction is aborted, commands ignored until end of transaction block
。插入表格失败后我该怎么做才能防止这种状态?
我试着在这里更有说服力地问这个问题:***.com/questions/3120688/…【参考方案2】:
我认为使用 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 倍。
【讨论】:
以上是关于哪种 SQL 模式可以更快地避免插入重复行?的主要内容,如果未能解决你的问题,请参考以下文章