4

私は最近、コードの一部を Oracle データベースの本番環境に移行しました。そのコードをレビューした経験豊富な開発者の 1 人が、existsandnot existsステートメントが多すぎるため、それらを削除する方法があるはずだと述べましたが、彼がそれを使用する必要があり、それがどのように機能したかについてあまり覚えていませんでした。現在、ビジネスロジック/要件の変更に伴い、今後数年間で複数回変更される可能性が高いコードであるため、コードの一部をより保守しやすくするために戻っており、より保守しやすくしながら最適化を進めたいと考えていました。 .

私はそれを調べてみましたが、見つけることができるのは、置き換えnot innot exists実際の結果を返さないことに関する推奨事項だけです。

そのため、最適化するために何ができるか、またはオラクルが内部的に最適化するように/existsnot exists記述する方法があるかどうか疑問に思っています(おそらく私よりも優れた程度で)。existsnot exists

たとえば、次のように最適化するにはどうすればよいでしょうか。

UPDATE
    SCOTT.TABLE_N N
SET
    N.VALUE_1 = 'Data!'
WHERE
    N.VALUE_2 = 'Y'
    AND
    EXISTS
    (
        SELECT
            1
        FROM
            SCOTT.TABLE_Q Q
        WHERE
            N.ID = Q.N_ID
    )
    AND
    NOT EXISTS
    (
        SELECT
            1
        FROM
            SCOTT.TABLE_W W
        WHERE
            N.ID = W.N_ID
    )
4

3 に答える 3

9

あなたの発言は私にはまったく問題ないように思えます。

最適化タスクでは、パターンを考えないでください。(not) exists「悪くて遅い、(not) in超クールで速い」などと考えないでください。

データベースは各ステップでどのくらいの作業を行い、それをどのように測定できるでしょうか?

簡単な例:

- ありませんで:

23:59:41 HR@sandbox> alter system flush buffer_cache;

System altered.

Elapsed: 00:00:00.03
23:59:43 HR@sandbox> set autotrace traceonly explain statistics
23:59:49 HR@sandbox> select country_id from countries where country_id not in (select country_id from locations);

11 rows selected.

Elapsed: 00:00:00.02

Execution Plan
----------------------------------------------------------
Plan hash value: 1748518851

------------------------------------------------------------------------------------------
| Id  | Operation              | Name            | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |                 |     1 |     6 |     4   (0)| 00:00:01 |
|*  1 |  FILTER                |                 |       |       |            |          |
|   2 |   NESTED LOOPS ANTI SNA|                 |    11 |    66 |     4  (75)| 00:00:01 |
|   3 |    INDEX FULL SCAN     | COUNTRY_C_ID_PK |    25 |    75 |     1   (0)| 00:00:01 |
|*  4 |    INDEX RANGE SCAN    | LOC_COUNTRY_IX  |    13 |    39 |     0   (0)| 00:00:01 |
|*  5 |   TABLE ACCESS FULL    | LOCATIONS       |     1 |     3 |     3   (0)| 00:00:01 |
------------------------------------------------------------------------------------------

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

   1 - filter( NOT EXISTS (SELECT 0 FROM "LOCATIONS" "LOCATIONS" WHERE
              "COUNTRY_ID" IS NULL))
   4 - access("COUNTRY_ID"="COUNTRY_ID")
   5 - filter("COUNTRY_ID" IS NULL)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         11  consistent gets
          8  physical reads
          0  redo size
        446  bytes sent via SQL*Net to client
        363  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         11  rows processed

- 存在しません

23:59:57 HR@sandbox> alter system flush buffer_cache;

System altered.

Elapsed: 00:00:00.17
00:00:02 HR@sandbox> select country_id from countries c where not exists (select 1 from locations l where l.country_id = c.country_id );

11 rows selected.

Elapsed: 00:00:00.30

Execution Plan
----------------------------------------------------------
Plan hash value: 840074837

-------------------------------------------------------------------------------------
| Id  | Operation         | Name            | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                 |    11 |    66 |     1   (0)| 00:00:01 |
|   1 |  NESTED LOOPS ANTI|                 |    11 |    66 |     1   (0)| 00:00:01 |
|   2 |   INDEX FULL SCAN | COUNTRY_C_ID_PK |    25 |    75 |     1   (0)| 00:00:01 |
|*  3 |   INDEX RANGE SCAN| LOC_COUNTRY_IX  |    13 |    39 |     0   (0)| 00:00:01 |
-------------------------------------------------------------------------------------

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

   3 - access("L"."COUNTRY_ID"="C"."COUNTRY_ID")


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          5  consistent gets
          2  physical reads
          0  redo size
        446  bytes sent via SQL*Net to client
        363  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         11  rows processed

この例では、NOT IN で 2 倍のデータベース ブロックを読み取り、より複雑なフィルタリングを実行します。

于 2013-04-25T16:09:54.147 に答える
2

EXISTS または NOT EXISTS が必要な場合は、使用を避ける理由はありません。あなたが与えた例では、それはおそらくまさにあなたが使いたいものです。

典型的なジレンマは、IN/NOT IN または EXISTS/NOT EXISTS を使用するかどうかです。それらはまったく異なって評価され、特定の状況に応じて、より高速または低速になる場合があります。

おそらく必要以上の詳細については、こちらを参照してください。

于 2013-04-25T15:39:50.620 に答える