MySQL:内部クエリで「ORDER BY」を使用してUNIONを最適化する


9

同じレイアウトの複数のテーブルで構成されるロギングシステムをセットアップしただけです。

データソースごとに1つのテーブルがあります。

ログビューアについて、私はしたいです

  • UNIONすべてのログテーブル
  • アカウントそれらをフィルタリングし
  • ソースを識別するための疑似列追加し
  • 時間順並べ替え
  • そして、ページ分割のためにそれらを制限します

すべてのテーブルにzeitpunktは、インデックス付きの日付/時刻列であるというフィールドが含まれています。

私の最初の試みは:

(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt AS zeit,
 'hp' AS source FROM is_log AS l WHERE l.account_id = 730)

UNION

(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt,
 'ig' AS source FROM ig_is_log AS l WHERE l.account_id = 730)

ORDER BY zeit DESC LIMIT 10;

両方のテーブルのすべての行がサブクエリによって返され、の後にソートされるため、オプティマイザはここでインデックスを使用できませんUNION

私の回避策は次のとおりです:

(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt AS zeit,
 'hp' AS source FROM is_log AS l WHERE l.account_id = 730
 ORDER BY l.zeitpunkt DESC LIMIT 10)

UNION

(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt,
 'ig' AS source FROM ig_is_log AS l WHERE l.account_id = 730
 ORDER BY l.zeitpunkt DESC LIMIT 10)

ORDER BY zeit DESC LIMIT 10;

両方のサブクエリはの前にすでにソートおよび制限されている必要があるため、クエリエンジンがここでインデックスを使用することを期待していましたUNION

私は本当にこれだと思っていEXPLAINましたが、クエリで実行すると、サブクエリがまだ両方のテーブルを検索していることがわかります。

EXPLAINingサブクエリ自体は私に必要な最適化を示していますがUNIONing、それらを一緒にはしていません。

私は何か見落としてますか?

サブクエリORDER BY内の句UNIONがなしで無視されることは知ってLIMITいますが、制限があります。

編集:
実際には、おそらくaccount_id条件のないクエリもあるでしょう。

テーブルはすでに存在し、データが入力されています。ソースによってはレイアウトが変更になる可能性があるので分割していきたいと思います。さらに、ログクライアントは、理由により異なる資格情報を使用します。

ログリーダーと実際のテーブルの間に一種のレイヤーを維持する必要があります。

以下は、クエリ全体と最初のサブクエリの実行プラン、およびテーブルレイアウトの詳細です。

https://gist.github.com/ca8fc1093cd95b1c6fc0


1
このための最良の指標は化合物(account_id, zeitpunkt)です。そのような指標はありますか?2番目に優れているのは(私は)シングルです(zeitpunkt)が、それを使用した場合の効率は、行がaccount_id=730表示される頻度に依存します。
ypercubeᵀᴹ

2
そしてなぜUNION DISTINCT?余分なID列があるため、結果はサブクエリ間で異なるため、強制的に並べ替えて区別する必要はありません。を使用しUNION ALLます。
ypercubeᵀᴹ

1
@ypercubeの提案に加えて、質問がありsourceます。列を追加して、これらのすべてのログを同じテーブルに置く方が良いのではないでしょうか。このようにして、UNIONsを回避し、すべてのデータでインデックスを使用できます。
dezso 2012

1
@ypercube実際には、おそらくaccount_id条件のないクエリも存在します。DISTINCTフラグは、以前の試行の遺存であるとの結果が常に異なりますので、実際には役に立たないと理由DISTINCTは dafualtの動作です。テーブルはすでに存在し、データが入力されています。とにかく、ソースによってはレイアウトが変わる場合があるので分けておきたい。さらに、ログクライアントは、理由により異なる資格情報を使用します。ログリーダーと実際のテーブルの間に一種のレイヤーを維持する必要があります。
Lukas 2012

OK、ただし変更してUNION ALL別の実行プランが生成されるかどうかを確認します。
ypercubeᵀᴹ

回答:


8

好奇心から、このバージョンを試すことができますか?サブクエリが個別に使用するのと同じインデックスを使用するようにオプティマイザをだます場合があります。

SELECT *
FROM
(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt AS zeit,
 'hp' AS source FROM is_log AS l WHERE l.account_id = 730
 ORDER BY l.zeitpunkt DESC LIMIT 10) 
    AS a

UNION ALL

SELECT *
FROM
(SELECT l.id, l.account_id, l.vnum, l.count, l.preis, l.zeitpunkt,
 'ig' AS source FROM ig_is_log AS l WHERE l.account_id = 730
 ORDER BY l.zeitpunkt DESC LIMIT 10)
    AS b

ORDER BY zeit DESC LIMIT 10;

私はまだあなたが持つことができる最高のインデックスは化合物だと思います(account_id, zeitpunkt)。それは10行を高速に生成し、トリックは必要ありません。


あなたの変更は望ましい結果をもたらすことが判明しました。ありがとう!余談ですが、今のところ、どちらのインデックスの方が良いかわかりません。両方を使用することもできます。ユーザー数とがどのようlog entries / userに拡大するかを確認する必要があります。
ルーカス

を使用したクエリと使用しないクエリが必要な場合はaccount_id=?、両方を保持してください。
ypercubeᵀᴹ

@ ypercube、+ 1これは非常に賢く、私の(同様の)状況でも機能しました!ユニオンクエリをダミーでラップすると、SELECT * FROMMySQLがインデックスを使用するようになるのはなぜですか?
dkamins 2013年

@dkamins:MySQLオプティマイザーはあまり賢くありません。通常、ここにのような派生テーブルがある場合(SELECT ...) AS a、他の派生テーブルとは別に、クエリ全体とは別に派生テーブルを評価および最適化しようとします。
ypercubeᵀᴹ

@Lukas、実際にはインデックスが使用されていることを確認する必要があるため、使用/追加force indexするとより良い解決策が得られます。
Pacerier、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.