11

これらのオペレーターの違い、主にパフォーマンスの違いを知りたいです。

Difference between <> and != in 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);

シーケンスを使用して、1,000 万行がテーブルに追加されましSYSDATEた。タイムスタンプとランダム データは DBMS_RANDOM (AZ) 経由で、(0-99) は他の 2 つのフィールドです。

SELECT COUNT(*) FROM load_test;

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

1 row selected.

スキーマが分析され、適切な統計が提供されました。

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

3 つの単純なクエリは次のとおりです。

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

3 つのクエリの実行計画は同じで、タイミングは 2.12、2.13、および 2.10 秒です。

クエリで使用される構文に関係なく、実行計画では常に <> が表示されることに注意してください。

テストは、演算子の構文ごとに 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

数百分の 1 秒の変動がありますが、重要ではありません。3 つの構文の選択肢のそれぞれの結果は同じです。

構文の選択は解析され、最適化され、同じ労力で同時に返されます。したがって、このテストでどちらか一方を使用しても、認識できる利点はありません。

「ああ、BC」、「私のテストでは、本当の違いがあると信じており、そうでなければそれを証明することはできません」とあなたは言います.

はい、私は言います、それは完全に真実です。テスト、クエリ、データ、または結果を表示していません。だから私はあなたの結果について何も言うことはありません。他のすべての条件が同じであれば、どの構文を使用しても問題ないことを示しました。

「では、テストで 1 つの方が優れていることがわかるのはなぜですか?」

良い質問。いくつかの可能性があります:-

  1. テストに欠陥があります (外部要因 (他のワークロード、キャッシュなど) を排除していません。十分な情報に基づいて決定できる情報が提供されていません)
  2. あなたのクエリは特殊なケースです (クエリを見せてください。それについて話し合うことができます)。
  3. あなたのデータは特殊なケースです (おそらく - しかし、どのように - 私たちにもそれはわかりません)。
  4. 外部からの影響は他にもあります。

文書化された反復可能なプロセスを通じて、ある構文を使用しても別の構文を使用してもメリットがないことを示しました。<> != と ^= は同義だと思います。

そうでなければ大丈夫だと思うなら、そう

a)自分で試すことができる文書化された例を示します

b) 最適と思われる構文を使用します。私が正しくて違いがなければ、それは問題ではありません。あなたが正しければ、クールであれば、ほんの少しの作業で改善が得られます。

「でも、バーレソンはそのほうがましだと言ったし、私はファロート、ルイス、カイト、その他のバカよりも彼を信頼している」

彼はそれが良いと言ったのですか?私はそうは思わない。彼は決定的な例、テスト、または結果を提供しませんでしたが、!= の方が優れていると言っている人にリンクし、投稿の一部を引用しただけです。

言わずに見せる。

于 2012-09-20T12:44:41.860 に答える
19

Burleson サイトの記事を参照します。Oracle-L アーカイブへのリンクをたどりましたか? また、バーレソンが引用した電子メールに返信する他の電子メールを読みましたか?

そうでなければ、あなたはこの質問をしなかったでしょう。!= と の間に根本的な違いはないから<>です。元の観察結果は、ほぼ確実に、データベース内の周囲条件によってもたらされたまぐれでした。詳細については、 Jonathan LewisStephane Faroultからの回答をお読みください。


「尊敬はプログラマーが持つ必要があるものではなく、人間が持つべき基本的な態度です」

ある程度まで。路上で見知らぬ人に会った場合、もちろん礼儀正しく、敬意を持って接する必要があります。

しかし、その見知らぬ人が「パフォーマンスを向上させる」ために特定の方法でデータベースアプリケーションを設計することを望んでいる場合、説得力のある説明とそれを裏付ける防弾テストケースが必要です. ランダムな個人からの孤立した逸話では十分ではありません。

于 2012-08-17T13:28:27.880 に答える
12

The writer of the article, although a book author and the purveyor of some useful information, does not have a good reputation for accuracy. In this case the article was merely a mention of one persons observations on a well known Oracle mailing list. If you read through the responses you will see the assumptions of the post challenged, but no presumption of accuracy. Here are some excerpts:

