在SQL中“透视”一个表(即交叉制表/交叉制表)

时间:2008-10-17 19:11:01

标签: sql database oracle pivot

我正在尝试从几个数据库表生成报告。简化版看起来像这样

Campaign
----------
CampaignID

Source
-----------------------
Source_ID | Campaign_ID

Content
---------------------------------------------------------
Content_ID | Campaign_ID | Content_Row_ID | Content_Value

报告需要像这样阅读:

CampaignID - SourceID - ContentRowID(Value(A)) - ContentRowID(Value(B))

其中ContentRowID(Value(A))表示“查找具有给定CampaignID的行,并且ContentRowId为”A“,然后获取该行的ContentValue”

基本上,我必须“旋转”(我认为这是正确的术语)行成为列......

这是一个Oracle 10g数据库......

有什么建议吗?

9 个答案:

答案 0 :(得分:2)

比尔卡尔文提到了这一点,但我认为应该非常明确地指出:

SQL不能满足您的要求,因此您获得的任何“解决方案”都将成为一个障碍。

如果您知道,当然,它总是会在Oracle 10上运行,那么确定,Walter Mitty的交叉制作可能会这样做。正确的方法是在查询和应用程序代码中使用最简单的排序顺序组合来正确排列。

  • 适用于其他数据库系统,
  • 它不会冒任何其他层崩溃的风险(我记得MySQL有例如> 255列的问题。你确定接口库应对以及db本身吗?)
  • (通常)没那么难。

如果您需要,您可以先询问Content_Row_ID,然后询问您需要的行,按CampaignIDContentRowID排序,这会给您每个(填充)单元格从左到右,逐行排列。


的PS。

