在 Oracle 11g 数据库中,假设我们有表 CUSTOMER
和PAYMENT
如下
客户
CUSTOMER_ID | CUSTOMER_NAME | CUSTOMER_AGE | CUSTOMER_CREATION_DATE
--------------------------------------------------------------------
001 John 30 1 Jan 2017
002 Jack 10 2 Jan 2017
003 Jim 50 3 Jan 2017
付款
CUSTOMER_ID | PAYMENT_ID | PAYMENT_AMOUNT |
-------------------------------------------
001 900 100.00
001 901 200.00
001 902 300.00
003 903 999.00
我们想要编写一条 SQL 来获取表 CUSTOMER
中的所有列连同每个客户的所有付款总额。有很多可能的方法可以做到这一点,但我想问以下哪一种更好。
解决方案1
SELECT C.CUSTOMER_ID
, MAX(C.CUSTOMER_NAME) CUSTOMER_NAME
, MAX(C.CUSTOMER_AGE) CUSTOMER_AGE
, MAX(C.CUSTOMER_CREATION_DATE) CUSTOMER_CREATION_DATE
, SUM(P.PAYMENT_AMOUNT) TOTAL_PAYMENT_AMOUNT
FROM CUSTOMER C
JOIN PAYMENT P ON (P.CUSTOMER_ID = C.CUSTOMER_ID)
GROUP BY C.CUSTOMER_ID;
解决方案2
SELECT C.CUSTOMER_ID
, C.CUSTOMER_NAME
, C.CUSTOMER_AGE
, C.CUSTOMER_CREATION_DATE
, SUM(P.PAYMENT_AMOUNT) PAYMENT_AMOUNT
FROM CUSTOMER C
JOIN PAYMENT P ON (P.CUSTOMER_ID = C.CUSTOMER_ID)
GROUP BY C.CUSTOMER_ID, C.CUSTOMER_NAME, C.CUSTOMER_AGE, C.CUSTOMER_CREATION_DATE
请注意,在解决方案 1 中,我使用 MAX
不是因为我实际上想要最大结果,而是因为我想要列中的“一行”,我知道这些行对于具有相同 CUSTOMER_ID
的所有行都是相等的
在解决方案 2 中,我避免添加误导性的 MAX
在SELECT
部分将列放入 GROUP BY
部分代替。
以我目前的知识,我更喜欢解决方案1,因为理解GROUP BY
中的逻辑更重要。部分比 SELECT
部分。我只会放置一组唯一键来表达查询的意图,以便应用程序可以推断出预期的行数。但不知道性能如何。
我问这个问题是因为我正在审查一个大型 SQL 的代码更改,该 SQL 将 50 列放入 GROUP BY
中子句,因为编辑者希望避免 MAX
函数位于 SELECT
部分。我知道我们可以以某种方式重构查询,以避免将不相关的列放在 GROUP BY
中。和SELECT
部分,但请放弃该选项,因为它会影响应用程序逻辑并需要更多时间进行测试。
更新
按照大家的建议,我刚刚在两个版本中对我的大查询进行了测试。查询很复杂,有69行,涉及20多个表,执行计划有190多行,所以我想这里不适合展示。
我的生产数据现在很小,大约有 4000 个客户,并且查询是针对整个数据库运行的。仅表CUSTOMER
还有一些引用表有TABLE ACCESS FULL
在执行计划中,其他表可以通过索引访问。两个版本的执行计划在连接算法( HASH GROUP BY
与 SORT AGGREGATE
)的某些部分略有不同。
两个版本均用时约13分钟,无显着差异。
我也对类似于问题中的SQL的简化版本进行了测试。两个版本具有完全相同的执行计划和运行时间。
根据当前信息,我认为最合理的答案是,除非通过测试来确定两个版本的质量,否则这是不可预测的,因为优化器将完成这项工作。如果有人能提供任何信息来说服或拒绝这个想法,我将非常感激。
最佳答案
另一个选择是
SELECT C.CUSTOMER_ID
, C.CUSTOMER_NAME
, C.CUSTOMER_AGE
, C.CUSTOMER_CREATION_DATE
, P.PAYMENT_AMOUNT
FROM CUSTOMER C
JOIN (
SELECT CUSTOMER_ID, SUM(PAYMENT_AMOUNT) PAYMENT_AMOUNT
FROM PAYMENT
GROUP BY CUSTOMER_ID
) P ON (P.CUSTOMER_ID = C.CUSTOMER_ID)
要决定三者中哪一个更好,只需测试它们并查看执行计划即可。
关于sql - 在 Oracle SQL 中将许多列放在 group by 子句中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45693600/