2

我正在编写仪表板,我需要从 Microsoft SQL Server 中提取一些数据。

举个简单的例子,我有三个表,一个主类别表,两个表包含通过主/外键关系链接到类别表的值(蓝色和绿色值表)。

使用 Microsoft SQL Sever (t-sql),我希望合计(求和)两个值表中的值,按类别表中的常见类别分组。

类别表

CategoryID (PK) | CategoryName
1               | Square
2               | Circle

蓝表

BlueID (PK) | CategoryID (FK) | BlueValue | BlueMonth | BlueYear
1           | 1               | 10        | 6         | 2012
2           | 1               | 20        | 12        | 2012
3           | 2               | 5         | 6         | 2012
4           | 2               | 9         | 12        | 2012
5           | 1               | 12        | 6         | 2013
6           | 1               | 21        | 12        | 2013
7           | 2               | 4         | 6         | 2013
8           | 2               | 8         | 12        | 2013

绿桌

GreenID (PK)| CategoryID (FK) | GreenValue| GreenMonth| GreenYear
1           | 1               | 3         | 6         | 2012
2           | 1               | 6         | 12        | 2012
3           | 2               | 2         | 6         | 2012
4           | 2               | 7         | 12        | 2012
5           | 1               | 2         | 6         | 2013
6           | 1               | 5         | 12        | 2013
7           | 2               | 4         | 6         | 2013
8           | 2               | 8         | 12        | 2013

如果我使用下面的 SQL,我会得到我期望的结果。

SELECT
    [Category].[CategoryName],
    SUM([Green].[GreenValue]) AS [GreenTotal]
FROM
    [Category]
LEFT JOIN
    [Green] ON [Category].[CategoryID] = [Green].[CategoryID]
GROUP BY
    [Category].[CategoryName]

结果:

CategoryName | GreenTotal
Square       | 16
Triangle     | 21

但是,如果我添加 Blue 表来尝试获取 BlueValue 的总数,我明显不正确的 T-SQL 会给我带来意想不到的结果。

SELECT
    [Category].[CategoryName],
    SUM([Green].[GreenValue]) AS [GreenTotal],
    SUM([Blue].[BlueValue]) AS [BlueTotal]
FROM
    [Category]
LEFT JOIN
    [Green] ON [Category].[CategoryID] = [Green].[CategoryID]
LEFT JOIN
    [Blue] ON [Category].[CategoryID] = [Blue].[CategoryID]
GROUP BY
    [Category].[CategoryName]

不正确的结果:

CategoryName | GreenTotal | BlueTotal
Square       | 64         | 252
Triangle     | 84         | 104

结果似乎都相差了 4 倍,这是每个类别的每个值表中的总行数。

我的目标是看到以下结果:

CategoryName | GreenTotal | BlueTotal
Square       | 16         | 63
Triangle     | 21         | 26

如果有人能告诉我到底做错了什么,我会欣喜若狂?

谢谢,马克。

4

5 回答 5

4

在我看来,这样的事情最好用 APPLY 来完成。快速的性能,易于使用,并且在查询变化的情况下易于控制。

IE:

SELECT C.[CategoryName], G.[GreenTotal], B.[BlueTotal]
FROM [Category] C
OUTER APPLY (SELECT SUM([GreenValue]) AS [GreenTotal] FROM [Green] WHERE [CategoryID] = C.CategoryID) G
OUTER APPLY (SELECT SUM([BlueValue]) AS [BlueTotal] FROM [Blue] WHERE [CategoryID] = C.CategoryID) B
于 2013-11-13T14:31:40.233 回答
2

你得到的是笛卡尔积。您可以通过删除分组并查看数据来查看此效果。

例如; 如果您的绿色表包含 2 行,而您的蓝色表包含 4 行,则您的联接将返回总共 8 条记录。

为了解决这个问题,好吧,你就快到了。你已经得到了所有正确的部分,只是没有把它们放在一起。

假设以下查询返回正确的绿色结果:

SELECT CategoryID
     , Sum(GreenValue) As GreenTotal
