从表中选择多个列,但逐个分组

时间:2014-01-19 14:08:49

标签: sql group-by

表名是“OrderDetails”,列如下:

OrderDetailID || ProductID || ProductName || OrderQuantity

我正在尝试选择多个列和Group By ProductID,同时使用OrderQuantity的SUM。

 Select ProductID,ProductName,OrderQuantity Sum(OrderQuantity)
 from OrderDetails Group By ProductID

但当然这段代码会出错。我必须添加其他列名称到分组,但这不是我想要的,因为我的数据有很多项目,所以结果是出乎意料的。

示例数据查询:

ProductDetails中的ProductID,ProductName,OrderQuantity

结果如下:

 ProductID     ProductName    OrderQuantity
    1001          abc               5
    1002          abc               23    (ProductNames can be same)
    2002          xyz               8
    3004          ytp               15
    4001          aze               19
    1001          abc               7     (2nd row of same ProductID)

预期结果:

 ProductID     ProductName    OrderQuantity
    1001          abc               12    (group by productID while summing)
    1002          abc               23
    2002          xyz               8
    3004          ytp               15
    4001          aze               19

如何选择多列和Group By ProductID列,因为ProductName不是唯一的?

这样做时,也可以得到OrderQuantity列的总和。

11 个答案:

答案 0 :(得分:45)

当我选择多列时,我使用此技巧分组一列:

lst <- list(blue = blue, Turq = Turq, Indigo = Indigo)
lst
# $blue
# [1] "Ti" "Ka" "Vv" "Cn"

# $Turq
# [1] "Cc" "Wx" "Oq" "Yt"

# $Indigo
# [1] "Gb" "Ap"

stack(lst)
#   values    ind
#1      Ti   blue
#2      Ka   blue
#3      Vv   blue
#4      Cn   blue
#5      Cc   Turq
#6      Wx   Turq
#7      Oq   Turq
#8      Yt   Turq
#9      Gb Indigo
#10     Ap Indigo

这很有效。

答案 1 :(得分:10)

您的数据

DECLARE @OrderDetails TABLE 
(ProductID INT,ProductName VARCHAR(10), OrderQuantity INT)

INSERT INTO @OrderDetails VALUES
(1001,'abc',5),(1002,'abc',23),(2002,'xyz',8),
(3004,'ytp',15),(4001,'aze',19),(1001,'abc',7)

<强>查询

 Select ProductID, ProductName, Sum(OrderQuantity) AS Total
 from @OrderDetails 
 Group By ProductID, ProductName  ORDER BY ProductID

<强>结果

╔═══════════╦═════════════╦═══════╗
║ ProductID ║ ProductName ║ Total ║
╠═══════════╬═════════════╬═══════╣
║      1001 ║ abc         ║    12 ║
║      1002 ║ abc         ║    23 ║
║      2002 ║ xyz         ║     8 ║
║      3004 ║ ytp         ║    15 ║
║      4001 ║ aze         ║    19 ║
╚═══════════╩═════════════╩═══════╝

答案 2 :(得分:4)

你可以试试这个:

Select ProductID,ProductName,Sum(OrderQuantity) 
 from OrderDetails Group By ProductID, ProductName

您只需要Group By子句中没有聚合函数的Select列。因此,在这种情况下,您只能使用Group By ProductID和ProductName。

答案 3 :(得分:2)

    WITH CTE_SUM AS (
      SELECT ProductID, Sum(OrderQuantity) AS TotalOrderQuantity 
      FROM OrderDetails GROUP BY ProductID
    )
    SELECT DISTINCT OrderDetails.ProductID, OrderDetails.ProductName, OrderDetails.OrderQuantity,CTE_SUM.TotalOrderQuantity 
    FROM 
    OrderDetails INNER JOIN CTE_SUM 
    ON OrderDetails.ProductID = CTE_SUM.ProductID

请检查一下是否有效。

答案 4 :(得分:1)

== EDIT ==

我再次检查了你的问题并得出结论,这是不可能的。

ProductName不是唯一的,它必须是Group By的一部分或从您的结果中排除。

例如,如果Group By只有ProductID?

,SQL会如何向您展示这些结果?
ProductID | ProductName | OrderQuantity 
---------------------------------------
1234      | abc         | 1
1234      | def         | 1
1234      | ghi         | 1
1234      | jkl         | 1

答案 5 :(得分:1)

mysql GROUP_CONCAT函数可以帮助https://dev.mysql.com/doc/refman/8.0/en/group-by-functions.html#function_group-concat

SELECT ProductID, GROUP_CONCAT(DISTINCT ProductName) as Names, SUM(OrderQuantity)
FROM OrderDetails GROUP BY ProductID

这将返回:

ProductID     Names          OrderQuantity
1001          red            5
1002          red,black      6
1003          orange         8
1004          black,orange   15

与@Urs Marian的想法类似的想法在这里发布了https://stackoverflow.com/a/38779277/906265

答案 6 :(得分:1)

在我看来,这是一个严重的语言缺陷,使SQL比其他语言落后几年。这是我难以置信的hack解决方法。这是一个总的麻烦,但总能奏效。

在我开始之前,我想提请注意@Peter Mortensen的答案,我认为这是正确的答案。我之所以执行以下操作的唯一原因是,由于大多数SQL实现的连接操作都异常缓慢,并迫使您中断“不要重复自己”操作。我需要快速填充查询内容。

这也是一种古老的做事方式。 STRING_AGGSTRING_SPLIT更加干净。我再次这样做是因为它始终有效。

