2017-02-17 15 views
3

我的問題在自然語言中有明確說明,但我無法在(ORACLE)SQL中找到解決方案。刪除重量超過限制的行,直到沒有這樣的行

數據具有VALUE(正數)和LIMIT(0到1之間的值表示百分比)列。其任務是刪除(或識別)VALUE大於其餘行的VALUES總和的LIMIT%的行。另一個公式:刪除重量(由VALUE定義)大於總量的LIMIT的行。

請注意,刪除某些行後,總和會減少,因此另一行可能會失敗並需要刪除。

到目前爲止,我嘗試瞭解析函數(請參閱下面的示例),遞歸與連接。一切都沒有成功。

該解決方案應該在沒有PL/SQL的SQL中。如果共識是沒有這樣的解決方案存在,那麼程序就沒問題。

我的錯誤的解決方法使用分析功能如下:

WITH 
DATA as (
    SELECT 23 as KEY, 100 as VALUE, 0.15 as LIMIT from DUAL 
    UNION ALL 
    SELECT 22 as KEY, 101 as VALUE, 0.05 as LIMIT from DUAL 
    UNION ALL 
    SELECT 21 as KEY, 10 as VALUE, 0.05 as LIMIT from DUAL 
    UNION ALL 
    SELECT 20 as KEY, 1 as VALUE, 0.15 as LIMIT from DUAL 
    UNION ALL 
    SELECT 19 as KEY, 1 as VALUE, 0.15 as LIMIT from DUAL 
    UNION ALL 
    SELECT 18 as KEY, 1 as VALUE, 0.15 as LIMIT from DUAL 
    UNION ALL 
    SELECT 17 as KEY, 1 as VALUE, 0.15 as LIMIT from DUAL 
    UNION ALL 
    SELECT 16 as KEY, 1 as VALUE, 0.15 as LIMIT from DUAL 
    UNION ALL 
    SELECT 15 as KEY, 1 as VALUE, 0.15 as LIMIT from DUAL 
    UNION ALL 
    SELECT 14 as KEY, 1 as VALUE, 0.15 as LIMIT from DUAL 
    UNION ALL 
    SELECT 13 as KEY, 1 as VALUE, 0.15 as LIMIT from DUAL 
    UNION ALL 
    SELECT 12 as KEY, 1 as VALUE, 0.15 as LIMIT from DUAL 
    UNION ALL 
    SELECT 11 as KEY, 1 as VALUE, 0.15 as LIMIT from DUAL 
    UNION ALL 
    SELECT 10 as KEY, 1 as VALUE, 0.15 as LIMIT from DUAL 
    UNION ALL 
    SELECT 9 as KEY, 1 as VALUE, 0.15 as LIMIT from DUAL 
    UNION ALL 
    SELECT 8 as KEY, 1 as VALUE, 0.15 as LIMIT from DUAL 
    UNION ALL 
    SELECT 7 as KEY, 1 as VALUE, 0.15 as LIMIT from DUAL 
    UNION ALL 
    SELECT 6 as KEY, 1 as VALUE, 0.15 as LIMIT from DUAL 
    UNION ALL 
    SELECT 5 as KEY, 1 as VALUE, 0.15 as LIMIT from DUAL 
    UNION ALL 
    SELECT 4 as KEY, 1 as VALUE, 0.15 as LIMIT from DUAL 
    UNION ALL 
    SELECT 3 as KEY, 1 as VALUE, 0.15 as LIMIT from DUAL 
    UNION ALL 
    SELECT 2 as KEY, 1 as VALUE, 0.15 as LIMIT from DUAL 
    UNION ALL 
    SELECT 1 as KEY, 1 as VALUE, 0.15 as LIMIT from DUAL 
), 
REMOVED AS (
    SELECT 
    d.*, 
    CASE 
     WHEN d.VALUE >= d.LIMIT * (SUM(d.VALUE) OVER()) THEN 'N' 
     ELSE 'Y' 
    END as FLAG, 
    d.VALUE/d.LIMIT * (SUM(d.VALUE) OVER()) AS ACT_WEIGHT, 
    SUM(d.VALUE) OVER() AS TOTOAL_SUM 
    FROM DATA d 
) 
SELECT r.KEY 
FROM REMOVED r 
WHERE FLAG='N'; 
-- Wrong: KEY=21 is missing! 
+0

