使用不带GROUP BY子句的聚合函数

时间:2018-02-21 14:19:42

标签: sql sql-server join group-by aggregate-functions

以前曾经问过,但我没有找到一个与我相似的案例。如果可能的话,这应该很简单。

这些是我的表格:

CARS
ID | COLOR | TYPE
1  | 'Red' | 'Coupe'
2  | 'Blue'| 'Type1'
3  | 'Red' | 'Type2'
...

CAR_PARTS
ID | CAR_ID | PRICERED | PRICEBLUE
1  | 1      | 100      | 200
2  | 1      | 200      | 300
3  | 2      | 400      | 200
4  | 3      | 200      | 500
5  | 2      | 100      | 900
...

CAR_PARTS.CAR_ID是绑定到ID表的CARS列的外键列,正如您可能已假设的那样。必须有一种方法可以让SELECT语句返回CARS列的TOTAL_PRICE列,其中所有PRICERED列都会添加到CARS.ID = CAR_PARTS.CAR_ID所在的位置如果CARS.COLOR='Red'为真且CARS.ID = CAR_PARTS.CAR_ID为真,则会添加true和'CARS.COLOR='Blue'以及所有'PRICEBLUE'列。

这样的事情:

SELECT C.ID, C.COLOR, C.TYPE,
SUM(CASE WHEN C.COLOR='Red' THEN P.PRICERED
         WHEN C.COLOR='Blue' THEN P.PRICEBLUE END) AS TOTAL
FROM CARS C
JOIN CAR_PARTS P ON C.ID = P.CAR_ID

或者,正如我所尝试的,基于类似的情况:

SELECT C.ID, C.COLOR, C.TYPE, P.TOTAL
FROM CARS C
  JOIN ( SELECT CAR_ID,
           SUM (CASE WHEN C.COLOR='Red' THEN PRICERED
             WHEN C.COLOR='Blue' THEN PRICEBLUE END) AS TOTAL
         FROM CAR_PARTS
         GROUP BY CAR_ID ) P ON C.ID = P.CAR_ID

显然,这些都不起作用。我明白为什么他们不工作。第一个与SQL不明确,另一个不知道'C'是什么。但是,如果我不希望找到解决方案,我不会在这里写一个问题。所以,我想在主GROUP BY语句中没有SELECT子句的情况下,如果GROUP BY子句中有JOIN子句,那就没关系,尽管不完美。谢谢:)。

修改:所需的输出格式

ID | COLOR | TYPE   | TOTAL
1  | 'Red' | 'Coupe'| 300
2  | 'Blue'| 'Type1'| 1100
3  | 'Red' | 'Type2'| 200

因此,TOTAL为300,因为COLOR'Red'CAR_ID=2COLOR='Blue'为1100,CAR_ID=3为200,{ {1}}。

编辑2:TL; DR(或不理解)

嗯,输出应该是整个COLOR='Red'表,但是添加了CARS列,该列是根据另一个TOTAL表计算的。 CAR_PARTS应检查汽车的SQL,并根据汽车的CAR_ID汇总所有PRICE值。我希望现在更清楚了。

3 个答案:

答案 0 :(得分:2)

好的,正如我在评论中所说的那样,你的表格设计中存在问题。你不应该像这样设计表格;它没有遵循普通形式,你最终会遇到像你这样的问题。

首先,让我们看看我们只有两种颜色:

CREATE TABLE Car (ID int IDENTITY(1,1),
                  Colour varchar(10),
                  [Type] varchar(10));
GO

CREATE TABLE CarPart (ID int IDENTITY(1,1),
                      CarID int,
                      PriceRed int,
                      PriceBlue int);

INSERT INTO Car (Colour, [Type])
VALUES ('Red','Coupe'),
       ('Blue','Type1'),
       ('Blue','Type2');

INSERT INTO CarPart (CarId, PriceRed, PriceBlue)
VALUES (1, 100, 200),
       (1, 200, 300),
       (2, 400, 200),
       (3, 200, 500),
       (2, 100, 900);

使用以下方法可以轻松实现您想要的结果:

SELECT C.ID,
       C.Colour,
       C.[Type],
       SUM(CASE C.Colour WHEN 'Red' THEN CP.PriceRed
                         WHEN 'Blue' THEN CP.PriceBlue END) AS Total
FROM Car C
     JOIN CarPart CP ON C.ID = CP.CarID
GROUP BY C.ID,
         C.Colour,
         C.[Type];

但是,正如你所说,可能有两种以上的颜色,如果有这意味着表CarPart中的另一列。所以,让我们这样做:

ALTER TABLE CarPart ADD PriceGreen int;
GO

INSERT INTO Car (Colour, [Type])
VALUES ('Green','Coupe');

INSERT INTO CarPart (CarId, PriceRed, PriceBlue, PriceGreen)
VALUES (4, 500, 200, 100),
       (4, 300, 700, 250);

