4

我正在尝试从几个数据库表中生成报告。简化版是这样的

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 数据库...

有什么建议么?

4

9 回答 9

2

比尔卡尔文提到了这一点,但我认为这值得非常清楚地指出:

SQL 不能满足您的要求,因此您获得的任何“解决方案”都将是一个杂项。

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

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

如果需要,您可以Content_Row_ID先询问 s,然后询问您需要的任何行,按 , 排序CampaignIDContentRowID这将为您提供从左到右、逐行顺序的每个(填充的)单元格。


附言。

现代人认为SQL应该拥有/做的很多东西只是不存在。这是一个,生成的范围是另一个,递归闭包、参数化ORDER BY、标准化编程语言……不胜枚举。(虽然,诚然,有一个技巧ORDER BY

于 2008-10-18T01:32:39.790 回答
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'
于 2008-10-17T19:39:32.170 回答
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;

对每个附加的 Content_Row_ID 重复子查询。

于 2008-10-17T20:35:07.707 回答
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 或应用程序代码中重新格式化它可能更容易。

于 2008-10-17T20:35:29.280 回答
1

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

于 2008-10-18T02:12:53.907 回答
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

于 2008-10-17T20:09:30.863 回答
0

如果您有“Oracle, the Complete Reference”,请查找标题为“Turning a Table on Its Side”的部分。这给出了执行枢轴的详细示例和说明,尽管我拥有的版本并未将其称为枢轴。

“透视表”的另一个术语是交叉制表。

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

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

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

再一次,如果您真的想在 SQL 中执行此操作,“Oracle,完整参考”应该可以帮助您。

于 2008-10-17T20:23:05.453 回答
0

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

于 2008-12-03T12:58:46.890 回答
0

我用这个 SQL 做了一个解决方案。我需要行是类的数量,列是每个类按月的总和,所以,第一列是行的总和,每个其他列是每个月的总和,最后一行是总和逐月完成列。

祝你好运

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
于 2010-07-07T17:28:18.113 回答