2017-04-04 37 views
0

我在甲骨文12C類似表:如何查找按某個字段排序的每N行的最大值?

CREATE TABLE test (
    id INT PRIMARY KEY, 
    foo VARCHAR2(20), 
    seconds int, 
    system_dt DATE 
); 

有了這樣

ABC, 10, 2016-01-01 00:00:01 
ABC, 11, 2016-01-01 00:00:02 
ABC, 5, 2016-01-01 00:35:54 
ABC, 6, 2016-01-01 00:41:01 
DEF, 15, 2016-01-01 00:00:02 
DEF, 14, 2016-01-01 00:01:03 
DEF, 51, 2016-01-01 00:36:55 
DEF, 1, 2016-01-01 00:42:02 

一組數據(需要注意的是idsystem_dt(顯然rownum)不要在增加是很重要的任何一致的值)

我想要做的是按foo, system_dt對此表進行排序,然後在每N行中找到MAX(seconds)foo承擔。

因此,在上面的例子中,其中N是2,輸出將是:

ABC, 11, 2015-01-01 00:00:02 
ABC, 6, 2016-01-01 00:41:01 
DEF, 15, 2016-01-01 00:00:02 
DEF, 51, 2016-01-01 00:36:55 

我想過使用ROWNUM,但ROWNUM增量是不是在下面的查詢是一致的:

 
SELECT foo, rownum, seconds, system_dt 
FROM test 
ORDER BY foo, system_dt 
----- 
ABC, 3, 20 
ABC, 134 25 
ABC, 137, 18 
ABC, 5086, 15 
ABC, 5098, 16 
+0

什麼是所期望的結果,如果在輸入的行,對於給定FOO的數量,是不完全除盡的組大小?例如,如果您有20個值FOO ='ABC',並且您一次分爲六行? – mathguy

+0

@mathguy我想這是不明確的,但我現在在內存中實現它的方式,我會採取在您的示例中的最後兩個值的最大值,而不是放棄它們。我想我並不擔心,因爲我正在將最糟糕的運行查詢樣本放在圖表上。但是如果你知道實現它的方法,我一定會使用它! –

+0

戈登的解決方案已經做到了。我在問,因爲我在考慮使用MATCH_RECOGNIZE的解決方案(因爲您使用的是Oracle 12,它可用)。 – mathguy

回答

1

你會使用rownumrow_number()

select foo, max(seconds), 
     max(system_dt) keep (dense_rank first order by seconds desc) as system_dt 
from (select t.*, 
      row_number() over (partition by foo order by system_dt) - 1 as seqnum 
     from test t 
    ) t 
group by foo, trunc(seqnum/2); 
+0

這似乎對我的數據集感謝您的工作;你能否以'desc'來解釋'dense_rank first order by seconds desc'中'seconds desc'的選擇? –

+0

Gordon本可以使用'ORDER BY SECONDS'和KEEP(DENSE_RANK ** LAST **)。如果'SECONDS'列允許'NULL',那麼會有區別。在這種情況下,人們可以使用'KEEP(SECONDS NULLS FIRST'的DENSE_RANK最後命令,或者Gordon寫的,基本上是同樣的東西,只是寫法不同而已。 – mathguy

0

這是一個解決方案,它使用MATCH_RECOGNIZE(僅在Oracle 12.1以後纔可用)按固定數量的行進行分組。不幸的是,至少在12.1(我使用的版本)中,MATCH_RECOGNIZE中還不支持聚合函數FIRST/LAST;如果是的話,一切都可以在match_recognize中完成,並且GROUP BY將不再需要。

with 
    test (foo, seconds, system_dt) as (
     select 'ABC', 10, to_date('2016-01-01 00:00:01', 'yyyy-mm-dd hh24:mi:ss') from dual union all 
     select 'ABC', 11, to_date('2016-01-01 00:00:02', 'yyyy-mm-dd hh24:mi:ss') from dual union all 
     select 'ABC', 5, to_date('2016-01-01 00:35:54', 'yyyy-mm-dd hh24:mi:ss') from dual union all 
     select 'ABC', 6, to_date('2016-01-01 00:41:01', 'yyyy-mm-dd hh24:mi:ss') from dual union all 
     select 'DEF', 15, to_date('2016-01-01 00:00:02', 'yyyy-mm-dd hh24:mi:ss') from dual union all 
     select 'DEF', 14, to_date('2016-01-01 00:01:03', 'yyyy-mm-dd hh24:mi:ss') from dual union all 
     select 'DEF', 51, to_date('2016-01-01 00:36:55', 'yyyy-mm-dd hh24:mi:ss') from dual union all 
     select 'DEF', 1, to_date('2016-01-01 00:42:02', 'yyyy-mm-dd hh24:mi:ss') from dual 
    ) 
-- end of test data (not part of the SQL query); query begins BELOW THIS LINE 
select foo, max(seconds) as seconds, 
     max(system_dt) keep (dense_rank last order by seconds) as system_dt 
from test 
match_recognize (
    partition by foo 
    order by system_dt 
    measures match_number() as mn 
    all rows per match 
    pattern (a{2} | a+ $) 
    define a as 0 = 0 
) 
group by foo, mn 
; 

FOO SECONDS SYSTEM_DT 
--- ------- ------------------- 
ABC  11 01/01/2016 00:00:02 
ABC  6 01/01/2016 00:41:01 
DEF  15 01/01/2016 00:00:02 
DEF  51 01/01/2016 00:36:55 
相關問題