SQL Server:如何模仿oracle keep dense_rank查询?

时间:2012-05-25 14:58:01

标签: sql sql-server oracle

我有一个Oracle查询

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 ..)

5 个答案:

答案 0 :(得分: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

答案 1 :(得分:3)

我回来并回到这个问题和答案。不幸的是,在使用"窗口功能进行迁移时,有几种情况可以用于排名"变得非常复杂。那些情况是:

  1. 选择部分中的许多KEEP-DENSE_RANK结构 基于不同订单的Oracle查询
  2. 按分组/汇总分组
  3. 因此我会在答案中添加其他信息。 原始数据SQLFIDDLE:http://sqlfiddle.com/#!6/e5c6d/6

    1。阅读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。直接导致无法正常工作因为错误: Column' 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。改进'直接'是无效的

    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#一起使用。如果有人感兴趣,请与我联系...自定义聚合是这类问题的最佳解决方案,但就varchar长度来说并不是很好的。对于每个varchar长度,您将有义务创建"专门的"聚合功能

答案 2 :(得分:1)

SQL Server不支持“keep”构造,因此您需要使用子查询:

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。然后它在外部查询中选择该行。请注意,此方法不需要使用组。

答案 3 :(得分:1)

这绝对有用。首先尝试,然后争辩。 当您有多个订单时,可以执行此操作(在Oracle上进行示例):

- 这一个与keep_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

答案 4 :(得分:0)

如果有人在Postgres中寻找Oracle KEEP DENSE_RANK模拟:

CREATE TABLE myt (
  "id" INTEGER,
  "update_date" timestamp,
  "some_id" INTEGER
);

INSERT INTO myt
  ("id", "update_date", "some_id")
VALUES
  ('1', '2012-01-20', '10'),
  ('2', '2012-01-20', '10'),
  ('3', '2012-01-01', '10'),
  ('4', '2012-10-02', '20'),
  ('5', '2012-01-02', '20'),
  ('6', '2012-01-04', '30');                         


select
    some_id, 
    (array_agg(id order by update_date desc, id desc))[1]
from myt
group by some_id
order by some_id

enter image description here