3

最近の質問で、StevieG はピボット テーブルに関する問題を解決する方法を教えてくれました。新しい問題は、ピボット テーブルでいくつかの条件を確認する必要があることです。最後のクエリを見てみましょう:

SELECT 
  c.id, 
  GROUP_CONCAT(if(d.name = 'p1', d.value, NULL)) AS 'p1', 
  GROUP_CONCAT(if(d.name = 'p2', d.value, NULL)) AS 'p2', 
  GROUP_CONCAT(if(d.name = 'p3', d.value, NULL)) AS 'p3', 
  GROUP_CONCAT(if(d.name = 'p4', d.value, NULL)) AS 'p4', 
  GROUP_CONCAT(if(d.name = 'p5', d.value, NULL)) AS 'p5', 
  GROUP_CONCAT(if(d.name = 'p6', d.value, NULL)) AS 'p6'
FROM container c
JOIN data d ON c.id = d.container
GROUP BY c.id

明らかに、WHERE 句を追加することはできません (たとえば、p5>30 かどうかを確認したい場合)。この問題を解決する 2 つの方法を見つけました。まず、派生テーブルでこれを変換します。

SELECT * FROM (
    SELECT 
      c.id, 
      GROUP_CONCAT(if(d.name = 'p1', d.value, NULL)) AS 'p1', 
      GROUP_CONCAT(if(d.name = 'p2', d.value, NULL)) AS 'p2', 
      GROUP_CONCAT(if(d.name = 'p3', d.value, NULL)) AS 'p3', 
      GROUP_CONCAT(if(d.name = 'p4', d.value, NULL)) AS 'p4', 
      GROUP_CONCAT(if(d.name = 'p5', d.value, NULL)) AS 'p5', 
      GROUP_CONCAT(if(d.name = 'p6', d.value, NULL)) AS 'p6'
    FROM container c
    JOIN data d ON c.id = d.container
    GROUP BY c.id
) WHERE p5>30

そして、私が見つけたもう1つの方法は、HAVING句を追加することです:

SELECT 
  c.id, 
  GROUP_CONCAT(if(d.name = 'p1', d.value, NULL)) AS 'p1', 
  GROUP_CONCAT(if(d.name = 'p2', d.value, NULL)) AS 'p2', 
  GROUP_CONCAT(if(d.name = 'p3', d.value, NULL)) AS 'p3', 
  GROUP_CONCAT(if(d.name = 'p4', d.value, NULL)) AS 'p4', 
  GROUP_CONCAT(if(d.name = 'p5', d.value, NULL)) AS 'p5', 
  GROUP_CONCAT(if(d.name = 'p6', d.value, NULL)) AS 'p6'
FROM container c
JOIN data d ON c.id = d.container
GROUP BY c.id
HAVING p5>30

問題はパフォーマンスについてです。私は 50,000 エントリのテスト データベースを使用していますが、本番環境では 100 万に達する可能性があります。最初の文 (p5>30 をチェックしていないもの) は、私の開発用コンピューター (キャッシュなし) で 1000 文を実行するのに 0'60 秒かかりますが、2 番目と 3 番目の文は同じことを行うのに 5 分以上かかります。

データ インデックスを使用しない暗黙的な派生テーブルの生成があることは理解していますが、これを最適化するためのオプションは何ですか?

4

2 に答える 2

2

は一意であるためdata(container, name)、を使用する必要はありませんGROUP_CONCAT。これはどうですか:

SELECT 
  c.id, 
  d_p1.value AS 'p1', 
  d_p2.value AS 'p2', 
  d_p3.value AS 'p3', 
  d_p4.value AS 'p4', 
  d_p5.value AS 'p5'
FROM container AS c
LEFT JOIN data AS d_p1 ON (d_p1.container = c.id AND d_p1.name = 'p1')
LEFT JOIN data AS d_p2 ON (d_p2.container = c.id AND d_p2.name = 'p2')
LEFT JOIN data AS d_p3 ON (d_p3.container = c.id AND d_p3.name = 'p3')
LEFT JOIN data AS d_p4 ON (d_p4.container = c.id AND d_p4.name = 'p4')
LEFT JOIN data AS d_p5 ON (d_p5.container = c.id AND d_p5.name = 'p5')
WHERE d_p5.value > 30

にインデックスがある場合data(container, name)、クエリは数秒で実行されます。

data.nameが数文字より長い場合(たとえば5文字)、おそらく。の代わりに代理(整数)キーを使用する必要がありdata.nameます。

于 2012-10-25T14:34:26.533 に答える
1

私はYakの試みに近いですが、「p5.value」がゼロより大きいエントリのみを探している場合は、「事前クエリ」としてP5を持つエントリのみを取得するように再構築します。100,000 のレコードがあり、20,000 だけが範囲 30 より大きい「P5.value」を持っている場合、最初にそれらだけを取得します...次に残りを結合します...また、「データ」テーブルにインデックスがあることを確認しますインデックスとしての「名前、値」...さらに、「コンテナ、名前」のインデックスを確認します

最初の事前クエリでは、1 つのコンテナーに適した P5 値が既に「連結」されており、結合の結果として他の値が取得されます。

select STRAIGHT_JOIN
      PreQuery.QualifiedContainer ID,
      coalesce( d_p1.Value, ' ' ) p1,
      coalesce( d_p2.Value, ' ' ) p2,
      coalesce( d_p3.Value, ' ' ) p3,
      coalesce( d_p4.Value, ' ' ) p4,
      PreQuery.P5Value  p5,
      coalesce( d_p5.Value, ' ' ) p6
   from
      ( select 
              JustP5.Container as QualifiedContainer,
              JustP5.Value as P5Value
           from
              Container JustP5
           where
                  JustP5.Name = 'p5'
              AND JustP5.Value > 30 
           group by
              JustP5.Container ) as PreQuery

         LEFT JOIN data AS d_p1 
            ON PreQuery.QualifiedContainer = d_p1.container
           AND d_p1.name = 'p1'

         LEFT JOIN data AS d_p2
            ON PreQuery.QualifiedContainer = d_p2.container
           AND d_p2.name = 'p2'

         LEFT JOIN data AS d_p3
            ON PreQuery.QualifiedContainer = d_p3.container
           AND d_p3.name = 'p3'

         LEFT JOIN data AS d_p4
            ON PreQuery.QualifiedContainer = d_p4.container
           AND d_p4.name = 'p4'

         LEFT JOIN data AS d_p6
            ON PreQuery.QualifiedContainer = d_p6.container
           AND d_p6.name = 'p6'

あなたが参照した他の質問に基づいて、私が思うに「グループ化」は必要ありません...特定のコンテナに対して特定の「名前/値」ペアのインスタンスが1回しかないため...私が間違っている場合、 COALESCE() を GROUP_CONCAT() に変更し、GROUP BY PreQuery.QualifiedContainer を追加するだけです。

于 2012-10-26T18:33:37.213 に答える