FROM   Green
GROUP
    BY CategoryID

可以按照相同的方法检索蓝色的结果:

SELECT CategoryID
     , Sum(BueValue) As BlueTotal
FROM   Blue
GROUP
    BY CategoryID

现在我们有两个不同的结果是正确的,我们应该将这些结果加入到我们的类别表中:

SELECT Category.CategoryName
     , GreenSummary.GreenTotal
     , BlueSummary.BlueTotal
FROM   Category
 LEFT
  JOIN (    
        SELECT CategoryID
             , Sum(GreenValue) As GreenTotal
        FROM   Green
        GROUP
            BY CategoryID
       ) As GreenSummary
    ON GreenSummary.CategoryID = Category.CategoryID
 LEFT
  JOIN (    
        SELECT CategoryID
             , Sum(BlueValue) As BlueTotal
        FROM   Blue
        GROUP
            BY CategoryID
       ) As BlueSummary
    ON BlueSummary.CategoryID = Category.CategoryID
于 2013-11-13T14:25:19.310 回答
0

为每个总表创建一个查询。按类别分组,创建总和列并添加列 ID。

然后将此查询用作子查询并与主表进行左外连接。这可以为您提供预期的结果,并且在总和不可用时可以具有空值。您可以使用 isnull 函数将空值转换为 0。

于 2013-11-13T14:23:57.223 回答
0

我会先用 CTE 来总结它们。然后简单地将 2 加入到共同点上,每个只出现 1 次,即 CategoryName。这样你就不能得到笛卡尔积。我放入 isnull 语句是因为可能没有蓝色或绿色类别名称的结果。如果您不这样做,您的 CategoryName 可能会为空。

WITH GREENSUM as (
SELECT
    [Category].[CategoryName],
    SUM([Green].[GreenValue]) AS [GreenTotal]
FROM
    [Category]
LEFT JOIN
    [Green] ON [Category].[CategoryID] = [Green].[CategoryID]
GROUP BY
    [Category].[CategoryName]
),
WITH BLUESUM as (
SELECT
    [Category].[CategoryName],
    SUM([Blue].[BlueValue]) AS [BlueTotal]
FROM
    [Category]
LEFT JOIN
    [Blue] ON [Category].[CategoryID] = [Blue].[CategoryID]
GROUP BY
    [Category].[CategoryName])
SELECT isnull(GREENSUM.CategoryName, BLUESUM.CategoryName) as CategoryName, 
    GreenTotal, BlueTotal 
FROM [GREENSUM] 
FULL OUTER JOIN 
    [BLUESUM] ON [GREENSUM].CategoryName = [BLUESUM].CategoryName)
于 2013-11-13T14:48:04.783 回答
0

我也使用 CTE,发现它在眼睛上更容易 - 但对内部选择进行排名。

/*
create table Category ( CategoryId Integer, CategoryName nvarchar(50) )
create table Green ( CategoryId Integer, GreenValue Integer )
create table Blue ( CategoryId Integer, BlueValue Integer )

insert into Category VALUES (1,'Square'),(2,'Circle')
insert into Blue VALUES (1,10),(1,20),(2,5),(2,9),(1,12),(1,21),(2,4),(2,8)
insert into Green VALUES (1,3),(1,6),(2,2),(2,7),(1,2),(1,5),(2,4),(2,8)
*/

with CatSums(ColorRank, CategoryId, CategoryValue) as
(
  select 1, CategoryId, GreenValue from Green
  union all
  select 2, CategoryId, BlueValue  from Blue
)

select 
    C.CategoryName, 
    Sum(case when ColorRank = 1 then CategoryValue else 0 end) as GreenTotal,
    Sum(case when ColorRank = 2 then CategoryValue else 0 end) as BlueTotal
 from CatSums S left join Category C on C.CategoryId = S.CategoryId
group by C.CategoryName
  • 尽管我必须承认越来越喜欢 OUTER APPLY 解决方案。
于 2013-11-13T14:56:29.887 回答