11

我想知道这些运营商的区别,主要是他们的性能差异。

我看过SQL 中 <> 和 != 之间的区别,它没有与性能相关的信息。

然后我在dba-oracle.com上找到了这个,它表明在 10.2 以后的性能可能会大不相同。

我想知道为什么?那么!=总是表现更好<>吗?

注意:我们的测试和实时系统显示的性能,从 更改为<>!=查询返回的时间有很大影响。我在这里问为什么会发生这种情况,而不是它们是否相同。我从语义上知道它们是,但实际上它们是不同的。

4

4 回答 4

28

我已经测试了 Oracle 中不等于运算符的不同语法的性能。我试图消除对测试的所有外部影响。

我正在使用 11.2.0.3 数据库。没有连接其他会话,并且在开始测试之前重新启动了数据库。

使用单个表和主键序列创建了模式

CREATE TABLE loadtest.load_test (
  id NUMBER NOT NULL,
  a VARCHAR2(1) NOT NULL,
  n NUMBER(2) NOT NULL,
  t TIMESTAMP NOT NULL
);

CREATE SEQUENCE loadtest.load_test_seq
START WITH 0
MINVALUE 0;

该表已编入索引以提高查询性能。

ALTER TABLE loadtest.load_test
ADD CONSTRAINT pk_load_test
PRIMARY KEY (id)
USING INDEX;

CREATE INDEX loadtest.load_test_i1
ON loadtest.load_test (a, n);

使用序列将一千万行添加到表中,SYSDATE时间戳和随机数据通过 DBMS_RANDOM (AZ) 和 (0-99) 用于其他两个字段。

SELECT COUNT(*) FROM load_test;

COUNT(*)
----------
10000000

1 row selected.

分析模式以提供良好的统计数据。

EXEC DBMS_STATS.GATHER_SCHEMA_STATS(ownname => 'LOADTEST', estimate_percent => NULL, cascade => TRUE);

三个简单的查询是:-

SELECT a, COUNT(*) FROM load_test WHERE n <> 5 GROUP BY a ORDER BY a;

SELECT a, COUNT(*) FROM load_test WHERE n != 5 GROUP BY a ORDER BY a;

SELECT a, COUNT(*) FROM load_test WHERE n ^= 5 GROUP BY a ORDER BY a;

除了不等于运算符的语法(不仅是 <> 和 != ,还有 ^= )之外,它们完全相同

首先,每个查询都在不收集结果的情况下运行,以消除缓存的影响。

下一次计时和自动跟踪已打开,以收集查询的实际运行时间和执行计划。

SET TIMING ON

SET AUTOTRACE TRACE

现在查询依次运行。首先是<>

> SELECT a, COUNT(*) FROM load_test WHERE n <> 5 GROUP BY a ORDER BY a;

26 rows selected.

Elapsed: 00:00:02.12

Execution Plan
----------------------------------------------------------
Plan hash value: 2978325580

--------------------------------------------------------------------------------------
| Id  | Operation             | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |              |    26 |   130 |  6626   (9)| 00:01:20 |
|   1 |  SORT GROUP BY        |              |    26 |   130 |  6626   (9)| 00:01:20 |
|*  2 |   INDEX FAST FULL SCAN| LOAD_TEST_I1 |  9898K|    47M|  6132   (2)| 00:01:14 |
--------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("N"<>5)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      22376  consistent gets
      22353  physical reads
          0  redo size
        751  bytes sent via SQL*Net to client
        459  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
         26  rows processed

下一个!=

> SELECT a, COUNT(*) FROM load_test WHERE n != 5 GROUP BY a ORDER BY a;

26 rows selected.

Elapsed: 00:00:02.13

Execution Plan
----------------------------------------------------------
Plan hash value: 2978325580

--------------------------------------------------------------------------------------
| Id  | Operation             | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |              |    26 |   130 |  6626   (9)| 00:01:20 |
|   1 |  SORT GROUP BY        |              |    26 |   130 |  6626   (9)| 00:01:20 |
|*  2 |   INDEX FAST FULL SCAN| LOAD_TEST_I1 |  9898K|    47M|  6132   (2)| 00:01:14 |
--------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("N"<>5)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      22376  consistent gets
      22353  physical reads
          0  redo size
        751  bytes sent via SQL*Net to client
        459  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
         26  rows processed

最后^=

> SELECT a, COUNT(*) FROM load_test WHERE n ^= 5 GROUP BY a ORDER BY a;

26 rows selected.

Elapsed: 00:00:02.10

Execution Plan
----------------------------------------------------------
Plan hash value: 2978325580

