web-dev-qa-db-ja.com

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

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

データソースごとに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それらを一緒にすると、そうではありません。

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

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

編集:
実際には、account_id条件のないクエリもおそらく存在します。

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

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

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

https://Gist.github.com/ca8fc1093cd95b1c6fc

9
Lukas

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

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行を高速に生成し、トリックは必要ありません。

7
ypercubeᵀᴹ