insert into ... where not exists 在性能上成本更高吗?
Is the insert into ... where not exists costlier in performance?
我们正在使用 Oracle 数据库 (12c)
A table ABCD 具有以下结构,大约 o(10^5) 行
CCOL* - Varchar2 列
DCOL* - 时间戳列
Name Null? Type
----- -------- -----------------
CCOL1 NOT NULL VARCHAR2(64 CHAR)
CCOL2 NOT NULL VARCHAR2(30 CHAR)
CCOL3 NOT NULL VARCHAR2(64 CHAR)
DCOL1 NOT NULL TIMESTAMP(6)
CCOL4 NOT NULL VARCHAR2(64 CHAR)
DCOL2 NOT NULL TIMESTAMP(6)
CCOL5 VARCHAR2(32 CHAR)
这个 table 的主键是 (CCOL1, CCOL2)
我们有以下插入语句:
插入 1:
BEGIN
INSERT INTO abcd (
ccol1, ccol2, ccol3, dcol1, ccol4, dcol2, ccol5)
SELECT
:b1, :b2, :b3, :b4, :b5, :b6, :b7
FROM
dual
WHERE
NOT EXISTS (
SELECT 1 FROM abcd WHERE ccol1 = :b1 AND ccol2 = :b2 );
EXCEPTION
WHEN dup_val_on_index THEN
NULL;
WHEN OTHERS THEN
RAISE;
END;
我们有替代方案(删除了不存在的部分)
插入 2:
BEGIN
INSERT INTO abcd (
ccol1, ccol2, ccol3, dcol1, ccol4, dcol2, ccol5)
VALUES
(:b1, :b2, :b3, :b4, :b5, :b6, :b7);
EXCEPTION
WHEN dup_val_on_index THEN
NULL;
WHEN OTHERS THEN
RAISE;
END;
这些插页哪个更好?
我猜你问是因为你多次调用这段代码。通常,哪种方法更快将取决于您要插入的值已存在于 table 中的可能性有多大。抛出和捕获异常比检查主键值是否不存在要慢几个数量级。但是,如果您只想对每百万次插入抛出一次异常,则第二种方法可能更有效。当我过去遇到这样的事情时,Oracle 版本之间在抛出和捕获异常的成本方面也存在显着差异,因此确切的盈亏平衡点也会因 Oracle 版本而异。实际上,您需要对系统进行基准测试才能确定。
就我个人而言,我可能会把这个(正如@Boneist 建议的那样)写成 MERGE
只有一个 when not matched
子句
MERGE INTO abcd dest
USING( SELECT :b1 ccol1, :b2 ccol2, :b3 ccol3, ...
FROM dual ) src
ON( src.ccol1 = dest.ccol1 AND
src.ccol2 = dest.ccol2 )
WHEN NOT MATCHED THEN
INSERT( ccol1, ccol2, ccol3, ... )
VALUES( src.ccol1, src.ccol2, src.ccol3, ... )
另外,拥有一个只执行 RAISE
的 WHEN OTHERS
异常处理程序没有多大意义——如果您不打算对它。在您的第一段代码中,捕获并忽略 dup_val_on_index
检查没有意义,因为您已经有了 NOT EXISTS
子句。那时,dup_val_on_index
不再是预期的异常,因此您不应该捕捉并忽略它。
我们正在使用 Oracle 数据库 (12c)
A table ABCD 具有以下结构,大约 o(10^5) 行
CCOL* - Varchar2 列
DCOL* - 时间戳列
Name Null? Type
----- -------- -----------------
CCOL1 NOT NULL VARCHAR2(64 CHAR)
CCOL2 NOT NULL VARCHAR2(30 CHAR)
CCOL3 NOT NULL VARCHAR2(64 CHAR)
DCOL1 NOT NULL TIMESTAMP(6)
CCOL4 NOT NULL VARCHAR2(64 CHAR)
DCOL2 NOT NULL TIMESTAMP(6)
CCOL5 VARCHAR2(32 CHAR)
这个 table 的主键是 (CCOL1, CCOL2)
我们有以下插入语句: 插入 1:
BEGIN
INSERT INTO abcd (
ccol1, ccol2, ccol3, dcol1, ccol4, dcol2, ccol5)
SELECT
:b1, :b2, :b3, :b4, :b5, :b6, :b7
FROM
dual
WHERE
NOT EXISTS (
SELECT 1 FROM abcd WHERE ccol1 = :b1 AND ccol2 = :b2 );
EXCEPTION
WHEN dup_val_on_index THEN
NULL;
WHEN OTHERS THEN
RAISE;
END;
我们有替代方案(删除了不存在的部分)
插入 2:
BEGIN
INSERT INTO abcd (
ccol1, ccol2, ccol3, dcol1, ccol4, dcol2, ccol5)
VALUES
(:b1, :b2, :b3, :b4, :b5, :b6, :b7);
EXCEPTION
WHEN dup_val_on_index THEN
NULL;
WHEN OTHERS THEN
RAISE;
END;
这些插页哪个更好?
我猜你问是因为你多次调用这段代码。通常,哪种方法更快将取决于您要插入的值已存在于 table 中的可能性有多大。抛出和捕获异常比检查主键值是否不存在要慢几个数量级。但是,如果您只想对每百万次插入抛出一次异常,则第二种方法可能更有效。当我过去遇到这样的事情时,Oracle 版本之间在抛出和捕获异常的成本方面也存在显着差异,因此确切的盈亏平衡点也会因 Oracle 版本而异。实际上,您需要对系统进行基准测试才能确定。
就我个人而言,我可能会把这个(正如@Boneist 建议的那样)写成 MERGE
只有一个 when not matched
子句
MERGE INTO abcd dest
USING( SELECT :b1 ccol1, :b2 ccol2, :b3 ccol3, ...
FROM dual ) src
ON( src.ccol1 = dest.ccol1 AND
src.ccol2 = dest.ccol2 )
WHEN NOT MATCHED THEN
INSERT( ccol1, ccol2, ccol3, ... )
VALUES( src.ccol1, src.ccol2, src.ccol3, ... )
另外,拥有一个只执行 RAISE
的 WHEN OTHERS
异常处理程序没有多大意义——如果您不打算对它。在您的第一段代码中,捕获并忽略 dup_val_on_index
检查没有意义,因为您已经有了 NOT EXISTS
子句。那时,dup_val_on_index
不再是预期的异常,因此您不应该捕捉并忽略它。