web-dev-qa-db-ja.com

SQLServerで高速に実行されるOracleで低速で実行されるSELECTクエリの最適化

Oracleで次のSQLステートメントを実行しようとしていますが、実行には時間がかかります。

SELECT orderID FROM tasks WHERE orderID NOT IN 
(SELECT DISTINCT orderID FROM tasks WHERE
 engineer1 IS NOT NULL AND engineer2 IS NOT NULL)

IN句にあるサブパーツだけを実行すると、Oracleで非常に高速に実行されます。

SELECT DISTINCT orderID FROM tasks WHERE
engineer1 IS NOT NULL AND engineer2 IS NOT NULL

Oracleでステートメント全体にこれほど長い時間がかかるのはなぜですか? SQL Serverでは、ステートメント全体がすばやく実行されます。

あるいは、使用すべきより単純な/異なる/より良いSQLステートメントはありますか?

問題に関する詳細:

  • 各注文は多くのタスクで構成されています
  • 各注文は割り当てられます(1つ以上のタスクにengineer1とengineer2が設定されます)、または注文の割り当てを解除できます(すべてのタスクのエンジニアフィールドにnull値があります)
  • 割り当てられていないすべてのorderIDを見つけようとしています。

違いが生じる場合に備えて、テーブルには最大12万行あり、注文ごとに3つのタスクがあるため、最大4万の異なる注文があります。

回答への回答:

  • SQLServerとOracleの両方で機能するSQLステートメントが望ましいです。
  • タスクには、orderIDとtaskIDのインデックスのみがあります。
  • NOT EXISTSバージョンのステートメントを試しましたが、キャンセルする前に3分以上実行されました。おそらく、ステートメントのJOINバージョンが必要ですか?
  • OrderID列を持つ「orders」テーブルもあります。しかし、元のSQLステートメントに含めないことで、質問を単純化しようとしていました。

元のSQLステートメントでは、サブクエリはSQLステートメントの最初の部分の各行に対して毎回実行されると思います-静的であり、1回だけ実行する必要がありますか?

実行中

ANALYZE TABLE tasks COMPUTE STATISTICS;

元のSQLステートメントの実行がはるかに高速になりました。

なぜこれをしなければならないのか、そしていつまた実行する必要があるのか​​、まだ興味がありますか?

統計は、さまざまな実行プランの効率を決定するために必要なOracleのコストベースのオプティマイザー情報を提供します。たとえば、テーブル内の行数、行の平均幅、列ごとの最大値と最小値、列ごとの個別の値の数などです。 、インデックスのクラスタリング係数など。

小さなデータベースでは、毎晩統計を収集してそのままにしておくジョブを設定できます。実際、これは10g未満のデフォルトです。大規模な実装の場合、通常、実行プランの安定性とデータの変更方法を比較検討する必要がありますが、これは難しいバランスです。

Oracleには、実行時に関連する統計を決定するためにテーブルをサンプリングするために使用される「動的サンプリング」と呼ばれる機能もあります。これは、サンプリングのオーバーヘッドが、長時間実行されるクエリの潜在的なパフォーマンスの向上よりも重要であるデータウェアハウスでより頻繁に使用されます。

12
RickL

多くの場合、このタイプの問題は、関連するテーブルを分析すると解消されます(したがって、Oracleはデータの分散についてより良いアイデアを持っています)

ANALYZE TABLE tasks COMPUTE STATISTICS;
9
hamishmcn

「IN」句は、Oracleではかなり遅いことが知られています。実際、Oracleの内部クエリオプティマイザは、「IN」を含むステートメントを適切に処理できません。 「EXISTS」を使用してみてください:

SELECT orderID FROM tasks WHERE orderID NOT EXISTS 
    (SELECT DISTINCT orderID FROM tasks WHERE
         engineer1 IS NOT NULL AND engineer2 IS NOT NULL)`print("code sample");`

注意:クエリが同じデータ結果を作成するかどうかを確認してください。

