从表中选择多个列,但按一个分组
Posted
技术标签:
【中文标题】从表中选择多个列,但按一个分组【英文标题】:Select multiple columns from a table, but group by one 【发布时间】:2014-02-08 15:57:56 【问题描述】:表名是“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 列的总和。
【问题讨论】:
您可能想查看字符串的聚合。不幸的是,我没有这方面的经验。 ***.com/questions/13639262/… 【参考方案1】:当我选择多列时,我使用此技巧按一列分组:
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
这行得通。
【讨论】:
巧妙,谢谢!对于那些路过的人:您将max()
放在每个未分组的列周围,将as ___
重命名为您希望它显示的内容,然后将group by
放在您想要区分的列周围没有max()
.
哈哈,欺骗 SQL 的好方法,但我想知道这是否适用于所有情况?
这没有意义,而且可能是错误的!如果您的数据中每个 column_A 有几个 column_B,那么如果您按 Column_A 分组并在选择中使用 MAX(Column_B) 绕过分组限制,那么它只是这些 column_B 值之一(这里是由最大限度)。这通常不是你想要的!如果您的数据中每个 column_A 没有不同的 column_B 值,那么您应该简单地将您的 column_B 添加到 GROUP BY 子句中,如其他答案所述。 @安德鲁
我同意@Andrew - S.Serpooshan 当我们在列中有不同的值时它不起作用
如果您的列类型是布尔值,这将不起作用【参考方案2】:
您的数据
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 ║
╚═══════════╩═════════════╩═══════╝
【讨论】:
但我说过,我不想将其他列名添加到分组依据,它会产生意想不到的结果。 好吧,除非您有多个产品名称与同一个产品 ID 相关联,否则它不会给您带来意想不到的结果。如果是这种情况并且您想避免这种情况,请参阅我的更新 我之前使用的查询确实提供了您在示例数据中显示的预期结果集。 @OzanAyten 我已将您更新的数据用于相同的查询,它向我显示的结果如您预期的结果集中所示。 是的,但我的问题很清楚。如果我把它放在我的问题上,有太多的数据是无法理解的。所以这就是为什么我要求只选择多列而只按一列分组。【参考方案3】:我只是想添加一种更有效、更通用的方法来解决这类问题。 主要思想是处理子查询。
按照表的 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
主键(唯一)在这个表中。
在这种情况下,只需执行几个步骤即可包含额外的列,同时仅对一个列进行分组。这将与以下解决方案相同
我们以这个t_Value
表为例:
如果我想按描述分组并显示所有列。
我要做的就是:
-
使用 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
结果如下:
【讨论】:
【参考方案4】: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://***.com/a/38779277/906265 类似的想法
【讨论】:
非常酷的功能 :) 看起来微软不久前终于有了类似的东西,database.guide/the-sql-server-equivalent-to-group_concat【参考方案5】: 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
请检查这是否有效。
【讨论】:
【参考方案6】:你可以试试这个:
Select ProductID,ProductName,Sum(OrderQuantity)
from OrderDetails Group By ProductID, ProductName
您只需要Group By
Select
子句中不带有聚合函数的列。因此,在这种情况下,您可以只使用Group By
ProductID 和 ProductName。
【讨论】:
这个答案也一样,我说过,我不想添加其他列名来分组,它会产生意想不到的结果。 一个productId 只能有一个相关的ProductName 不是吗?所以 Group By ProductId, ProductName 在这种情况下将给出与 Group By ProductId 相同的结果 ProductName 不是唯一的,只有 ProductID 是唯一的。另外,我知道您的回答是什么意思,但在我的问题中,我只要求按一列分组。【参考方案7】:您可以尝试以下查询。我假设您的所有数据都有一个表。
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
【讨论】:
【参考方案8】:在我看来,这是一个严重的语言缺陷,使 SQL 落后于其他语言数年。这是我令人难以置信的 hacky 解决方法。这是一个完全的混搭,但它总是有效的。
在此之前,我想提请注意@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' 分组
【讨论】:
【参考方案9】:==编辑==
我再次检查了您的问题并得出结论,这是无法完成的。
ProductName 不是唯一的,它必须是 Group By
的一部分或从您的结果中排除。
例如,如果您 Group By
只有 ProductID,SQL 将如何向您呈现这些结果?
ProductID | ProductName | OrderQuantity
---------------------------------------
1234 | abc | 1
1234 | def | 1
1234 | ghi | 1
1234 | jkl | 1
【讨论】:
我正在使用 sql,第一个代码块给出了语法错误。另外,我不想将其他列添加到 Group By。 我附上了我能想到的唯一方法,无需按两个项目分组。问题是如果您按数字分组,则无法选择相应的字符串而不对其进行一些聚合。 @har07 发布的答案看起来是最好的选择。例如,如果两个项目具有相同的 OrderQuantity 但具有不同的 ProductName,则服务器不知道要显示哪个 ProductName。希望这是有道理的。 我想合并和求和相同 ProductID 的行的 OrderQuantity :) 我也知道为什么这不起作用。这一切都说得通,但真的不可能吗? 我刚刚注意到这让你回到了第一方......你得到的结果有什么问题?也许您的查询正是您想要的,只是格式错误? 对不起,但正如我在我的问题中所说,我也需要其他专栏。我知道如何分组。我可以自己做,但我的问题不同。我已经编辑了我的问题,请阅读最后一个阶段【参考方案10】:我遇到了与 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
【讨论】:
【参考方案11】: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中类似的问题。
【讨论】:
问题是如何在不使用所有列的情况下进行分组。以上是关于从表中选择多个列,但按一个分组的主要内容,如果未能解决你的问题,请参考以下文章