最適化の問題:複合クラスターキー、フラグ条件、およびインデックスのマージ


11

3つのテーブル:

product:列あり: ( a, g, ...a_lot_more... )

a: PK, clustered
g: bit-column

main:列あり: ( c, f, a, b, ...a_lot_more... )

c: PK, clustered
f: bit-column
(a, b): UQ 

lookup 列あり: ( a, b, c, i )

(a, b): PK, clustered
a: FK to product(a)
c: UQ, FK to main(c)
i: bit-column

結合に適したインデックスが見つかりません。

FROM  
    product
  JOIN 
    lookup
      ON  lookup.a = product.a  
  JOIN
    main
      ON  main.c = lookup.c 
WHERE 
      product.g = 1
  AND
      main.f = 1
  AND 
      lookup.i = 1
  AND lookup.b = 17

カバリングインデックスを試してみましたが、product (g, a, ...)使用されましたが、すばらしい結果は得られませんでした。

lookupテーブルのインデックスの組み合わせによっては、インデックスのマージを含む実行プランが作成され、以前のプランよりもわずかに効率が向上します。

私が見逃している明らかな組み合わせはありますか?

構造の再設計は役に立ちますか?

DBMSはMySQL 5.5であり、すべてのテーブルはInnoDBを使用しています。


テーブルサイズ:

product: 67K   ,  g applied:    64K 

main:   420K   ,  f applied:   190K

lookup:  12M   ,  b,i applied:  67K 

フィルター述部を結合に移動して、オプティマイザーがそれに賢明なことを行っているかどうかを確認してください。SQL Serverのオプティマイザが失敗するのを見たことがある。
ConcernedOfTunbridgeWells

製品テーブルからJOINしているものが何もないので、デカルト製品のように見えます。または私は何かを逃した???
RolandoMySQLDBA 2012

@RolandoMySQLDBA:その通りです。クエリを修正します。
ypercubeᵀᴹ

回答:


3

これは私を苦しめる...

以前にInnoDBで一時テーブルを使用する必要がありました。それらをフィルターで読み込み、インデックスを作成し、これらの一時テーブルを結合します。

私が考えている問題は、そのInnoDBにネストされた結合アルゴリズムしかない場合です。大人になったRDBMSクエリオプティマイザーには、さらに使用する必要があります。これは、InnoDBでデータウェアハウスタイプのロードを実行しようとすることに基づいています。

一時テーブルは、全体的な複雑さをMySQLクエリオプティマイザのレベルに引き下げます...


Thnx、私はそれを試してみます。数または行(基準が適用された後は、それほど大きくありません。それぞれ64K、67K、190K)。多分私はmainデータを非正規化することによって3つのテーブル()の1つを取り除くことを試みるべきlookupですか?
ypercubeᵀᴹ

1
@ypercube:非正規化すると行が広くなり、ページ密度が低くなる=その他の問題
gbn

3

デカルト積のように見えます。結合基準をやり直す

FROM  
    product
  JOIN 
    lookup
      ON  product.a = lookup.a  
  JOIN
    main
      ON  main.c = lookup.c 
WHERE 
      product.g = 1
  AND
      main.f = 1
  AND 
      lookup.i = 1
  AND lookup.b = 17

代替案

これは正統ではないように思えるかもしれませんが、おそらくSQL Anitpatternのようなにおいがしますが、ここでそれは...

FROM  
    product
JOIN 
    (
        SELECT * FROM lookup
        WHERE i=1 AND b=17
    ) lookup ON product.a = lookup.a  
JOIN
   main ON main.c = lookup.c 
WHERE 
    product.g = 1 AND main.f = 1

product.g = 1およびmain.f = 1をサブクエリに移動しませんでした。これらはビットフィールドであり、その時点でテーブルスキャンを実行するだけだからです。ビットフィールドがインデックスであったとしても、クエリオプティマイザーはそのようなインデックスを単に無視します。

もちろん、あなたが変更される可能性SELECT * FROM lookupSELECT a FROM lookupあなたのSELECTでから何かを必要としない場合lookup

おそらく、これが理にかなっている場合、ルックアップとメインの間のJOINにa、bを含める

FROM  
    product
  JOIN 
    lookup
      ON  product.a = lookup.a  
  JOIN
    main
      ON  main.a = lookup.a AND main.b = lookup.b
WHERE 
      product.g = 1
  AND
      main.f = 1
  AND 
      lookup.i = 1
  AND lookup.b = 17

またはバックCを入れて、(で3つの列にインデックスを3つの列に参加するmainlookup

FROM  
    product
  JOIN 
    lookup
      ON  product.a = lookup.a  
  JOIN
    main
      ON main.a = lookup.a
      AND main.b = lookup.b
      AND main.c = lookup.c
WHERE 
      product.g = 1
  AND
      main.f = 1
  AND 
      lookup.i = 1
  AND lookup.b = 17

Thnx。EXPLAINプランは異なりますが、パフォーマンスは同じです。
ypercubeᵀᴹ

何のカーディナリティmain.fproduct.g??? main.fproduct.gの値のカーディナリティーが1の場合、表の行の5%未満であり、索引付けされmain.fているproduct.g可能性があります。
RolandoMySQLDBA 2012

気にしないでください、彼らはすでに索引付けされています。main.fand のカーディナリティproduct.gが2の場合、これらのインデックスを破棄できます。
RolandoMySQLDBA 2012

使用されたテーブルのサイズと行で質問を編集しました(条件が適用された後)。
ypercubeᵀᴹ

私は質問を更新し、cではなくa、bにJOINするという提案をしました。それが別のEXPLAIN計画になるかどうかを確認してください
RolandoMySQLDBA 2012
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.