Toptalに投稿されたこのサンプルインタビューの質問と回答がここに再現されているのを見つけました。しかし、私はコードを本当に理解していません。 UNION ALLはどのようにしてそのようなUNIION(個別)に変わることができますか?また、なぜこのコードは速いのですか?
質問
WHERE句を使用して重複を排除するUNIONALL(UNIONではない)を使用してSQLクエリを記述します。なぜあなたはこれをしたいのですか?回答を非表示にする次のようなクエリを実行することで、UNION ALLを使用して重複を回避し、UNION DISTINCT(実際にはUNIONと同じ)よりもはるかに高速に実行できます。
回答
_SELECT * FROM mytable WHERE a=X UNION ALL SELECT * FROM mytable WHERE b=Y AND a!=X
_
The key is the AND a!=X part. This gives you the benefits of the UNION (a.k.a., UNION DISTINCT) command, while avoiding much of its performance hit.
ただし、この例では、最初のクエリの列a
に条件があり、2番目のクエリの列b
に条件があります。これはおそらく、最適化が難しいクエリから来ています。
SELECT * FROM mytable WHERE a=X OR b=Y
このクエリは、単純なBツリーインデックスで最適化するのは困難です。エンジンは列a
のインデックスを検索しますか?または列b
?いずれにせよ、他の用語を検索するには、テーブルスキャンが必要です。
したがって、UNIONを使用して、それぞれ1つの用語に対して2つのクエリに分割するトリック。各サブクエリは、各検索語に最適なインデックスを使用できます。次に、UNIONを使用して結果を結合します。
ただし、b=Y
がa=X
を持つ行もあるため、2つのサブセットが重複する可能性があります。その場合、そのような行は両方のサブセットで発生します。したがって、重複除去を行う必要があります。そうしないと、最終結果にいくつかの行が2回表示されます。
SELECT * FROM mytable WHERE a=X
UNION DISTINCT
SELECT * FROM mytable WHERE b=Y
UNION DISTINCT
は、一般的な実装では行を並べ替えて重複を見つけるため、コストがかかります。 SELECT DISTINCT ...
を使用する場合と同じです。
また、結合している行の2つのサブセットに、両方のサブセットで多数の行が発生している場合は、さらに「無駄な」作業になるという認識もあります。削除する行がたくさんあります。
ただし、2セットの行がすでに区別されていることを保証できる場合は、重複を排除する必要はありません。つまり、重複がないことを保証する場合です。これに頼ることができれば、重複を排除することは常にノーオペレーションであるため、クエリはそのステップをスキップでき、したがってコストのかかる並べ替えをスキップできます。
重複しない行のサブセットを選択することが保証されるようにクエリを変更すると、それがメリットになります。
SELECT * FROM mytable WHERE a=X
UNION ALL
SELECT * FROM mytable WHERE b=Y AND a!=X
これらの2つのセットは重複しないことが保証されています。最初のセットにa=X
の行があり、2番目のセットにa!=X
の行がある場合、両方のセットに行はありません。
したがって、2番目のクエリはb=Y
の行のsomeのみをキャッチしますが、a=X AND b=Y
が最初のセットにすでに含まれている行をキャッチします。
したがって、クエリは、重複を生成せず、UNION DISTINCT
操作を必要とせずに、2つのOR
用語の最適化された検索を実現します。
私はそれがうまくいくと思います
select col1 From (
select row_number() over (partition by col1 order by col1) as b, col1
from (
select col1 From u1
union all
select col1 From u2 ) a
) x
where x.b =1
テーブルに一意の識別子(主キー)がある場合、質問は正しくなります。それ以外の場合、すべての選択で同じ行が多数返される可能性があります。
なぜ高速になるのかを理解するために、データベースがUNIONALLとUNIONを実行する方法を見てみましょう。
1つ目は、2つの独立したクエリの結果を単純に結合することです。これらのクエリは並行して処理し、1つずつクライアントに送信できます。
2つ目は参加+区別です。 2つのクエリからレコードを区別するには、dbはそれらすべてをメモリに保持する必要があります。メモリが十分でない場合、dbはそれらを一時テーブルに格納し、次に一意のテーブルを選択する必要があります。これは、パフォーマンスが低下する可能性がある場所です。 DBは非常にスマートで、識別アルゴリズムは適切に開発されていますが、結果セットが大きい場合は、とにかく問題になる可能性があります。
UNION ALL +追加のWHERE条件は、フィルタリング中にインデックスが使用される場合、より高速になる可能性があります。だから、ここでパフォーマンスの魔法。