--------------------------------------------------------------------------------------
| Id  | Operation             | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |              |    26 |   130 |  6626   (9)| 00:01:20 |
|   1 |  SORT GROUP BY        |              |    26 |   130 |  6626   (9)| 00:01:20 |
|*  2 |   INDEX FAST FULL SCAN| LOAD_TEST_I1 |  9898K|    47M|  6132   (2)| 00:01:14 |
--------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("N"<>5)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      22376  consistent gets
      22353  physical reads
          0  redo size
        751  bytes sent via SQL*Net to client
        459  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
         26  rows processed

三个查询的执行计划相同,时间分别为 2.12、2.13 和 2.10 秒。

应该注意的是,无论查询中使用哪种语法,执行计划总是显示 <>

对每个运算符语法重复测试十次。这些是时间:-

<>

2.09
2.13
2.12
2.10
2.07
2.09
2.10
2.13
2.13
2.10

!=

2.09
2.10
2.12
2.10
2.15
2.10
2.12
2.10
2.10
2.12

^=

2.09
2.16
2.10
2.09
2.07
2.16
2.12
2.12
2.09
2.07

虽然有百分之几秒的一些差异,但并不重要。三种语法选择中的每一种的结果都是相同的。

语法选择被解析、优化并在同一时间以同样的努力返回。因此,在这个测试中使用一个比另一个没有明显的好处。

“Ah BC”,您说,“在我的测试中,我相信存在真正的差异,否则您无法证明这一点”。

是的,我说,这是完全正确的。您尚未显示您的测试、查询、数据或结果。所以我对你的结果无话可说。我已经证明,在所有其他条件相同的情况下,使用哪种语法并不重要。

“那为什么我在测试中发现那个更好呢?”

好问题。有几种可能性:-

  1. 您的测试存在缺陷(您没有消除外部因素 - 其他工作负载、缓存等您没有提供我们可以做出明智决定的信息)
  2. 您的查询是一个特例(给我看查询,我们可以讨论它)。
  3. 您的数据是一种特殊情况(也许 - 但如何 - 我们也没有看到)。
  4. 还有一些其他的外部影响。

我通过一个记录在案且可重复的过程表明,使用一种语法优于另一种语法并没有什么好处。我相信 <> != 和 ^= 是同义词。

如果你相信否则没问题,那么

a)展示一个我可以自己尝试的记录示例

b) 使用您认为最好的语法。如果我是正确的并且没有区别,那没关系。如果你是正确的,那么很酷,你只需很少的工作就能得到改进。

“但伯勒森说这更好,我比你、法鲁特、刘易斯、凯特和所有其他流浪汉更信任他。”

他说这样更好吗?我不这么认为。他没有提供任何明确的示例、测试或结果,但只链接到有人说 != 更好,然后引用了他们的一些帖子。

秀不说。

于 2012-09-20T12:44:41.860 回答
19

您参考了 Burleson 网站上的文章。您是否点击了指向 Oracle-L 存档的链接?您是否阅读过其他回复 Burleson 引用的电子邮件的电子邮件?

我不认为你这样做了,否则你不会问这个问题。!= 因为和之间没有根本区别<>。最初的观察几乎可以肯定是数据库中的环境条件造成的侥幸。阅读Jonathan LewisStephane Faroult的回复以了解更多信息。


“尊重不是程序员必须具备的,是任何人都应该具备的基本态度”

在一定程度上。当我们在街上遇到陌生人时,我们当然应该礼貌并尊重他们。

但是,如果那个陌生人希望我以特定的方式设计我的数据库应用程序以“提高性能”,那么他们应该有一个令人信服的解释和一些防弹的测试用例来支持它。来自某个随机个体的孤立轶事是不够的。

于 2012-08-17T13:28:27.880 回答
12

文章的作者虽然是书籍作者和一些有用信息的提供者,但在准确性方面并没有很好的声誉。在这种情况下,这篇文章只是提到了一个人在著名的 Oracle 邮件列表上的观察。如果您阅读回复,您会看到帖子的假设受到质疑,但没有准确性假设。以下是一些摘录:

尝试通过解释计划(或自动跟踪)运行您的查询,看看它说了什么......据此,“!=”被认为与“<>”相同......乔纳森刘易斯

Jonathan Lewis 是 Oracle 社区中一位备受尊敬的专家。

只是出于好奇......查询优化器是否为两个查询生成不同的执行计划?问候,克里斯

.

可能是绑定变量偷看?写 != 而不是 <> 的某些效果是强制重新解析。如果在第一次执行时 :id 的值不同,并且如果您在 claws_doc_id 上有直方图,则可能是一个原因。如果你告诉我 claws_doc_id 是主键,那么我会问你计数的目的是什么,特别是当 EXISTS 子句中的查询与外部查询不相关并且无论 :id 是什么都会返回相同的结果时. 看起来像一个轮询查询。围绕它的代码一定很有趣。

斯蒂芬·法鲁特

.

