web-dev-qa-db-ja.com

Oracle SQL:複数の列/フィールドでのピボット

さまざまな列のエントリが列ヘッダーに「ピボット」されるテーブルを作成したいと思います。この表はレポート用です-ユーザーはExcel経由で(Microsoft Queryを使用して)データをクエリしたいのですが、問題は、Excelでピボットを実行すると、中程度のサイズのデータ​​セット(約100kデータポイント)でもファイルが非実用的に大きくなり、遅くなることです。 )。

次の例を考えてみましょう。

CREATE TABLE tt
(   
  "COMMODITY" VARCHAR2(4000 BYTE), 
  "MARKET"    VARCHAR2(4000 BYTE), 
  "BID_ASK"   VARCHAR2(4000 BYTE), 
  "PRICE"     NUMBER
);

INSERT INTO tt VALUES ('Gold','US','Ask',1.1);
INSERT INTO tt VALUES ('Gold','US','Bid',1);
INSERT INTO tt VALUES ('Gold','EU','Ask',1.2);
INSERT INTO tt VALUES ('Gold','EU','Bid',1.1);
INSERT INTO tt VALUES ('Oil','US','Ask',11);
INSERT INTO tt VALUES ('Oil','US','Bid',10);
INSERT INTO tt VALUES ('Oil','EU','Ask',12);
INSERT INTO tt VALUES ('Oil','EU','Bid',11);

私が達成したい出力は次のようになります(正確な列ヘッダーはそれほど重要ではありません):

COMMODITY   'US_Bid'    'US_Ask'    'EU_Bid'    'EU_Ask'
Gold         1           1.1         1.1        1.2
Oil          10          11          11         12

これで、単一の列をピボットするのは簡単です。

SELECT * FROM
(
  SELECT * FROM tt
)
PIVOT
(
  SUM(PRICE)
  FOR MARKET IN ('US','EU')
)

それは与える:

COMMODITY   BID_ASK 'US'    'EU'
Gold        Bid      1      1.1
Oil         Bid      10     11
Oil         Ask      11     12
Gold        Ask      1.1    1.2

私の調査によると、複数の列を直接ピボットするための構文はありません。関連する質問がいくつかあります( ここここ または ここ )が、そこで私の問題に対する直接の答えを見つけることができませんでした。だから私は次の解決策を思いついた:

SELECT * FROM
(
  SELECT COMMODITY, CONCAT(CONCAT(MARKET,'_'),BID_ASK) AS MARKET_BID_ASK, PRICE FROM tt
)
PIVOT
(
  SUM(PRICE)
  FOR MARKET_BID_ASK IN ('US_Bid','US_Ask','EU_Bid','EU_Ask')
)

これにより、正確に目的の出力が生成されます。ただし、入力する必要のある変数の数が非常に速く増加するため、実用的な解決策とは考えていません(実際のデータセットでは、一度に多くのフィールドをピボットしたいので、すべてが多くの異なる値を持ちます)。 動的ピボット が存在することは知っていますが、これがExcelで機能するかどうかはわかりません。また、ユーザーが独自にクエリを定義するため、構文をできるだけ単純にしておきたいと思います。 (私は彼らが適応できるテンプレートクエリを提供したいだけです)。そこで、IN句のフィールド名をクエリしようとしました。

SELECT * FROM
(
  SELECT COMMODITY, CONCAT(CONCAT(MARKET,'_'),BID_ASK) AS MARKET_BID_ASK, PRICE FROM tt
)
PIVOT
(
  SUM(PRICE)
  FOR MARKET_BID_ASK IN 
  (
    SELECT DISTINCT CONCAT(CONCAT(MARKET,'_'),BID_ASK) AS MARKET_BID_ASK FROM tt
  )
)

サブクエリでLIKE条件を使用してすべての連結オプションをリストする必要がなくても、クエリされた変数を制約できるため、このようなソリューションは実用的であると思います。ただし、見つかった documentation によると、ここではサブクエリが有効であるはずなのに、このクエリで「ORA-00936-式がありません」というエラーが発生します。

5
Chris7b

列と値のセットを括弧で囲むことにより、複数の列をピボットできます。

SELECT * FROM
(
  SELECT * FROM tt
)
PIVOT
(
  SUM(PRICE)
  FOR (MARKET, BID_ASK)
  IN (('US', 'Bid') us_bid, ('US', 'Ask') us_ask, ('EU', 'Bid') eu_bid, ('EU', 'Ask') eu_ask)
);

COMMODITY      US_BID     US_ASK     EU_BID     EU_ASK
---------- ---------- ---------- ---------- ----------
Gold                1        1.1        1.1        1.2
Oil                10         11         11         12

ただし、クエリを解析するときは値のペアを知る必要があり、値の組み合わせが多い場合、これは適切にスケーリングされません。

あなたが疑ったように、あなたの唯一の選択肢は動的SQLです。ただし、ExcelにXMLピボットの結果を処理させることができない限り、これは不可能だと思います。動的SQLを使用すると、Excelがピボットクエリよりも処理しやすいと判断した場合に、クエリとピボットを実行して参照カーソルを返す関数を使用できます。

9
Alex Poole