sql排名只有连续的行

时间:2015-04-16 08:51:33

标签: sql-server sql-server-2008 rows rank continuous

我有一个查询,其中我根据3列对行进行排名。 我成功地这样做了,除了如果任何行在这3列中包含相同的数据,即使它在输出中不连续,它也会给它下一个等级。我希望如果任何行匹配这些列中的数据,只有当它在连续的行中时才应该给出下一个等级,如果没有那么它应该再次给它作为1的等级。 我尝试了以下代码:

  SELECT DISTINCT DENSE_RANK () OVER (PARTITION BY Patient_ID, 
                                                 Opnametype, 
                                                 afdelingscode ORDER BY  Patient_ID, Opnamenummer, SPECIALISMEN, Opnametype, OntslagDatumTijd) AS rnk, 
                *
  FROM t_opnames
  ORDER BY Patient_ID, Opnamenummer, SPECIALISMEN, Opnametype, OntslagDatumTijd

输出为:

rnk Opnamenummer Patient_ID afdelingscode     Opnametype   Specialismen  OntslagDatumTijd ...
1   2983800      100006     RD8-GH MAU        Inpatient-E  GM            2014-09-01 14:50:00.000
2   2983800      100006     RD8-GH MAU        Inpatient-E  GM            2014-09-02 19:32:00.000
1   2983800      100006     RD8-GH Ward 08    Inpatient-E  GM            2014-09-03 17:12:00.000  
1   2983800      100006     RD8-GH Endo       Inpatient-E  GM            2014-09-09 09:06:00.000
2   2983800      100006     RD8-GH Ward 08    Inpatient-E  GM            2014-09-17 17:00:00.000
3   2983800      100006     RD8-GH Ward 08    Inpatient-E  GM            2014-10-01 17:15:00.000

因此,除了最后两行之外,所有行都是正确的。我希望它们的等级为1和2而不是2和3,因为行有" RD8-GH Endo"在他们之间。 那我怎么能这样做呢?

3 个答案:

答案 0 :(得分:1)

您可以使用相关子查询来实现此目的。使用类似这样的东西

DECLARE @t_opnames TABLE
(
    Opnamenummer INT,
    Patient_ID INT,
    afdelingscode     VARCHAR(100),
    Opnametype   VARCHAR(100),
    Specialismen  CHAR(2),
    OntslagDatumTijd DATETIME
)

Insert into @t_opnames
SELECT  2983800      ,100006,     'RD8-GH MAU',        'Inpatient-E',  'GM',            '2014-09-01 14:50:00.000'
UNION ALL SELECT 2983800      ,100006,     'RD8-GH MAU',        'Inpatient-E',  'GM',            '2014-09-02 19:32:00.000'
UNION ALL SELECT 2983800      ,100006,     'RD8-GH Ward 08',    'Inpatient-E',  'GM',            '2014-09-03 17:12:00.000'  
UNION ALL SELECT 2983800      ,100006,     'RD8-GH Endo',       'Inpatient-E',  'GM',            '2014-09-09 09:06:00.000'
UNION ALL SELECT 2983800      ,100006,     'RD8-GH Ward 08',    'Inpatient-E',  'GM',            '2014-09-17 17:00:00.000'
UNION ALL SELECT 2983800      ,100006,     'RD8-GH Ward 08',    'Inpatient-E',  'GM',            '2014-10-01 17:15:00.000'


;WITH CTE as 
(
SELECT DENSE_RANK() OVER(ORDER BY  Patient_ID, Opnamenummer, SPECIALISMEN, Opnametype, OntslagDatumTijd) rnk,* 
  FROM @t_opnames
)
SELECT rnk-ISNULL((
            SELECT MAX(rnk) 
            FROM CTE c2 
            WHERE c2.Opnamenummer <= c1.Opnamenummer
            AND c2.SPECIALISMEN <= c1.SPECIALISMEN
            AND c2.OntslagDatumTijd <= c1.OntslagDatumTijd
            AND c2.rnk < c1.rnk
            AND (c2.Patient_ID <> c1.Patient_ID 
                OR   c2.Opnametype <> c1.Opnametype 
                OR c2.afdelingscode <> c1.afdelingscode)),0) rnk,Patient_ID, Opnametype,afdelingscode,Opnamenummer, SPECIALISMEN, OntslagDatumTijd
FROM CTE c1
  ORDER BY Patient_ID, Opnamenummer, SPECIALISMEN, Opnametype, OntslagDatumTijd

答案 1 :(得分:1)

最后我得到了我的查询解决方案,现在我得到了我想要的输出,并且在3秒内运行超过75k +行。我使用的代码是:

SELECT DISTINCT ROW_NUMBER () OVER (ORDER BY Patient_ID, Opnamenummer, SPECIALISMEN, Opnametype, OntslagDatumTijd) AS rownum, 
            * INTO #temp
FROM t_opnames
ORDER BY Patient_ID, Opnamenummer, SPECIALISMEN, Opnametype, OntslagDatumTijd;

