2012-05-25 25 views
5

我有一個Oracle查詢SQL Server:如何模仿oracle保持dense_rank查詢?

select max(m.id), 
     m.someId keep (DENSE_RANK FIRST ORDER BY m.UpdateDate desc) 
from MyTable m 
groupBy m.someId 

這對於像這樣的數據:

id UpdateDate someId 
1 20-01-2012 10 
2 20-01-2012 10 
3 01-01-2012 10 
4 10-02-2012 20 
5 01-02-2012 20 
6 01-04-2012 30 

將返回我到底這樣的:

2 10 
4 20 
6 30 

因此,對於每一個someId它會搜索最新updateDate並確實返回相應的id。 (如果最近的日期有多個ID,則需要最新的ID)。

但是對於SQL服務器,這個查詢的工作方式是否一樣?我的意思是這種結構keep (dense_rank first order by ..)

回答

8

我不認爲你的特定查詢將運行SQL Server。但是你可以達到同樣的效果這樣做:

SELECT id, SomeId 
FROM ( SELECT *, ROW_NUMBER() OVER(PARTITION BY someId ORDER BY UpdateDate DESC, id DESC) Corr 
     FROM MyTable) A 
WHERE Corr = 1 
+0

我不知道它..它應該如何工作,沒有'someId組'?我的查詢總是會返回3個條目,因爲我在表中有3個不同的someIds。你的查詢將返回不依賴於不同someIds數量的結果,所以它應該是錯誤的呢? – javagirl

+1

@javagirl - 你先試了嗎?它會起作用。至於它是如何工作的,分析函數('OVER()...')可以有一個'PARTITION BY',並且不需要在整個級別上進行分組。它爲表中的每一行返回一個值。 – Lamak

+0

我誤解了原始數據。通過將someid放入分區而不是id來解決問題。 –

1

SQL Server不支持「保持」建設,所以你需要使用子查詢:

select m.* 
from (select *, row_number() over (partition by m.someid ORDER BY m.UpdateDate desc) as seqnum 
     from MyTable m 
    ) m 
where seqnum = 1 

此發現的第一行各m.id與最新的UpdateDate。然後它在外部查詢中選擇該行。請注意,您不需要使用此方法進行分組。

+0

我沒有得到它..它應該如何工作沒有'group by someId' ?我的查詢總是會返回3個條目,因爲我在表中有3個不同的someIds。你的查詢將返回不依賴於不同someIds數量的結果,所以它應該是錯誤的呢? – javagirl

+0

這不起作用,因此每個ID都會有一行,這不是我們想要的。 – Lamak

3

我返回並返回到此問題和答案。不幸的是,有幾種情況下,使用「窗口函數進行排名」的遷移變得非常複雜。這兩種情況是:

  1. 許多 Oracle查詢的選擇部分保DENSE_RANK結構基於不同的順序進行分組套/彙總
  2. 分組

因此,我會添加到答案附加信息。 原始數據SQLFIDDLE:http://sqlfiddle.com/#!6/e5c6d/6

閱讀Oracle函數:

select max(m.id), m.someId keep (DENSE_RANK FIRST ORDER BY m.UpdateDate desc) 
from MyTable m 
groupBy m.someId 

有我們的組中選擇m.id的最大值(someId,UpdateDate),其中UpdateDate是它最大的組(someId )

2.直接的方式不起作用,因爲錯誤的:列「MyTable.UpdateDate」是在選擇無效列表,因爲它不包含在聚合函數或GROUP BY子句中。

SELECT FIRST_VALUE(id) OVER(PARTITION BY someId ORDER BY UpdateDate DESC, id DESC) first_in_orderedset , someId 
FROM MyTable 
GROUP BY someId 

3. improoved '直線前進' 是無效藥

SELECT someId, MIN(first_in_orderedset) 
FROM 
(SELECT FIRST_VALUE(id) OVER(PARTITION BY someId ORDER BY UpdateDate DESC, id DESC) first_in_orderedset , someId 
    FROM MyTable) t 
GROUP BY someId; 

4。交叉應用:

