在Oracle 11g數據庫,假設我們有桌子,CUSTOMER
和PAYMENT
如下用Oracle SQL把很多列在GROUP BY子句
客戶
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
不是因爲其實我是想最大的結果,但我是因爲我希望「ONE通知「從我知道的列的行是相同的所有行相同的CUSTOMER_ID
雖然在解決方案2,我避免把SELECT
部分中誤導MAX
通過將GROUP BY
部分而不是列。
從我目前的知識,我更喜歡解決方案1 ,因爲它理解的邏輯GROUP BY
部分比SELECT
部分更重要。我只會放一組唯一的鍵來表示查詢的意圖,所以應用程序可以推斷出預期的行數。但我不知道表現。
,因爲編輯器要避免SELECT
部分MAX
功能我問這個問題,因爲我將回顧一個大的SQL投入50列GROUP BY
子句中的代碼更改。我知道我們可以在某種程度上重構查詢以避免將不相關的列放在GROUP BY
和SELECT
部分,但是請放棄該選項,因爲它會影響應用程序邏輯並需要更多時間來執行測試。
更新
我剛纔做了測試上我的大查詢兩個版本中,每個人都建議。查詢是複雜的,它有69行涉及超過20個表,執行計劃超過190行,所以我認爲這不是顯示它的地方。
我的生產數據現在很小,它有大約4000個客戶,並且查詢是針對整個數據庫運行的。在執行計劃中只有表CUSTOMER
和幾個參考表有TABLE ACCESS FULL
,其他表有索引訪問。兩個版本的執行計劃在某些部分中的加入算法(HASH GROUP BY
與SORT AGGREGATE
)有一點點差異。
兩個版本使用約13分鐘,無顯着差異。
我也對與問題中的SQL類似的簡化版本進行了測試。兩個版本的執行計劃和流逝時間完全相同。
根據目前的信息,我認爲最合理的答案是它是不可預測的,除非測試決定優化器將執行這項工作時兩個版本的質量。如果有人能夠提供任何信息來說服或拒絕這個想法,我將非常感激。
請有資格你所說的「更好」是什麼意思? –
這個問題暗示着SQL語法的冗餘。 'select'子句**中的每個非聚合列必須出現在'group by'子句中,以使SQL有效,並且'group by'子句中的每一列都應該是**在'select'子句中聚集列,否則結果可能不明確。所以在我看來,應該不需要一個「group by」子句! –
@Caius Jard,如果有任何方面從未想過,我想留下來,但我最關心的是性能。 – asinkxcoswt