好的,但现在我们先前的查询无法正常工作。因此,我们还必须更新Green的查询:

SELECT C.ID,
       C.Colour,
       C.[Type],
       SUM(CASE C.Colour WHEN 'Red' THEN CP.PriceRed
                         WHEN 'Blue' THEN CP.PriceBlue
                         WHEN 'Green' THEN CP.PriceGreen END) AS Total
FROM Car C
     JOIN CarPart CP ON C.ID = CP.CarID
GROUP BY C.ID,
         C.Colour,
         C.[Type];

真的不太好,是吗?你已经看到了问题。现在让我们为黑色添加另一列,只是为了让事情变得更加困难:

ALTER TABLE CarPart ADD PriceBlack int;
GO

INSERT INTO Car (Colour, [Type])
VALUES ('Black','Hatchback');

INSERT INTO CarPart (CarId, PriceRed, PriceBlue, PriceGreen, PriceBlack)
VALUES (5, 300, 300, 200, 200),
       (5, 500, 300, 300, 400);

现在这是一个真正的问题。我们还有多少种颜色?黄色,橙色,天蓝色,绿色,洋红色?金,白,银怎么样?我可以继续列出...无论哪种方式,这个设置都很糟糕。因此,我们需要动态地解决这个问题:

DECLARE @SQL nvarchar(MAX);

SET @SQL = N'
SELECT C.ID,
       C.Colour,
       C.[Type],
       SUM(CASE C.Colour ' + STUFF((SELECT DISTINCT NCHAR(10) + N'WHEN ''' + REPLACE(C.Colour,N'''',N'''''') + N''' THEN CP.' + QUOTENAME(N'Price' + C.Colour)
                                    FROM Car C
                                    FOR XML PATH(N'')),1,1,'') + N' END) AS Total
FROM Car C
     JOIN CarPart CP ON C.ID = CP.CarID
GROUP BY C.ID,
         C.Colour,
         C.[Type];';

PRINT @SQL;

EXEC sp_executesql @SQL;

现在,无论我们添加多少列,它都能正常工作。太好了!

这是一个小附带条件,这个依赖在你的桌子CarPart上,并且有适当的颜色列。如果表格'HotPink'中的颜色为Car,但表格PriceHotPink中没有列CarPart,那么这将会落空。如果这是一个问题,那么使用额外的逻辑定义进行评论,我会看到我能做些什么。

编辑:它还依赖于具有(完全)相同名称的列。如果表'Sky Blue'中的值Car,但列PriceSkyBlue,则此值也会导致动态SQL失败;因为Dynamic SQl将尝试从列[PriceSky Blue]返回数据。

无论哪种方式,您都需要重新考虑您的桌面设计。这不是一个可扩展的解决方案,它只会变得更糟糕"。

清理:

DROP TABLE CarPart;
DROP TABLE Car;

EDIT2:所以,没有GROUP BY并且没有Op现在所说的额外颜色也不会被添加......

SELECT C.ID,
       C.Colour,
       C.[Type],
       CASE C.Colour WHEN 'Red' THEN (SELECT SUM(PriceRed) FROM CarPart CP WHERE CP.CarID = C.ID)
                     ELSE (SELECT SUM(PRiceBlue) FROM CarPart CP WHERE CP.CarID = C.ID) END AS Total
FROM Car C;

但是,为什么你想这样做是超出我的。

答案 1 :(得分:1)

使用下面的代码

SELECT C.ID, C.COLOR, C.TYPE, 
       SUM(CASE WHEN C.COLOR='Red' THEN P.PRICERED 
                WHEN C.COLOR='Blue' THEN P.PRICEBLUE END) AS TOTAL 
FROM CARS C 
JOIN CAR_PARTS P 
  ON C.ID = P.CAR_ID 
group by C.ID, C.COLOR, C.TYPE

或使用APPLY

       SELECT C.ID, C.COLOR, C.TYPE, t.TOTAL
    FROM CARS C
      CROSS APPLY  ( SELECT CAR_ID,
               SUM (CASE WHEN C.COLOR='Red' THEN PRICERED
                 WHEN C.COLOR='Blue' THEN PRICEBLUE END) AS TOTAL
             FROM CAR_PARTS
             GROUP BY CAR_ID  P ON C.ID = P.CAR_ID
             )t

答案 2 :(得分:0)

我只需要使用OVER()CASE添加另一种更简单的方法(这可以帮助您避免使用其他子查询和GROUP BY)。

SELECT DISTINCT
    Car.ID, 
    Car.Color, 
    Car.Type,   
    SUM(CASE WHEN Car.Color = 'Red' THEN CP.PRICERED WHEN Car.Color = 'Blue' THEN CP.PRICEBLUE END) 
    OVER (PARTITION BY Car.ID ORDER BY Car.ID ASC) AS TOTAL 
FROM #CARS Car
JOIN #CAR_PARTS CP ON Car.ID =  CP.CAR_ID