群里面有位網友提出了這樣一個SQL挑戰問題(原話不是這樣,為了說明問題,我略做調整些):
問題說明如下
有一條個銷售報表TEST :
商品 金額
A 1400
B 800
C 790
... ...
現在有這樣一個需求(要寫一個SQL取出如下數據):先按銷售金額倒敘排序,然后從這個報表取出前N條記錄,並且這N條記錄的銷售金額之和是總金額的80%(<= 80%), 80%將作為一個查詢條件(有可能是20%,30%).他的想法是用嵌套函數 遞歸做法記錄砍掉一半判斷是否達到百分比 如果不足再取后半部分的一半 類似這樣的方法;還有就是逐行金額相加當金額到達80%的時候 記錄就從頭顯示到該條的前一條。 現在他想有沒有其他跟高效的方法來實現這個需求?
那么我看到這個問題的時候,覺得有嵌套函數遞歸處理的方法的效率肯定是最低的,畢竟SQL不擅長於做邏輯處理。那么有沒有更好的方法來實現呢?大家在看后面方法的時候,可以思考一下有沒有更好的方法,能夠達到一擊必殺的效果(一個SQL搞定)。歡迎大家探討!
下面是我的一個解決思路和方法,我在按銷售金額倒敘排列的時候,能否得到一個銷售金額的累加值?然后把這個累加值除以銷售總金額即得到累加金額占總金額的一個比例,然后我們再加上查詢條件。即可解決問題。
新建一個測試表TEST
CREATE TABLE TEST
(
PRODUCT_NAME CHAR(32), --商品名稱
SALE_AMOUNT FLOAT --銷售金額
)
插入測試數據
- INSERT INTO TEST
- SELECT 'A' ,13000 UNION ALL
- SELECT 'A' ,12000 UNION ALL
- SELECT 'A' ,9000 UNION ALL
- SELECT 'B' ,167000 UNION ALL
- SELECT 'B' ,137000 UNION ALL
- SELECT 'B' ,107000 UNION ALL
- SELECT 'C' ,78000 UNION ALL
- SELECT 'C' ,12000;
實現銷售金額的累加值字段的腳本(這個腳本效率沒有測試,小量數據應該沒有問題)
- SELECT ROW_NUMBER() OVER (ORDER BY SALE_AMOUNT DESC ) AS ROW,PRODUCT_NAME,
- T.SALE_AMOUNT,
- (SELECT SUM(SALE_AMOUNT)AS ACCUMATE_SALE FROM TEST WHERE T.SALE_AMOUNT <= SALE_AMOUNT ) AS ACCUMATE_SALE
- FROM TEST T
(截圖)
那么接下來我們來實現上面的思路
SELECT ROW_NUMBER() OVER ( ORDER BY T.SALE_AMOUNT DESC ) AS ROW ,
T.PRODUCT_NAME ,
T.SALE_AMOUNT ,
( SELECT SUM(SALE_AMOUNT) SUM_SALE
FROM TEST
WHERE T.SALE_AMOUNT <= SALE_AMOUNT
) / L.SALE_AMOUNT AS SUM_RAT
FROM TEST T ,
( SELECT SUM(SALE_AMOUNT) AS SALE_AMOUNT
FROM TEST
) L
截圖
接下來就水到渠成了
SELECT T.ROW, T.PRODUCT_NAME, T.SALE_AMOUNT, T.SUM_RAT FROM
(
SELECT ROW_NUMBER() OVER(ORDER BY T.SALE_AMOUNT DESC) AS ROW,
T.PRODUCT_NAME,
T.SALE_AMOUNT,
(SELECT SUM(SALE_AMOUNT) SUM_SALE
FROM TEST
WHERE T.SALE_AMOUNT <= SALE_AMOUNT) / L.SALE_AMOUNT AS SUM_RAT
FROM TEST T, (SELECT SUM(SALE_AMOUNT) AS SALE_AMOUNT FROM TEST) L
) T WHERE SUM_RAT < 0.8
截圖
后記總結:其實我們可以用SQL很巧妙的實現很多邏輯復雜的需求,避免我們去做大量復雜的邏輯處理,這就需求我們開動腦筋,挑戰極限。像ITPUT就有很多SQL挑戰問題http://www.itpub.net/forum.php?mod=forumdisplay&fid=3&filter=typeid&typeid=1808 ,國外網站例如http://www.plsqlchallenge.com/, 有興趣的同學盡可去嘗試一下。