SELECT grouped.someId, orderedSet.FirstUpdateDate, maxInSet.first_in_orderedset FROM 
(
    SELECT mt.someId 
    FROM MyTable mt 
    GROUP BY mt.someId 
) grouped CROSS APPLY 
( 
    SELECT top 1 mt2.UpdateDate as FirstUpdateDate 
    FROM MyTable mt2 
    WHERE mt2.someId=grouped.someId 
    ORDER BY UpdateDate desc 
) orderedSet CROSS APPLY 
( 
    SELECT max(mt3.id) as first_in_orderedset 
    FROM MyTable mt3 
    WHERE mt3.someId=grouped.someId and mt3.UpdateDate=orderedSet.FirstUpdateDate 
) maxInSet; 

5.現在讓我們得到更復雜的表格和更復雜的查詢: ORACLE:http://sqlfiddle.com/#!4/c943c/23 SQL SERVER:http://sqlfiddle.com/#!6/dc7fb/1/0 (數據預生成的,它是在這兩個沙箱是相同的 - 很容易比較的結果) 表:

CREATE TABLE AlarmReports (
    id int PRIMARY KEY, 
    clientId int, businessAreaId int , projectId int, taskId int, 
    process1Spent int, process1Lag int, process1AlarmRate varchar2(1) null, 
    process2Spent int, process2Lag int, process2AlarmRate varchar2(1) null, 
    process3Spent int, process3Lag int, process3AlarmRate varchar2(1) null 
) 

Oracle查詢:

SELECT clientId, businessAreaId, projectId, 
    sum(process1Spent), 
    sum(process2Spent), 
    sum(process3Spent), 
    MIN(process1AlarmRate) KEEP (DENSE_RANK FIRST ORDER BY process1Lag DESC), 
    MIN(process2AlarmRate) KEEP (DENSE_RANK FIRST ORDER BY process2Lag DESC), 
    MIN(process3AlarmRate) KEEP (DENSE_RANK FIRST ORDER BY process3Lag DESC) 
FROM AlarmReports 
GROUP BY GROUPING SETS ((),(clientId),(clientId, projectId),(businessAreaId),(clientId,businessAreaId)) 

SQL查詢:

(to be continued) 

居然還有我已經計劃把用C#wroted我的自定義聚合。如果有人感興趣,請與我聯繫...自定義聚合是這類問題的最佳解決方案,但它在varchar長度方面不是非常明顯的。對於每個varchar長度,您都有義務創建「專用」聚合函數

0

這絕對可行。先嚐試,然後爭論。 當你有多個訂單由你可以這樣做(在Oracle做例子):

- 這一次跟上DENSE_RANK

WITH a AS (SELECT 1 s1, 4 s2, 'a' c, 10 g FROM dual UNION all 
      SELECT 2 s1, 2 s2, 'b' c, 10 g FROM dual UNION ALL 
      SELECT 3 s1, 1 s2, 'c' c, 20 g FROM dual UNION ALL 
      SELECT 4 s1, 3 s2, 'd' c, 20 g FROM dual) 
SELECT g, 
     MAX(c) KEEP (DENSE_RANK FIRST ORDER BY s1) s1, 
     MAX(c) KEEP (DENSE_RANK FIRST ORDER BY s2) s2 
    FROM a 
GROUP BY g 

- 這一個不守DENSE_RANK

WITH a AS (SELECT 1 s1, 4 s2, 'a' c, 10 g FROM dual UNION all 
       SELECT 2 s1, 2 s2, 'b' c, 10 g FROM dual UNION ALL 
       SELECT 3 s1, 1 s2, 'c' c, 20 g FROM dual UNION ALL 
       SELECT 4 s1, 3 s2, 'd' c, 20 g FROM dual) 
SELECT g, 
     MAX(DECODE(s1, 1, c)) s1, 
     MAX(DECODE(s2, 1, c)) s2 
    FROM (SELECT g,c, 
       ROW_NUMBER() OVER (PARTITION BY g ORDER BY s1) s1, 
       ROW_NUMBER() OVER (PARTITION BY g ORDER BY s2) s2 
      FROM a) b 
GROUP BY g