WITH CTE
AS (SELECT *, 
           ROW_NUMBER () OVER (ORDER BY rownum) - ROW_NUMBER () OVER (PARTITION BY Patient_ID, 
                                                                                   Opnametype, 
                                                                                   afdelingscode ORDER BY rownum) AS RowGroup
      FROM #temp) 
SELECT ROW_NUMBER () OVER (PARTITION BY RowGroup, 
                                        Patient_ID, 
                                        Opnametype, 
                                        afdelingscode ORDER BY rownum) AS GroupSequence, 
       *
  FROM CTE
  ORDER BY rownum;

DROP TABLE #temp;

我提到了在此page

发布的示例

答案 2 :(得分:0)

这并没有直接回答这个问题,但我的目的是解释为什么你所尝试的并不像你期望的那样工作。

您的问题是由PARTITION引起的。如果您从PARTITION子句中删除非唯一列,则会留下afdelingscode。因此,简单来说,您的PARTITION正在对数据进行分组,如下所示:

RD8-GH Endo
RD8-GH MAU
RD8-GH MAU
RD8-GH Ward 08
RD8-GH Ward 08
RD8-GH Ward 08

ORDER BY子句确定了PARTITION中的顺序,因此再次移除非唯一列会为您生成ORDER BY OntslagDatumTijd,生成此内容,按日期列排序,注意分区仍由afdelingscode分隔:

afdelingscode   OntslagDatumTijd
RD8-GH Endo     2014-09-09 09:06:00.000
RD8-GH MAU      2014-09-01 14:50:00.000
RD8-GH MAU      2014-09-02 19:32:00.000
RD8-GH Ward 08  2014-09-03 17:12:00.000
RD8-GH Ward 08  2014-09-17 17:00:00.000
RD8-GH Ward 08  2014-10-01 17:15:00.000

然后将排名应用于这些分区。输出结果为:

rnk afdelingscode   OntslagDatumTijd
1   RD8-GH Endo     2014-09-09 09:06:00.000
1   RD8-GH MAU      2014-09-01 14:50:00.000
2   RD8-GH MAU      2014-09-02 19:32:00.000
1   RD8-GH Ward 08  2014-09-03 17:12:00.000
2   RD8-GH Ward 08  2014-09-17 17:00:00.000
3   RD8-GH Ward 08  2014-10-01 17:15:00.000

因此根据您指定的方式对其进行排名,输出中的问题是因为在您选择的最后(取出非唯一列)是按日期列OntslagDatumTijd排序,这给出了你:

rnk afdelingscode   OntslagDatumTijd
1   RD8-GH MAU      2014-09-01 14:50:00.000
2   RD8-GH MAU      2014-09-02 19:32:00.000
1   RD8-GH Ward 08  2014-09-03 17:12:00.000
1   RD8-GH Endo     2014-09-09 09:06:00.000
2   RD8-GH Ward 08  2014-09-17 17:00:00.000
3   RD8-GH Ward 08  2014-10-01 17:15:00.000

如果发布的其他答案不符合您的要求,我会继续查看。

<强>参考:

OVER Clause

  

PARTITION BY 将查询结果集划分为多个分区。窗户   函数分别应用于每个分区并进行计算   重启每个分区。

     

ORDER BY子句定义每个子行中的行的逻辑顺序   结果集的分区。也就是说,它指定逻辑顺序   其中执行窗口函数计算。

这是一个潜在的解决方案,可能会对您使用的数据大小产生性能问题,但您可以对其进行测试:

-- sets up your dummy data
CREATE TABLE #t_opnames
    (
      Opnamenummer INT ,
      Patient_ID INT ,
      afdelingscode NVARCHAR(20) ,
      Opnametype NVARCHAR(20) ,
      Specialismen NVARCHAR(20) ,
      OntslagDatumTijd DATETIME
    );

INSERT  INTO #t_opnames
        ( Opnamenummer, Patient_ID, afdelingscode, Opnametype, Specialismen,
          OntslagDatumTijd )
VALUES  ( 2983800, 100006, 'RD8-GH MAU', 'Inpatient-E', 'GM',
          '2014-09-01 14:50:00.000' ),
        ( 2983800, 100006, 'RD8-GH MAU', 'Inpatient-E', 'GM',
          '2014-09-02 19:32:00.000' ),
        ( 2983800, 100006, 'RD8-GH Ward 08', 'Inpatient-E', 'GM',
          '2014-09-03 17:12:00.000' ),
        ( 2983800, 100006, 'RD8-GH Endo', 'Inpatient-E', 'GM',
          '2014-09-09 09:06:00.000' ),
        ( 2983800, 100006, 'RD8-GH Ward 08', 'Inpatient-E', 'GM',
          '2014-09-17 17:00:00.000' ),
        ( 2983800, 100006, 'RD8-GH Ward 08', 'Inpatient-E', 'GM',
          '2014-10-01 17:15:00.000' )

-- I've added a row number to your data to enable iteration over the data
SELECT  ROW_NUMBER() OVER ( ORDER BY OntslagDatumTijd ) AS rn ,
        *
INTO #temp
FROM    #t_opnames
ORDER BY OntslagDatumTijd
-- this will iterate over the rows and apply the rankings
;WITH cte AS (
    SELECT *, 1 AS rnk 
    FROM #temp 
    WHERE rn = 1

    UNION ALL 

    SELECT t.*, CASE WHEN cte.afdelingscode = t.afdelingscode 
                     THEN cte.rnk + 1 
                     ELSE 1 
                END AS rnk 
    FROM #temp t
    INNER JOIN cte ON cte.rn +1 = t.rn
)
SELECT * FROM cte

DROP TABLE #t_opnames
DROP TABLE #temp

对于较大的数据集,您将达到MAXRECURSION限制,您需要在最终SELECT之后使用以下内容修改限制:

SELECT * FROM cte
OPTION (MAXRECURSION 0)

将此值设置为0不会产生任何限制,如果您事先知道,可以将此数字设置为数据集的大小。