【问题标题】:Oracle SQL: Pivot on multiple columns/fieldsOracle SQL:透视多个列/字段
【发布时间】:2018-08-28 16:43:31
【问题描述】:

我想创建一个表格,其中各个列的条目“透视”到列标题。该表用于报告目的 - 我的用户希望通过 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

根据我的研究,没有直接旋转多列的语法。有一些相关的问题(hereherehere),但我在那里找不到我的问题的直接答案。所以我想出了以下解决方案:

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')
)

这会产生所需的输出。但是,我不认为这是一个实用的解决方案,因为我必须输入的变量数量增长得太快(在我的真实数据集中,我想一次旋转更多字段,所有这些字段都有许多不同的值)。我知道存在dynamic pivots,但是我不确定这是否适用于 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 - 缺少表达式”错误。

【问题讨论】:

  • 请参考此链接stackoverflow.com/questions/38651147/… 并尝试使用 select * from table(pivot(Q'$ SELECT COMMODITY, CONCAT(CONCAT(MARKET,'_'),BID_ASK) AS MARKET_BID_ASK,价格从 tt $'))
  • Dynamic Sql 正是当您需要对用户在运行时定义的字段进行透视时要走的路。创建一个 VBA 函数,它将根据用户选择构建 Sql 命令文本。
  • 我认为您找到的“文档”不正确。如果您查阅official documentation,您会看到子查询选项仅允许与 XML 关键字结合使用。
  • 仅供大家参考,经过一些经验后,我可以看出我尝试过的基于 Excel 的解决方法(每列执行一个查询)也不是很实用。我想动态 SQL 确实是唯一的解决方案。如果我能找到时间以一种实用的方式让它工作,我会在这里发布解决方案......

标签: sql oracle microsoft-query


【解决方案1】:

您可以通过将列和值集括在括号中来对多列进行透视:

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 发现它比透视查询更容易处理。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-05
    • 1970-01-01
    • 2021-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多