嗨,這個過程應該是什麼順序?你需要指定。也嘗試使用滯後和領先的窗口函數,那些可以掃描剩餘的行。 – PeterRing

回答

2

此使用遞歸子查詢保條款(又名CTE)反覆產生兩套行。第一組消除了高於先前總數百分比限制的值並重新計算新總數,第二組生成的行更新again列,以確定在迭代生成之前是否有任何高於新總數的新行一組新的行。

查詢

WITH cte (key, value, limit, lvl, total, again) AS (
    SELECT key, value, limit, 1, SUM(value) OVER(), 1 
    FROM data 
UNION ALL 
    SELECT key, 
     value, 
     limit, 
     lvl + 1, 
     CASE MOD(lvl, 2) 
      WHEN 1 
      THEN SUM(value) OVER() 
      ELSE total 
     END, 
     CASE MOD(lvl, 2) 
      WHEN 0 
      THEN MAX(CASE WHEN value > limit * total THEN 1 ELSE 0 END) OVER() 
      ELSE 1 
     END 
    FROM cte 
    WHERE (MOD(lvl, 2) = 0 OR value <= limit * total) 
    AND again = 1 
) 
SELECT * 
FROM cte 
WHERE again = 0; 

輸出

 KEY  VALUE  LIMIT  LVL  TOTAL  AGAIN 
---------- ---------- ---------- ---------- ---------- ---------- 
     20   1  .15   5   20   0 
     19   1  .15   5   20   0 
     18   1  .15   5   20   0 
     17   1  .15   5   20   0 
     16   1  .15   5   20   0 
     15   1  .15   5   20   0 
     14   1  .15   5   20   0 
     13   1  .15   5   20   0 
     12   1  .15   5   20   0 
     11   1  .15   5   20   0 
     10   1  .15   5   20   0 
     9   1  .15   5   20   0 
     8   1  .15   5   20   0 
     7   1  .15   5   20   0 
     6   1  .15   5   20   0 
     5   1  .15   5   20   0 
     4   1  .15   5   20   0 
     3   1  .15   5   20   0 
     2   1  .15   5   20   0 
     1   1  .15   5   20   0 
+0

非常好。幾個月前,我想出了一個解決不同問題的類似解決方案,並且自從我多次使用該技術以來;我在OTN上寫了這篇文章。我不知道它是否有名稱 - 我稱之爲缺乏更好的術語的「蹺蹺板遞歸查詢」技術。如果這是一個衆所周知的技術,我會感興趣;在OTN上似乎並不熟悉。有幾個鏈接:https://community.oracle.com/thread/3966882和https://community.oracle.com/thread/3974508 – mathguy

+1

有了這個說法,現在我明白了數學問題(沒有必要一個「考慮行的順序」,因爲如果一行最初違反了條件,無論如何它必須被丟棄,因爲它會違反它可能成爲的一部分行的任何子集),我發現這個特殊問題不需要蹺蹺板技術。雖然我仍然對這項技術感興趣! – mathguy

+0

感謝MT0和mathguy。你的解決方案使用一個很好的技巧 –

1

這可以用一個遞歸CTE來解決,如MT0顯示。 MT0的解決方案可以簡化 - 這個問題不需要我在MT0評論中提到的「蹺蹺板遞歸查詢」。

在遞歸查詢中,我從給定數據開始,但添加了三列:值的總和(對於「當前行」),行數和數字1作爲佔位符。 (任何正數都可以在那裏使用。)

然後在遞歸步驟中,我保持上一級滿足限制條件的行(但對於「舊」總和,包括必須丟棄的行)。我再次計算總和(val),以用於下一步,我計算剩下多少行,並計算「this」級別與前一級別之間的計數差異。

如果在某個時候計數差異變爲零,那意味着在那個級別上我不需要丟棄任何行。那些是解決問題的答案。我在外部查詢中選擇它們。

with 
    data (key, value, limit) as (
     select ........... 
    ), 
    r (key, value, limit, tot, cnt, diff) as (
     select key, value, limit, sum(value) over(), count(*) over(), 1 
     from data 
     union all 
     select key, value, limit, sum(value) over(), count(*) over(), 
       cnt - count(*) over() 
     from r 
     where value <= limit * tot and diff > 0 
) 
select key, value, limit 
from r 
where diff = 0 
;