表名是“OrderDetails”,列如下:
OrderDetailID || ProductID || ProductName || OrderQuantity
我正在尝试选择多个列并按 ProductID 分组,同时具有 OrderQuantity 的总和。
Select ProductID,ProductName,OrderQuantity Sum(OrderQuantity)
from OrderDetails Group By ProductID
但是,这段代码当然会出错。我必须添加其他列名来分组,但这不是我想要的,因为我的数据有很多项目,所以结果是出乎意料的。
样本数据查询:
来自 OrderDetails 的 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
由于 ProductName 不是唯一的,如何选择多个列和 Group By ProductID 列?
在这样做的同时,还要得到 OrderQuantity 列的总和。
当我选择多列时,我使用此技巧按一列分组:
SELECT MAX(id) AS id,
Nume,
MAX(intrare) AS intrare,
MAX(iesire) AS iesire,
MAX(intrare-iesire) AS stoc,
MAX(data) AS data
FROM Produse
GROUP BY Nume
ORDER BY Nume
这行得通。
我只是想添加一种更有效、更通用的方法来解决这类问题。主要思想是关于使用子查询。
做你的分组,并在表的 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
是 products 表的外键,我猜这个表中应该有 OrderId
primary key (唯一)。
在这种情况下,只需几个步骤即可在仅对一个进行分组时包含额外的列。这将是与以下相同的解决方案
我们以这个 t_Value
表为例:
https://i.stack.imgur.com/9SCl5.png
如果我想按描述分组并显示所有列。
我所要做的就是:
使用您的 GroupBy 列和 COUNT 条件创建 WITH CTE_Name 子查询 从值表中选择所有(或您要显示的任何内容),并从 CTE 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
结果如下:
https://i.stack.imgur.com/HtIlC.png
您的数据
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 ║
╚═══════════╩═════════════╩═══════╝
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
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
请检查这是否有效。
你可以试试这个:
Select ProductID,ProductName,Sum(OrderQuantity)
from OrderDetails Group By ProductID, ProductName
您只需在 Select
子句中Group By
不附带聚合函数的列。因此,在这种情况下,您可以只使用 Group By
ProductID 和 ProductName。
您可以尝试以下查询。我假设您的所有数据都有一个表。
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
在我看来,这是一个严重的语言缺陷,使 SQL 落后于其他语言数年。这是我令人难以置信的 hacky 解决方法。这是一个完全的kludge,但它总是有效的。
在此之前,我想提请注意@Peter Mortensen 的答案,我认为这是正确的答案。我这样做的唯一原因是因为大多数 SQL 实现的连接操作非常慢,并迫使你打破“不要重复自己”。我需要我的查询快速填充。
这也是一种古老的做事方式。 STRING_AGG 和 STRING_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 的 substring 是一个快速的操作。
这避免了您在使用 MAX() 时遇到的问题,当您在多个字段上使用 MAX() 时,它们不再一致并且来自不同的行。在这种情况下,可以保证您的数据完全按照您要求的方式粘合在一起。
要访问第三个或第四个字段,您需要嵌套子字符串,“在第一个分号之后查找第二个”。这就是为什么 STRING_SPLIT 可用时更好的原因。
注意:虽然超出了您的问题范围,但当您处于相反的情况并且您正在对组合键进行分组但不希望显示所有可能的排列时,这尤其有用,即您想要公开 'foo' 和'bar' 作为组合键,但想按 'foo' 分组
==编辑==
我再次检查了您的问题并得出结论,这是无法完成的。
ProductName 不是唯一的,它必须是 Group By
的一部分或从您的结果中排除。
例如,如果您 Group By
只有 ProductID,SQL 将如何向您呈现这些结果?
ProductID | ProductName | OrderQuantity
---------------------------------------
1234 | abc | 1
1234 | def | 1
1234 | ghi | 1
1234 | jkl | 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 |
+-------+--------+----------+---------+
因此,如果相同的用户 ID 具有不同的用户名值,以下 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
获得所需结果的优雅方法是通过以下方式使用“over (partion by)” sql 子句:
SELECT ProductID,ProductName,OrderQuantity
,SUM(OrderQuantity) OVER(PARTITION BY ProductID) AS 'Total'
--,AVG(OrderQuantity) OVER(PARTITION BY ProductID) AS 'Avg'
--,COUNT(OrderQuantity) OVER(PARTITION BY ProductID) AS 'Count'
--,MIN(OrderQuantity) OVER(PARTITION BY ProductID) AS 'Min'
--,MAX(OrderQuantity) OVER(PARTITION BY ProductID) AS 'Max'
FROM OrderDetails
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中类似的问题。
max()
放在每个未分组的列周围,放置as ___
以将其重命名为您希望它显示的内容,然后group by
您想要区分且周围没有max()
的列.