web-dev-qa-db-ja.com

Oracle <>、!=、^ =演算子

それらの演算子の違い、主にパフォーマンスの違いを知りたいです。

SQLの<>と!=の違い を確認しましたが、パフォーマンスに関連する情報はありません。

次に、これを dba-Oracle.com で見つけました。これは、10.2以降ではパフォーマンスが大幅に異なる可能性があることを示しています。

なんでだろう? !=は常に<>よりもパフォーマンスが優れていますか?

注:テストとライブシステムでのパフォーマンスが示すように、<>から!=に変更すると、クエリが返される時間に大きな影響があります。これが発生する理由ではなく、なぜ発生するのかを尋ねます。それらは同じかどうか。私はそれらが意味的には知っていますが、実際にはそれらは異なります。

11
Junchen Liu

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、他の2つのフィールドにはDBMS_RANDOM(A-Z)および(0-99)を介したランダムデータのシーケンスを使用して、1,000万行がテーブルに追加されました。

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

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

構文の選択は解析され、最適化され、同じ努力で同時に返されます。したがって、このテストで相互に使用することによる認識できる利点はありません。

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

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

「では、なぜ私のテストで1つが優れていると思うのですか?」

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

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

文書化された反復可能なプロセスを介して、ある構文を別の構文よりも使用することに利点がないことを示しました。 <>!=と^ =は同義語だと思います。

あなたがそうでなければ大丈夫だと信じるなら、そう

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

そして

b)最適と思われる構文を使用します。私が正しく、違いがなければ、それは問題ではありません。あなたが正しくてかっこいいなら、あなたはほんの少しの仕事のために改善があります。

「しかし、バーレソンはそれがより良いと言った、そして私はあなた、ファロウト、ルイス、カイトと他のすべてのやつよりも彼を信頼している。」

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

語るのではなく見せる。

26
Lunc

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

私はあなたがそうしたとは思わない、そうでなければあなたはこの質問をしなかっただろう。 !=<>の間に基本的な違いはないからです。元の観察結果は、ほぼ確実に、データベースの周囲条件によって引き起こされたまぐれでした。 Jonathan Lewis および Stephane Faroult からの応答を読んで、詳細を理解してください。


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

ある程度まで。通りで見知らぬ人に会うときは、もちろん礼儀正しく、敬意を持って接する必要があります。

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

19
APC

記事 の作者は、本の著者であり、いくつかの有用な情報の提供者ですが、正確さについては評判が良くありません。この場合、記事は、よく知られているOracleメーリングリストでの 1人の観察 についての言及にすぎませんでした。回答を読むと、異議を唱えられた投稿の仮定がわかりますが、正確さの推定はありません。ここにいくつかの抜粋があります:

Explain Plan(またはAutotrace)を介してクエリを実行し、その内容を確認してください...これによると、「!=」は「<>」と同じであると見なされます... Jonathan Lewis

Jonathan Lewisは、Oracleコミュニティで尊敬されている専門家です。

好奇心から...クエリオプティマイザは、2つのクエリに対して異なる実行プランを生成しますか?よろしく、クリス

バインド変数のピークが動作している可能性がありますか? <>の代わりに!=を書き込むことの特定の効果は、再解析を強制することです。最初の実行時に:idの値が異なり、claws_doc_idにヒストグラムがある場合は、それが理由である可能性があります。そして、claws_doc_idが主キーであると私に言った場合、特にEXISTS句のクエリが外部クエリと無相関であり、:idが何であれ、同じ結果を返す場合は、カウントの目的を尋ねます。 。ポーリングクエリのように見えます。それを取り巻くコードは興味深いものでなければなりません。

StéphaneFaroult

字句解析が!=を<>に、または<>を!=に変換することは確かですが、それがSQLテキストが格納されたアウトラインと一致するかどうかに影響するかどうかはわかりません。

説明プランは同じように見えますか?同じ費用ですか?

次の回答は元のポスターからのものです。

ジョナサン、答えてくれてありがとう。私たちは声明の両方のバージョンで説明計画を行いました、そしてそれらは同一でした、それはこれについてとても不可解なことです。ドキュメントによると、等しくない2つの形式は同じであるため(^ =と、入力できないもう1つの形式)、パフォーマンスに違いがある理由はわかりません。

スコットカナン

包括的な小さなテストではありませんが、少なくとも10.1.0.2では、どちらの場合も「<>」に分類されます(各プランのフィルターラインに注意してください)。

保存されたアウトラインはありますか?保存されたアウトラインはexact(リテラル)と一致するため、たとえば、SQLに「!=」が付いた保存されたアウトラインが1つあり、SQLには何もない場合「<>」(またはその逆)を使用すると、保存されたアウトラインがヒントを使用している可能性がありますか? (とはいえ、考えてみると、保存されたアウトラインを実行する場合、EXPLAIN PLANはヒントを表示する必要がありますか?)

説明と自動トレースを超えて、完全な10046レベル12トレースを実行して、遅いバージョンがどこで時間を費やしているかを確認してみましたか?これにより、この問題に光が当てられる可能性があります。さらに、explainプランが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を使用している場合は、「altersession」の代わりに使用できる/ * + collect_plan_statistics * /ヒントがあります。

よろしくジョナサンルイス

この時点でスレッドは終了し、元の投稿者からの投稿はこれ以上表示されません。これにより、OPは、彼らが行った仮定が真実ではないことを発見したか、それ以上の調査を行わなかったと思います。

また、Explain PlanまたはAutotraceを実行すると、比較が常に<>として表示されることも指摘しておきます。

ここにいくつかのテストコードがあります。必要に応じて、ループの反復回数を増やします。サーバーアクティビティの他のアクティビティに応じて、一方または他方の数値が大きくなる場合がありますが、一方のオペレーターが他方よりも一貫して優れていることは決してありません。

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;
12
Leigh Riffel

プログラマーは!=を使用します

DBAは<>を使用します

異なる実行プランがある場合は、各表記のクエリキャッシュまたは統計に違いがある可能性があります。しかし、私は実際にはそうではないと思います。

編集:

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

4
edze