-- remember Substring is 1 indexed, not 0 indexed
SELECT ProductId
  , SUBSTRING (
      MAX(enc.pnameANDoq), 1, CHARINDEX(';', MAX(enc.pnameANDoq)) - 1
    ) AS ProductName
  , SUM ( CAST ( SUBSTRING (
      MAX(enc.pnameAndoq), CHARINDEX(';', MAX(enc.pnameANDoq)) + 1, 9999
    ) AS INT ) ) AS OrderQuantity
FROM (
    SELECT CONCAT (ProductName, ';', CAST(OrderQuantity AS VARCHAR(10)))
      AS pnameANDoq, ProductID
    FROM OrderDetails
  ) enc
GROUP BY ProductId

或用简明语言:

  • 将除一个字段以外的所有内容粘贴到一个带有您将不会使用的分度符的字符串中
  • 使用子字符串将数据分组后提取

在性能方面,我一直使用字符串优于bigints之类的字符串。至少使用microsoft和oracle子字符串是一种快速操作。

这避免了在使用MAX()时遇到的问题,当您在多个字段上使用MAX()时,它们不再一致并且来自不同的行。在这种情况下,可以保证您的数据完全按照您要求的方式粘在一起。

要访问第3或第4个字段,您需要嵌套的子字符串,“在第一个分号后寻找第二个”。这就是为什么STRING_SPLIT更好的原因。

注意:虽然超出了您的问题范围,但是当您处于相反的情况并且要对组合键进行分组时,这尤其有用,但是您不希望显示所有可能的排列,也就是说,您想公开' foo”和“ bar”作为组合键,但要按“ foo”分组

答案 7 :(得分:1)

我有与OP类似的问题。然后,我看到了@Urs Marian的回答,这很有帮助。 但是此外,我一直在寻找的是,当一列中有多个值并将它们分组时,如何获取上次提交的值(例如,按日期/ ID列排序)。

示例:

我们具有以下表格结构:

CREATE TABLE tablename(
    [msgid] [int] NOT NULL,
    [userid] [int] NOT NULL,
    [username] [varchar](70) NOT NULL,
    [message] [varchar](5000) NOT NULL
) 

现在表中至少有两个数据集:

+-------+--------+----------+---------+
| msgid | userid | username | message |
+-------+--------+----------+---------+
|     1 |      1 | userA    | hello   |
|     2 |      1 | userB    | world   |
+-------+--------+----------+---------+

因此,如果相同的用户标识具有不同的用户名值,则以下SQL脚本确实可以工作(在MSSQL上已选中)以对其进行分组。在下面的示例中,将显示具有最高msgid的用户名:

SELECT m.userid, 
(select top 1 username from table where userid = m.userid order by msgid desc) as username,
count(*) as messages
FROM tablename m
GROUP BY m.userid
ORDER BY count(*) DESC

答案 8 :(得分:0)

您可以尝试以下查询。我假设您的所有数据都有一个表格。

SELECT OD.ProductID, OD.ProductName, CalQ.OrderQuantity
FROM (SELECT DISTINCT ProductID, ProductName
      FROM OrderDetails) OD
INNER JOIN (SELECT ProductID, OrderQuantity SUM(OrderQuantity)
            FROM OrderDetails
            GROUP BY ProductID) CalQ
ON CalQ.ProductID = OD.ProductID

答案 9 :(得分:0)

我只是想添加一种更有效,更通用的方式来解决此类问题。 主要思想是使用子查询。

按分组依据,并在表的ID上加入相同的表。

您的情况更具体,因为您的productId不是不唯一,因此有两种解决方法。

我将从更具体的解决方案开始: 由于您的productId 不是唯一,因此,我们需要执行一个额外的步骤,即在对分组进行分组并进行子查询后选择DISCTINCT产品ID,如下所示:

WITH CTE_TEST AS (SELECT productId, SUM(OrderQuantity) Total
                    FROM OrderDetails
                    GROUP BY productId)
SELECT DISTINCT(OrderDetails.ProductID), OrderDetails.ProductName, CTE_TEST.Total
FROM OrderDetails 
INNER JOIN CTE_TEST ON CTE_TEST.ProductID = OrderDetails.ProductID

这完全返回了预期的结果

 ProductID     ProductName         Total
    1001          abc               12    
    1002          abc               23
    2002          xyz               8
    3004          ytp               15
    4001          aze               19

但是,这里有一种更简洁的方法。我猜想ProductId是产品表的外键,并且我猜该表中应该有OrderId 主键(唯一)。

在这种情况下,仅对一个分组时,只需执行几个步骤即可包括多余的列。将与以下相同解决方案

让我们以这个t_Value表为例:

enter image description here

如果我想按描述分组并显示所有列。

我要做的就是:

  1. 使用您的GroupBy列和COUNT条件创建WITH CTE_Name子查询
  2. 从值表中选择所有(或要显示的内容),并从CTE中选择总计
  3. INNER JOIN在ID( 主键或唯一约束 )列上具有CTE

就是这样!

这是查询

WITH CTE_TEST AS (SELECT Description, MAX(Id) specID, COUNT(Description) quantity 
                    FROM sch_dta.t_value
                    GROUP BY Description)
SELECT sch_dta.t_Value.*, CTE_TEST.quantity 
FROM sch_dta.t_Value 
INNER JOIN CTE_TEST ON CTE_TEST.specID = sch_dta.t_Value.Id

结果如下:

enter image description here

希望这会有所帮助!

HK

答案 10 :(得分:-3)

SELECT ProductID, ProductName, OrderQuantity, SUM(OrderQuantity) FROM OrderDetails WHERE(OrderQuantity) IN(SELECT SUM(OrderQuantity) FROM OrderDetails GROUP BY OrderDetails) GROUP BY ProductID, ProductName, OrderQuantity;

我使用上述解决方案来解决Oracle12c中的类似问题。