【问题标题】:Oracle SQL PerformanceOracle SQL 性能
【发布时间】:2021-03-13 02:36:22
【问题描述】:

我有 2 个这样的 SQL:

1.

select * from Customer_source s
where not exist 
(Select 1 from Customer_target t
 where s.CST_ID = t.CST_ID and ( s.NAME <> t.NAME
     or s.GENDER <> t.GENDER
     or .. or ) 
)
select * from Customer_source s
where not exist (Select 1 from Customer_target t
 where s.CST_ID = t.CST_ID and ( s.NAME || s.GENDER || ... <> t.NAME || t.GENDER || ... ) 
)

告诉我 SQL 是最好的性能

【问题讨论】:

  • 备选方案 2 有风险 - 不会提高性能。
  • 您有两个语句可以轻松地在您自己的系统上进行基准测试(我们只是猜测它有自己的性能特征)。任一查询实际上是您想要的 - 您是否希望源中的行在目标中没有具有相同 cst_id 的行和另一列中的不同值?我希望这将是 exists 而不是您想在其他列中查找具有相同值的行。
  • @jarlh 我同意 #2 有风险,但有时该代码会运行得更快。有关详细信息,请参阅我的答案。

标签: sql oracle performance


【解决方案1】:

TLDR; #1 最适合小型 OLTP 查询,#2 最适合大型数据仓库查询。


如果相关列被索引,并且查询只处理一小部分数据,那么使用 OR 条件连接会更快。但是如果列没有被索引,或者如果查询处理了很大比例的数据,那么连接列的速度会更快。

性能比较归结为经典的数据库性能选择 - 索引读取和嵌套循环连接对一小部分行更好,而全表扫描和哈希连接对大部分行更好。

OR 条件是 sargable - 它们是简单的比较条件,可以通过遍历一个或多个索引中的有序数据来快速查找。但是,OR 条件不能用于哈希连接 - Oracle 哈希连接一次只能比较两个值。

连接的列不可搜索 - 组合值不存储在索引中,因此 Oracle 无法遍历索引来查找相关值。但是,连接的列只需要一次比较,因此可以在哈希反连接中使用。

下面的测试用例处理了所有数据,并显示连接版本比 OR 版本运行得更快。如果删除索引,OR 版本的性能会更差。

--Create tables, insert 1M sample rows, created indexes, and gather optimizer statistics.
create table customer_source(cst_id number, name varchar2(100), gender varchar2(1));
create table customer_target(cst_id number, name varchar2(100), gender varchar2(1));

insert into customer_source select level, rpad(level, 10, 'A'), 'F' from dual connect by level <= 1000000;
insert into customer_target select level, rpad(level, 10, 'A'), 'F' from dual connect by level <= 1000000;

create index customer_source_idx1 on customer_source(cst_id);
create index customer_source_idx2 on customer_source(name);
create index customer_source_idx3 on customer_source(gender);

create index customer_target_idx1 on customer_target(cst_id);
create index customer_target_idx2 on customer_target(name);
create index customer_target_idx3 on customer_target(gender);

begin
    dbms_stats.gather_table_stats(user, 'customer_source');
    dbms_stats.gather_table_stats(user, 'customer_target');
end;
/

--#1: OR version.
--The explain plan shows a "FILTER" operation that re-reads an index repeatedly.
explain plan for
select * from Customer_source s
where not exists
(Select 1 from Customer_target t
 where s.CST_ID = t.CST_ID and ( s.NAME <> t.NAME
    or s.GENDER <> t.GENDER) 
);

select * from table(dbms_xplan.display);

--#2: Concatenation version.
--The explain plan shows a "HASH JOIN ANTI" operation.
explain plan for
select * from Customer_source s
where not exists (Select 1 from Customer_target t
 where s.CST_ID = t.CST_ID and ( s.NAME || s.GENDER <> t.NAME || t.GENDER) 
);

select * from table(dbms_xplan.display);

通常,“最佳外观”查询是运行最快的查询。连接值是丑陋的,正如其他人指出的那样,如果您的列可以为空,甚至可能无法正常工作。但是在数据仓库中,为了性能,编写奇怪的条件来启用哈希连接的情况并不少见。虽然您始终可以自己对查询进行基准测试,但最好了解这些概念,这样您就知道为什么要编写奇怪的查询。

【讨论】:

    【解决方案2】:

    两者做不同的事情。例如,只考虑两列,其值如下:

     Name    Gender
     ABC       M
     ABC      NULL
    

    字符串连接将是'ABCM' &lt;&gt; 'ABC'——它们不相等,因此计算结果为真。

    相等方法将是'ABC' &lt;&gt; 'ABC' OR 'M' &lt;&gt; NULL。这将评估为 FALSE OR NULL,即 FALSE。 Here 是一个 dbfiddle。

    还有其他不太可能产生相同值的情况。例如:

    ABCfe    male
    ABC      female
    

    还有其他情况,由于值到字符串的转换。例如,date 的时间分量在使用 || 时会被删除。

    因此,您应该选择符合您预期的版本。除此之外,您可能应该使用第一个版本,因为它对每种类型都使用本机比较——这似乎更适合您想要做的事情。

    我认为这里的语义比性能更重要。也就是说,在尝试优化之前选择准确反映您意图的代码。

    【讨论】:

    • 你可以在 concat 之前 nvl
    • @PhamHuyGiang 。 . .我不确定你的评论指的是什么。这是在回答您实际提出的问题。
    【解决方案3】:

    第二个是有风险的,正如评论中提到的,因为它正在连接值(a,b 将与 ab, null 匹配),并且如果有索引,也不会使用索引。

    所以根据我的经验,第一个查询对性能有好处,并且会以正确的逻辑获取记录。

    【讨论】:

    • 连接字符串前的 nvl
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-13
    • 1970-01-01
    • 2018-10-18
    • 2021-01-28
    • 2018-01-29
    • 1970-01-01
    相关资源
    最近更新 更多