有一堆现代人认为SQL应该拥有/做的东西就是不存在。这是一个,生成的范围是另一个,递归闭包,参数ORDER BY,标准化编程语言......列表继续。 (不过,诚然,ORDER BY

有一个技巧

答案 1 :(得分:1)

这是我第一次尝试。一旦我对Content表的内容有了更多的了解,就会出现细化。

首先,您需要一个临时表:

CREATE TABLE pivot (count integer);
INSERT INTO pivot VALUES (1);
INSERT INTO pivot VALUES (2);

现在我们已经准备好进行查询了。

SELECT campaignid, sourceid, a.contentvalue, b.contentvalue
FROM content a, content b, pivot, source
WHERE source.campaignid = content.campaignid
AND pivot = 1 AND a.contentrowid = 'A'
AND pivot = 2 AND b.contentrowid = 'B'

答案 2 :(得分:1)

如果您没有动态列数且数据集不是太大,则可以执行此操作...

SELECT CampaignID, SourceID, 
   (SELECT Content_Value FROM Content c 
      WHERE c.Campaign_ID=s.Campaign_ID 
      AND Content_Row_ID = 39100 
      AND rownum<=1) AS Value39100,
   (SELECT Content_Value FROM Content c 
      WHERE c.Campaign_ID=s.Campaign_ID 
      AND Content_Row_ID = 39200 
      AND rownum<=1) AS Value39200
FROM Source s;

为每个additonal Content_Row_ID重复子查询。

答案 3 :(得分:1)

要在标准SQL中执行此操作,您需要知道Content_Row_ID的所有不同值,并对每个不同的值执行连接。然后,每个不同的Content_Row_ID值需要一列。

SELECT CA.Campaign_ID, 
  C1.Content_Value AS "39100",
  C2.Content_Value AS "39200",
  C3.Content_Value AS "39300"
FROM Campaign CA
  LEFT OUTER JOIN Content C1 ON (CA.Campaign_ID = C1.Campaign_ID 
    AND C1.Content_Row_ID = 39100)
  LEFT OUTER JOIN Content C2 ON (CA.Campaign_ID = C2.Campaign_ID 
    AND C2.Content_Row_ID = 39200)
  LEFT OUTER JOIN Content C3 ON (CA.Campaign_ID = C3.Campaign_ID 
    AND C3.Content_Row_ID = 39300);

随着不同值的数量变大,此查询变得过于昂贵而无法高效运行。更简单地获取数据并在PL / SQL或应用程序代码中重新格式化它可能更容易。

答案 4 :(得分:1)

Bill Karwin和Anders Eurenius是正确的,没有任何解决方案是直截了当的,当预先不知道结果列值的数量时,也没有任何解决方案。 Oracle 11g确实使用the PIVOT operator对其进行了一些简化,但是这些列仍然必须提前知道,并且不符合您问题的10g标准。

答案 5 :(得分:0)

如果您需要动态数量的列,我不相信这可以在标准SQL中完成,唉,超出我的知识。但是Oracle有一些功能可以做到这一点。我找到了一些资源:

http://www.sqlsnippets.com/en/topic-12200.html

http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:124812348063#41097616566309

答案 6 :(得分:0)

如果您有“Oracle,完整参考”,请查找标题为“转动表格”的部分。这给出了执行数据透视的详细示例和说明,尽管我的版本并没有将其称为支点。

“旋转表格”的另一个术语是交叉制表。

用于执行交叉制表的最简单工具之一是MS Access。如果您有MS Access,并且可以建立从Access数据库到源表的表链接,那么您已经在那里了一半。

此时,您可以启动“查询向导”,并要求它为您构建交叉表查询。这真的就像回答向导问你的问题一样简单。这个解决方案的不幸之处在于,如果在SQL视图中查看结果查询,您将看到一些SQL的Access方言所特有的SQL,并且通常不能在其他平台上使用。

您也可以从Oracle网站下载一些简单的分析工具,并使用其中一种工具为您执行交叉制表。

再一次,如果你真的想在SQL中做到这一点,“Oracle,完整参考”应该会帮助你。

答案 7 :(得分:0)

如果你不知道前面的列数,只需带回正常的sql查询并使用我在此处列出的服务器端代码:Filling Datagrid And Sql Query

答案 8 :(得分:0)

我用这个SQL做了一个解决方案。我需要行是类的数量,列是每个每个classe的sumary,所以,第一列是行的sumary,每个ohters列是每个月的sumary,最后一行是sumary完整列的逐月。

祝你好运

Select DS.Cla,
Sum(case
when (Extract(year from DS.Data) =:intYear) then DS.PRE
else 0
end) as ToTal,
Sum(case
when (Extract(month from DS.Data) =1) then DS.PRE
else 0
end) as Jan,
Sum(case
when (Extract(month from DS.Data) =2) then DS.PRE
else 0
end) as FEV,
Sum(case
when (Extract(month from DS.Data) =3) then DS.PRE
else 0
end) as MAR,
Sum(case
when (Extract(month from DS.Data) =4) then DS.PRE
else 0
end) as ABR,
Sum(case
when (Extract(month from DS.Data) =5) then DS.PRE
else 0
end) as MAI,
Sum(case
when (Extract(month from DS.Data) =6) then DS.PRE
else 0
end) as JUN,
Sum(case
when (Extract(month from DS.Data) =7) then DS.PRE
else 0
end) as JUL,
Sum(case
when (Extract(month from DS.Data) =8) then DS.PRE
else 0
end) as AGO,
Sum(case
when (Extract(month from DS.Data) =9) then DS.PRE
else 0
end) as SETE,
Sum(case
when (Extract(month from DS.Data) =10) then DS.PRE
else 0
end) as OUT,
Sum(case
when (Extract(month from DS.Data) =11) then DS.PRE
else 0
end) as NOV,
Sum(case
when (Extract(month from DS.Data) =12) then DS.PRE
else 0
end) as DEZ
from Dados DS
Where DS.Cla > 0
And Extract(Year from DS.Data) = :intYear
group by DS.CLA

Union All

Select 0*count(DS.cla),  0*count(DS.cla),
Sum(case
when (Extract(month from DS.Data) =1) then DS.PRE
else 0
end) as JAN,
Sum(case
when (Extract(month from DS.Data) =2) then DS.PRE
else 0
end) as FEV,
Sum(case
when (Extract(month from DS.Data) =3) then DS.PRE
else 0
end) as MAR,
Sum(case
when (Extract(month from DS.Data) =4) then DS.PRE
else 0
end) as ABR,
Sum(case
when (Extract(month from DS.Data) =5) then DS.PRE
else 0
end) as MAI,
Sum(case
when (Extract(month from DS.Data) =6) then DS.PRE
else 0
end) as JUN,
Sum(case
when (Extract(month from DS.Data) =7) then DS.PRE
else 0
end) as JUL,
Sum(case
when (Extract(month from DS.Data) =8) then DS.PRE
else 0
end) as AGO,
Sum(case
when (Extract(month from DS.Data) =9) then DS.PRE
else 0
end) as SETE,
Sum(case
when (Extract(month from DS.Data) =10) then DS.PRE
else 0
end) as OUT,
Sum(case
when (Extract(month from DS.Data) =11) then DS.PRE
else 0
end) as NOV,
Sum(case
when (Extract(month from DS.Data) =12) then DS.PRE
else 0
end) as DEZ
from Dados DS
Where DS.Cla > 0
And Extract(Year from DS.Data) = :intYear