web-dev-qa-db-ja.com

mysqlピボット/クロス集計クエリ

質問1:以下の構造とデータのテーブルがあります:

_app_id  transaction_id  mobile_no   node_id  customer_attribute  entered_value 
100     111             9999999999  1        Q1                  2                             
100     111             9999999999  2        Q2                  1                             
100     111             9999999999  3        Q3                  4                             
100     111             9999999999  4        Q4                  3                             
100     111             9999999999  5        Q5                  2                             
100     222             8888888888  4        Q4                  1                             
100     222             8888888888  3        Q3                  2                             
100     222             8888888888  2        Q2                  1                             
100     222             8888888888  1        Q1                  3                             
100     222             8888888888  5        Q5                  4                             
_

これらのレコードを以下の形式で表示したいと思います。

_app_id  |  transaction_id  | mobile     |  Q1  |  Q2  |  Q3  |  Q4 |  Q5  |
 100    |      111         | 9999999999 |   2  |   1  |   4  |  3  |  2   |
 100    |      222         | 8888888888 |   3  |   1  |   2  |  1  |  4   |
_

この表示を取得するには、クロス集計/ピボットクエリを使用する必要があることを知っています。このために私はそれについて私が持っている限られた知識に基づいてそれを試しました。以下は私の質問です:

_SELECT app_id, transaction_id, mobile_no,
  (CASE node_id WHEN 1 THEN entered_value ELSE '' END) AS user_input1,
  (CASE node_id WHEN 2 THEN entered_value ELSE '' END) AS user_input2,
  (CASE node_id WHEN 3 THEN entered_value ELSE '' END) AS user_input3,
  (CASE node_id WHEN 4 THEN entered_value ELSE '' END) AS user_input4,
  (CASE node_id WHEN 5 THEN entered_value ELSE '' END) AS user_input5
FROM trn_user_log 
GROUP BY app_id, transaction_id, mobile_no, node_id
_

そして、このクエリに基づいて、次のように表示されます。

_app_id  transaction_id  mobile_no   user_input1  user_input2  user_input3  user_input4  user_input5  
100     111             9999999999  2                                                                
100     111             9999999999               1                                                   
100     111             9999999999                            4                                      
100     111             9999999999                                         3                         
100     111             9999999999                                                      2            
100     222             8888888888  3                                                                
100     222             8888888888               1                                                   
100     222             8888888888                            2                                      
100     222             8888888888                                         1                         
100     222             8888888888                                                      4            
_

上記のように複数の行ではなく、単一の行のレコードを取得するためにクエリに加える必要のある適切な変更について、誰かが私を助けてくれますか?.

質問2:特定のフィールドの値を列の名前として取得する方法もあります。上記のように、ヘッダーとして_user_input1_、_user_input2_、...があります。その代わりに、_customer_attribute_の値を列のヘッダーとして使用したいと思います。

このために、私は以下のようにNAME_CONST(name,value)をチェックしました:

_SELECT app_id, transaction_id, mobile_no,
NAME_CONST(customer_attribute, (CASE node_id WHEN 1 THEN entered_value ELSE '' END))
FROM trn_user_log 
_

しかし、それはエラーを与えます

_Error Code : 1210 Incorrect arguments to NAME_CONST
_

ヘルプが必要です。

11
DarkKnightFan

@Johnの静的な回答はうまく機能しますが、変換する列の数が不明な場合は、プリペアドステートメントを使用して結果を取得することを検討します。

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'GROUP_CONCAT((CASE node_id when ',
      node_id,
      ' then entered_value else NULL END)) AS user_input',
      node_id
    )
  ) INTO @sql
FROM trn_user_log;


SET @sql = CONCAT('SELECT app_id, transaction_id, mobile_no, ', @sql, ' 
                  FROM trn_user_log 
                  GROUP BY app_id, transaction_id, mobile_no');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

SQL Fiddle with Demo を参照してください

あなたの2番目に関しては、あなたが何をしようとしているのか明確にしないでください。

18
Taryn

追加 GROUP_CONCATCASE句内

SELECT app_id, transaction_id, mobile_no,
  GROUP_CONCAT((CASE node_id WHEN 1 THEN entered_value ELSE NULL END)) AS user_input1,
  GROUP_CONCAT((CASE node_id WHEN 2 THEN entered_value ELSE NULL END)) AS user_input2,
  GROUP_CONCAT((CASE node_id WHEN 3 THEN entered_value ELSE NULL END)) AS user_input3,
  GROUP_CONCAT((CASE node_id WHEN 4 THEN entered_value ELSE NULL END)) AS user_input4,
  GROUP_CONCAT((CASE node_id WHEN 5 THEN entered_value ELSE NULL END)) AS user_input5
FROM trn_user_log 
GROUP BY app_id, transaction_id, mobile_no

SQLFiddleデモ

7
John Woo

@DarkKnightFan、これは私が取り組んでいたタスクにとって非常に役立つ質問でした。私は先に進み、@ bluefinのソリューションを変更して、2番目の質問を解決しました。次のコードは、クロスタブの結果の列見出しとしてcustomer_attributeの値を使用して、最初に要求された形式を生成します。

関連する変更は次のとおりです。

' then entered_value else NULL END)) AS user_input',
      node_id

これに:

' then entered_value else NULL END)) AS ''',
          customer_attribute,''''

完全なコード:

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'GROUP_CONCAT((CASE node_id when ',
      node_id,
      ' then entered_value else NULL END)) AS ''',
      customer_attribute,''''
    )
  ) INTO @sql
FROM trn_user_log;


SET @sql = CONCAT('SELECT app_id, transaction_id, mobile_no, ', @sql, ' 
                  FROM trn_user_log 
                  GROUP BY app_id, transaction_id, mobile_no');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

また、この問題を閲覧している他のユーザーの場合、クロス集計しようとしている値がたくさんあると、GROUP_CONCAT()のデフォルトの最大長が1024文字であるため、エラーが発生する可能性があります。増やすには、これを準備したステートメントの先頭に置きます。

SET SESSION group_concat_max_len = value; -- replace value with an int
1
Kolath