エディスは言う:おっと、クエリはうまく形成されていませんが、一般的な考え方は正しいです。 Oracleは、2番目の(内部)クエリの全表スキャンを実行し、結果を作成してから、最初の(外部)クエリと比較する必要があるため、速度が低下します。試してみてください

SELECT orderID AS oid FROM tasks WHERE NOT EXISTS 
    (SELECT DISTINCT orderID AS oid2 FROM tasks WHERE
         engineer1 IS NOT NULL AND engineer2 IS NOT NULL and oid=oid2)

または似たようなもの;-)

3
Georgi

代わりに結合を使用してみます

SELECT 
    t.orderID 
FROM 
    tasks  t
    LEFT JOIN tasks t1
        ON t.orderID =  t1.orderID
        AND t1.engineer1 IS NOT NULL 
        AND t1.engineer2 IS NOT NULL
WHERE
    t1.orderID IS NULL 

また、元のクエリが次のように指定されていると、おそらく理解しやすくなります。

SELECT orderID FROM orders WHERE orderID NOT IN 
(SELECT DISTINCT orderID FROM tasks WHERE
 engineer1 IS NOT NULL AND engineer2 IS NOT NULL)

(すべての注文がリストされた注文テーブルがあると仮定します)

次に、結合を使用して次のように書き換えることができます。

SELECT 
    o.orderID 
FROM 
    orders o
    LEFT JOIN tasks t
        ON o.orderID =  t.orderID
        AND t.engineer1 IS NOT NULL 
        AND t.engineer2 IS NOT NULL
WHERE
    t.orderID IS NULL 
3
kristof

いくつかの質問:

  • タスクにはいくつの行がありますか?
  • どのようなインデックスが定義されていますか?
  • テーブルは最近分析されましたか?

同じクエリを作成する別の方法は次のとおりです。

select orderid from tasks
minus
select orderid from tasks
where engineer1 IS NOT NULL AND engineer2 IS NOT NULL

ただし、クエリに「orders」テーブルが含まれることを期待します。

select orderid from ORDERS
minus
select orderid from tasks
where engineer1 IS NOT NULL AND engineer2 IS NOT NULL

または

select orderid from ORDERS
where orderid not in
( select orderid from tasks
  where engineer1 IS NOT NULL AND engineer2 IS NOT NULL
)

または

select orderid from ORDERS
where not exists
( select null from tasks
  where tasks.orderid = orders.orderid
  and   engineer1 IS NOT NULL OR engineer2 IS NOT NULL
)
2
Tony Andrews

私はTZQTZIOに同意します、私はあなたの質問を受け取りません。

クエリが理にかなっていると想定する場合は、いくつかの提案としてEXISTSを使用してみて、INを回避することをお勧めします。 INは必ずしも悪いわけではなく、実際にはEXISTSよりもパフォーマンスが優れていることを示す場合があります。

質問のタイトルはあまり役に立ちません。このクエリを1つのOracleデータベースに設定して、実行速度を遅くし、別のデータベースで高速に実行することができます。データベースがクエリ、オブジェクト統計、SYSスキーマ統計、パラメータをどのように解決するか、およびサーバーのパフォーマンスを決定する多くの要因があります。ここでは、SQLServerとOracleの違いは問題ではありません。

クエリの調整とパフォーマンスに関心があり、検索するGoogle用語の詳細を知りたい場合は、「OaktableOracle」と「Oraclejonathanlewis」を使用してください。

2
Ethan Post

「なぜこれをしなければならないのか、そしていつまた実行する必要があるのか​​、私はまだ興味がありますか?」

統計は、さまざまな実行プランの効率を決定するために必要なOracleのコストベースのオプティマイザー情報を提供します。たとえば、テーブル内の行数、行の平均幅、列ごとの最大値と最小値、列ごとの個別の値の数などです。 、インデックスのクラスタリング係数など。