我很确定词法解析将!= 转换为 <> 或 <> 转换为!=,但我不确定这是否会影响 sql 文本是否与存储的大纲匹配。

.

解释计划看起来一样吗?一样的费用?

以下回复来自原始海报。

乔纳森,谢谢你的回答。我们确实对这两个版本的声明做了一个解释计划,它们是相同的,这就是令人费解的地方。根据文档,不等于的两种形式是相同的(以及 ^= 和另一种我无法输入的形式),所以对我来说为什么性能上有任何差异是没有意义的。

斯科特·迦南

.

不是一个包罗万象的小测试,但它至少出现在 10.1.0.2 中,它被缩减为一个“<>”(注意每个计划的过滤线)

.

你有存储大纲吗?存储大纲进行精确(文字)匹配,因此如果您有一个存储大纲,例如带有“!=”的 SQL,而没有一个用于带有“<>”的 SQL(反之亦然),则存储大纲可能是使用提示?(不过,想想看,如果执行存储大纲,您的 EXPLAIN PLAN 应该会显示提示?)

.

您是否尝试过超越仅解释和自动跟踪并运行完整的 10046 级别 12 跟踪以查看较慢版本在哪里花费时间?这可能会对该主题有所了解,另外 - 请务必验证 10046 跟踪文件(不是使用 EXPLAIN= 选项生成的文件)和 v$sqlplan 中的解释计划完全相同。自动跟踪和解释的一些“功能”可能会导致它无法为您提供准确的解释计划。

问候,布兰登

.

这种现象完全可以重现吗?

您是否检查了计划的 filter_predicates 和 access_predicates,或者只是结构。我不希望有任何区别,但是如果您不走运,谓词顺序的更改可能会导致 CPU 使用率发生显着变化。

如果没有区别,则启用行源统计(alter session set "_rowsource_execution_statistics"=true)并运行查询,然后从 V$sql_plan 中获取执行计划并加入 v$sql_plan_statistics 以查看是否有关于 last_starts 的数字, last_XXX_buffer_gets, last_disk_reads, last_elapsed_time 为您提供时间去向的线索。

如果您使用的是 10gR2,则可以使用 /*+gather_plan_statistics */ 提示来代替“alter session”。

问候乔纳森·刘易斯

在这一点上,线程死了,我们看不到原始海报的更多帖子,这让我相信,要么是 OP 发现了他们所做的不正确的假设,要么没有进行进一步的调查。

我还要指出,如果您执行解释计划或自动跟踪,您会看到比较始终显示为<>.

这是一些测试代码。如果您愿意,可以增加循环迭代的次数。您可能会看到一方或另一方获得更高的数字,具体取决于服务器活动上的其他活动,但绝不会看到一个操作员始终比另一个更好。

DROP TABLE t1;
DROP TABLE t2;
CREATE TABLE t1 AS (SELECT level c1 FROM dual CONNECT BY level <=144000);
CREATE TABLE t2 AS (SELECT level c1 FROM dual CONNECT BY level <=144000);

SET SERVEROUTPUT ON FORMAT WRAPPED

DECLARE
   vStart  Date;
   vTotalA Number(10) := 0;
   vTotalB Number(10) := 0;
   vResult Number(10);
BEGIN   
   For vLoop In 1..10 Loop
      vStart := sysdate;
      For vLoop2 In 1..2000 Loop
         SELECT count(*) INTO vResult FROM t1 WHERE t1.c1 = 777 AND EXISTS
            (SELECT 1 FROM t2 WHERE t2.c1 <> 0);
      End Loop;
      vTotalA := vTotalA + ((sysdate - vStart)*24*60*60);

      vStart := sysdate;
      For vLoop2 In 1..2000 Loop
         SELECT count(*) INTO vResult FROM t1 WHERE t1.c1 = 777 AND EXISTS
            (SELECT 1 FROM t2 WHERE t2.c1 != 0);
      End Loop;
      vTotalB := vTotalB + ((sysdate - vStart)*24*60*60);

      DBMS_Output.Put_Line('Total <>: ' || RPAD(vTotalA,8) || '!=: ' || vTotalB);
      vTotalA := 0;
      vTotalB := 0;
   End Loop;

END;
于 2012-08-17T13:49:35.817 回答
4

程序员将使用!=

DBA 将使用<>

如果有不同的执行计划,则可能是查询缓存或每种表示法的统计信息存在差异。但我真的不这么认为。

编辑:

我上面的意思。在复杂的数据库中可能会有一些奇怪的副作用。我不太了解 oracle,但我认为 SQL Server 2008 R2 中有一个查询编译缓存。如果查询被编译为新查询,则数据库优化器会根据当前统计信息计算新的执行计划。如果统计数据发生了变化,它将导致另一个,可能是一个更糟糕的计划。

于 2012-08-17T09:32:22.317 回答