Try running your query through explain plan (or autotrace) and see what that says... According to this, "!=" is considered to be the same as "<>"... Jonathan Lewis

Jonathan Lewis is a well respected expert in the Oracle community.

Just out of curiosity... Does the query optimizer generate a different execution plan for the two queries? Regards, Chris

.

Might it be bind variable peeking in action? The certain effect of writing != instead of <> is to force a re-parse. If at the first execution the values for :id were different and if you have an histogram on claws_doc_id it could be a reason. And if you tell me that claws_doc_id is the primary key, then I'll ask you what is the purpose of counting, in particular when the query in the EXISTS clause is uncorrelated with the outer query and will return the same result whatever :id is. Looks like a polling query. The code surrounding it must be interesting.

Stéphane Faroult

.

I'm pretty sure the lexical parse converts either != to <> or <> to !=, but I'm not sure whether that affects whether the sql text will match a stored outline.

.

Do the explain plans look the same? Same costs?

The following response is from the original poster.

Jonathan, Thank you for your answer. We did do an explain plan on both versions of the statement and they were identical, which is what is so puzzling about this. According to the documentation, the two forms of not equal are the same (along with ^= and one other that I can't type), so it makes no sense to me why there is any difference in performance.

Scott Canaan

.

Not an all inclusive little test but it appears at least in 10.1.0.2 it gets pared into a "<>" for either (notice the filter line for each plan)

.

Do you have any Stored Outline ? Stored Outlines do exact (literal) matches so if you have one Stored Outline for, say, the SQL with a "!=" and none for the SQL with a "<>" (or a vice versa), the Stored Outline might be using hints ? (although, come to think of it, your EXPLAIN PLAN should have shown the hints if executing a Stored Outline ?)

.

Have you tried going beyond just explain & autotrace and running a full 10046 level 12 trace to see where the slower version is spending its time? This might shed some light on the subject, plus - be sure to verify that the explain plans are exactly the same in the 10046 trace file (not the ones generated with the EXPLAIN= option), and in v$sqlplan. There are some "features" of autotrace and explain that can cause it to not give you an accurate explain plan.

Regards, Brandon

.

Is the phenomenon totally reproducible ?

Did you check the filter_predicates and access_predicates of the plan, or just the structure. I don't expect any difference, but a change in predicate order can result in a significant change in CPU usage if you are unlucky.

If there is no difference there, then enable rowsource statistics (alter session set "_rowsource_execution_statistics"=true) and run the queries, then grab the execution plan from V$sql_plan and join to v$sql_plan_statistics to see if any of the figures about last_starts, last_XXX_buffer_gets, last_disk_reads, last_elapsed_time give you a clue about where the time went.

If you are on 10gR2 there is a /*+ gather_plan_statistics */ hint you can use instead of the "alter session".

Regards Jonathan Lewis

At this point the thread dies and we see no further posts from the original poster, which leads me to believe that either the OP discovered an assumption they had made that was not true or did no further investigation.

I will also point out that if you do an explain plan or autotrace, you will see that the comparison is always displayed as <>.

Here is some test code. Increase the number of loop iterations if you like. You may see one side or the other get a higher number depending on the other activity on the server activity, but in no way will you see one operator come out consistently better than the other.

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 が使用します<>

実行計画が異なる場合は、表記ごとにクエリ キャッシュや統計に違いがある可能性があります。しかし、私は本当にそうではないと思います。

編集:

上記の意味。複雑なデータベースでは、いくつかの奇妙な副作用が発生する可能性があります。オラクルのことはよくわかりませんが、SQL Server 2008 R2 のようなクエリ コンパイル キャッシュがあると思います。クエリが新しいクエリとしてコンパイルされる場合、データベース オプティマイザは現在の統計に基づいて新しい実行計画を計算します。統計が変更された場合、別の結果になり、より悪い計画になる可能性があります。

于 2012-08-17T09:32:22.317 に答える