小さなデータベースでは、毎晩統計を収集してそのままにしておくジョブを設定できます。実際、これは10g未満のデフォルトです。大規模な実装の場合、通常、実行プランの安定性とデータの変更方法を比較検討する必要がありますが、これは難しいバランスです。

Oracleには、実行時に関連する統計を決定するためにテーブルをサンプリングするために使用される「動的サンプリング」と呼ばれる機能もあります。これは、サンプリングのオーバーヘッドが、長時間実行されるクエリの潜在的なパフォーマンスの向上よりも重要であるデータウェアハウスでより頻繁に使用されます。

1
David Aldridge

何人かの人々はほぼ正しいSQLを持っていると思いますが、内部クエリと外部クエリの間の結合が欠落しています。
これを試して:

SELECT t1.orderID 
FROM   tasks t1
WHERE  NOT EXISTS
       (SELECT 1 
        FROM   tasks t2 
        WHERE  t2.orderID   = t1.orderID
        AND    t2.engineer1 IS NOT NULL 
        AND    t2.engineer2 IS NOT NULL)
1
AJ.

テーブル内の行のどの割合が「engineer1 IS NOT NULL AND engineer2 IS NOTNULL」」の条件を満たしていますか?

これにより、(大まかに)インデックスを使用して関連するorderidを取得する価値があるかどうかがわかります。

インデックス付けされていないケースを非常にうまく処理するクエリをOracleで作成する別の方法は、次のとおりです。

select distinct orderid
from
(
select orderid,
       max(case when engineer1 is null and engineer2 is null then 0 else 1)
          over (partition by orderid)
          as max_null_Finder
from   tasks
)
where max_null_Finder = 0
0
David Aldridge

Oracleオプティマイザは、MINUSステートメントを適切に処理します。 MINUSを使用してクエリを書き直すと、非常に高速に実行される可能性があります。

SELECT orderID FROM tasks
MINUS
SELECT DISTINCT orderID FROM tasks WHERE
 engineer1 IS NOT NULL AND engineer2 IS NOT NULL
0
JoshL

新しいテイク。

Iff

  • COUNT()関数はNULL値をカウントしません

および

  • noneのタスクのengineer1またはengineer2のいずれかが値に設定されているすべてのタスクのorderIDが必要です

その後これはあなたが望むことをするはずです:

SELECT orderID
FROM tasks
GROUP BY orderID
HAVING COUNT(engineer1) = 0 AND COUNT(engineer2) = 0

テストしてください。

0
tzot

私はΤΖΩΤΖΙΟΥとwearejimboにあなたの質問が...

SELECT DISTINCT orderID FROM Tasks 
WHERE Engineer1 IS NULL OR Engineer2 IS NULL;

SQL Serverについてはわかりませんが、null行がインデックスに含まれていないため、このクエリはインデックスを利用できません。これに対する解決策は、null値の行のみを含む関数ベースのインデックスを作成できるようにクエリを書き直すことです。これはNVL2で実行できますが、SQLServerに移植できない可能性があります。

最善の答えは、あなたの基準を満たすものではなく、プラットフォームごとにそのプラットフォームに最適な異なるステートメントを書くことだと思います。

0
Leigh Riffel

あなたのクエリはと同じではありません

SELECT orderID FROM tasks
WHERE engineer1 IS NOT NULL OR engineer2 IS NOT NULL

0
tzot

どうですか:

SELECT DISTINCT orderID FROM tasks t1 WHERE NOT EXISTS (SELECT * FROM tasks t2 WHERE t2.orderID=t1.orderID AND (engineer1 IS NOT NULL OR engineer2 IS NOT NULL));

私は最適化の第一人者ではありませんが、Oracleデータベースのいくつかのインデックスを見落としているかもしれません。

0
Mac

別のオプションは、マイナスを使用することです(MSSQLを除く)

SELECT orderID FROM tasks
MINUS
SELECT DISTINCT orderID FROM tasks WHERE engineer1 IS NOT NULL 
AND engineer2 IS NOT NULL
0
Vinko Vrsalovic