12

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 を見つけようとしています。

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

回答に対する回答:

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

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

実行中

ANALYZE TABLE tasks COMPUTE STATISTICS;

元の SQL ステートメントの実行速度が大幅に向上しました。

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

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

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

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

4

18 に答える 18

10

多くの場合、関連するテーブルを分析すると、この種の問題は解消されます (そのため、Oracle はデータの分布についてより適切な考えを持っています)。

ANALYZE TABLE tasks COMPUTE STATISTICS;
于 2008-09-23T13:57:51.500 に答える
3

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

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 
于 2008-09-23T12:49:45.627 に答える
3

"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");`

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

Edith : おっと、クエリの形式が正しくありませんが、一般的な考え方は正しいです。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)

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

于 2008-09-23T11:55:15.380 に答える
2

TZQTZIO に同意します。質問は受け付けません。

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

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

クエリのチューニングとパフォーマンスに関心があり、検索する Google 用語の一部を詳しく知りたい場合は、「oak table oracle」と「oracle jonathan lewis」を検索してください。

于 2008-09-23T12:29:21.540 に答える
2

いくつかの質問:

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

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

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
)
于 2008-09-23T12:17:57.590 に答える
1

「なぜこれを実行しなければならないのか、また実行する必要があるかどうか、またいつ実行する必要があるのか​​、まだ知りたいです。」

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

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

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

于 2008-09-23T15:21:05.013 に答える
1

何人かの人々はほとんど正しい 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)
于 2008-09-23T13:30:31.220 に答える
0

新しいテイク。

場合:

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

  • どのタスクにもengineer1またはengineer2が値に設定されていないすべてのタスクのorderIDが必要です

次に、これはあなたが望むことをするはずです:

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

テストしてください。

于 2008-09-23T14:54:17.713 に答える
0

「engineer1 IS NOT NULL AND Engineer2 IS NOT NULL」という条件を満たしているテーブルの行の割合はどれくらいですか?

これは、インデックスを使用して関連する 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
于 2008-09-23T14:04:03.433 に答える
0

あなたのクエリは...

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

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

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

于 2008-10-12T17:56:08.170 に答える
0

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

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

?

于 2008-09-23T11:59:50.567 に答える
0

どうですか :

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 データベースのいくつかのインデックスを見落としているかもしれません。

于 2008-09-23T12:02:18.563 に答える
0

別のオプションは、MINUS (MSSQL では EXCEPT) を使用することです。

SELECT orderID FROM tasks
MINUS
SELECT DISTINCT orderID FROM tasks WHERE engineer1 IS NOT NULL 
AND engineer2 IS NOT NULL
于 2008-09-23T12:05:22.457 に答える
0

ORDERS テーブルを作成することにした場合は、それに ALLOCATED フラグを追加し、ビットマップ インデックスを作成します。このアプローチでは、フラグを最新の状態に保つためにビジネス ロジックを変更する必要もありますが、クエリは超高速になります。それは、アプリケーションにとってクエリがどれほど重要であるかによって異なります。

答えに関しては、この場合は単純であるほど良いです。サブクエリ、結合、distinct および group by は忘れてください。これらはまったく必要ありません。

于 2008-09-23T13:20:56.113 に答える
0

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

SELECT orderID FROM tasks
MINUS
SELECT DISTINCT orderID FROM tasks WHERE
 engineer1 IS NOT NULL AND engineer2 IS NOT NULL
于 2008-09-23T14:08:40.100 に答える
-1

Engineer1 列と Engineer2 列にインデックスがない場合は、常に SQL Server でテーブル スキャンを生成し、Oracle でそれと同等のものを生成することになります。

割り当てられていないタスクを持つ Orders だけが必要な場合は、以下が両方のプラットフォームで問題なく動作するはずですが、Tasks テーブルにインデックスを追加してクエリのパフォーマンスを向上させることも検討する必要があります。

SELECT DISTINCT orderID 
FROM tasks 
WHERE (engineer1 IS NULL OR engineer2 IS NULL)
于 2008-09-23T12:51:35.323 に答える
-1

これがあなたが望むものを与えると私が思う別のアプローチです:

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

HAVING 句に「AND」と「OR」のどちらが必要かわかりません。ビジネス ロジックによると、これら 2 つのフィールドは両方とも入力するか、両方とも NULL にする必要があるようです。これが保証されている場合は、engineer1 をチェックするだけに条件を減らすことができます。

元のクエリでは、orderID ごとに複数の行が返されると思いますが、私の場合は 1 つしか返されません。orderID のみを取得しているので、これで問題ないと思います。

于 2008-09-23T12:16:07.297 に答える
-2

Oracle では、サブクエリは「悪い」ものです。一般的には、結合を使用する方が適切です。

join を使用してサブクエリを書き換える方法に関する記事は次のとおりです 。 http://www.dba-oracle.com/sql/t_rewrite_subqueries_performance.htm

于 2008-09-23T12:13:04.417 に答える