T-SQL - 我可以强制 ROW_NUMBER() OVER (PARTITION BY... 从设定点对记录进行编号吗?

Posted

技术标签:

【中文标题】T-SQL - 我可以强制 ROW_NUMBER() OVER (PARTITION BY... 从设定点对记录进行编号吗?【英文标题】:T-SQL - Can I force ROW_NUMBER() OVER (PARTITION BY... to number records from a set point? 【发布时间】:2018-01-22 12:19:14 【问题描述】:

我有一张表格,其中包含患者预约列表:他们就诊的诊所和就诊日期。

我正在尝试编写一个查询,它提供以下信息:

“哪些患者在 2016 年 4 月至 2017 年 3 月期间的任何时候到过诊所 '123-45',以及该患者随后的两次预约(预约日期和就诊的诊所)是什么”? em>

我试图通过首先查询在时间范围内就诊于诊所“123-45”的所有患者的患者 ID 号列表,然后将此患者 ID 列表放入 WHERE 子句中来解决这个问题并使用ROW_NUMBER() OVER (PARTITION BY… 为我提供12 个月期间每位患者的所有预约的有序列表。

    SELECT 
     x.Patient_Id
    ,x.Clinic_Code
    ,x.Appointment_Date
    ,x.Row_No FROM 
    (
    SELECT
         Patient_Id
        ,Clinic_Code
        ,Appointment_Date
        ,ROW_NUMBER() OVER (PARTITION BY Patient_Id ORDER BY Patient_Id, Appointment_Date asc) [Row_No]

    FROM 
        Appointments

    WHERE 
             Appointment_Date BETWEEN '01/10/2016' AND '30/09/2017'
         AND Patient_ID = 'BLO123'

    ) x

    WHERE x.Row_No < 4

但是,这会产生意想不到的后果,即对诊所“123-45”就诊之前发生的任何预约进行编号。

所以,如果以下是我的来源:

Patient_ID | Clinic_Code | Appointment_Date
--------------------------------------------
BLO123     |   QWE-QW    |   01-04-2016
BLO123     |   OPD-ZZ    |   05-10-2016
BLO123     |   123-45    |   13-11-2016
BLO123     |   333-44    |   15-12-2016
BLO123     |   999-45    |   02-02-2017
BLO123     |   222-44    |   15-02-2017
BLO123     |   777-45    |   19-03-2017

我想要得到的是:

Patient_ID | Clinic_Code | Appointment_Date | Row_No
--------------------------------------------------------------
BLO123     |   123-45    |   13-11-2016     |   1
BLO123     |   333-44    |   15-12-2016     |   2
BLO123     |   999-45    |   02-02-2017     |   3

但是通过在日期范围内包含之前的约会,我得到的是:

Patient_ID | Clinic_Code | Appointment_Date | Row_No
--------------------------------------------------------------
BLO123     |   QWE-QW    |   01-04-2016     |   1
BLO123     |   OPD-ZZ    |   05-10-2016     |   2
BLO123     |   123-45    |   13-11-2016     |   3

我想查询的是忽略在“123-45 就诊”之前的任何诊所预约。 请任何人都可以建议是否可以这样做?

【问题讨论】:

脱机;强烈反对在 where 子句中使用 dd/mm/yyyy 作为 date format。 【参考方案1】:

此方法使用common table expression (CTE) 查找每位患者在诊所123-45 的第一次预约。查询主体返回所有后续约会。

样本数据:

DECLARE @Appointment TABLE
    (
        Patient_ID          varchar(6),
        Clinic_code         varchar(6),
        Appointment_Date    date
    )
;

INSERT INTO @Appointment
(
        Patient_ID,
        Clinic_code,
        Appointment_Date
)
VALUES 
    ('BLO123','QWE-QW','20160401'),
    ('BLO123','OPD-ZZ','20161005'),
    ('BLO123','123-45','20161113'),
    ('BLO123','333-44','20161215'),
    ('BLO123','999-45','20170202')
;

查询:

WITH     
    FirstAppointment AS 
    (
        -- Find patients first vist to clinic 123-45.
        SELECT
            Patient_ID,
            MIN(Appointment_Date) AS FirstAppointment_Date
        FROM
            @Appointment
        WHERE
            Appointment_Date >= '20160401'
            AND Appointment_Date <= '20170331'
            AND Clinic_code = '123-45'
        GROUP BY
            Patient_ID
    )    
SELECT
    ROW_NUMBER() OVER (PARTITION BY a.Patient_ID ORDER BY a.Appointment_Date) AS Rn,
    a.*
FROM
    FirstAppointment AS fa
        INNER JOIN @Appointment AS a        ON  a.Patient_ID        = fa.Patient_ID
                                            AND a.Appointment_Date  >= fa.FirstAppointment_Date
;

【讨论】:

【参考方案2】:
with foo as
(
select
*
from (values
('BLO123','QWE-QW', cast('20160401' as date))
,('BLO123','OPD-ZZ',cast('20161005' as date))
,('BLO123','123-45',cast('20161113' as date))
,('BLO123','333-44',cast('20161215' as date))
,('BLO123','999-45',cast('20170202' as date))
) a(Patient_ID , Clinic_Code , Appointment_Date)
)
,lags as
(
select 
*
,lag(Clinic_code,1) over (partition by Patient_id order by Appointment_Date) l1
,lag(Clinic_code,2) over (partition by Patient_id order by Appointment_Date) l2
,ROW_NUMBER() over (partition by Patient_id order by Appointment_Date) rn
from foo
)
select Patient_ID,Clinic_Code,Appointment_Date
,case when Clinic_Code='123-45' then 1
when l1='123-45' then 2
else 3 end  Row_Nr
from lags
where '123-45' in (Clinic_Code,l1,l2)

结果:

+----------------------------------------------+
|Patient_ID|Clinic_Code|Appointment_Date|Row_No|
+----------------------------------------------+
|BLO123    |123-45     |2016-11-13      |1     |
|BLO123    |333-44     |2016-12-15      |2     |
|BLO123    |999-45     |2017-02-02      |3     |
+----------------------------------------------+

【讨论】:

谢谢,但恐怕我在 SQL Server 2008 上,而且显然要到 2012 年才能使用 LAG 功能。

以上是关于T-SQL - 我可以强制 ROW_NUMBER() OVER (PARTITION BY... 从设定点对记录进行编号吗?的主要内容,如果未能解决你的问题,请参考以下文章

重新启动 T-SQL 中的行号 [重复]

T-SQL 中的数据透视表分区

T-SQL开窗函数

T-SQL分页查询语句

如何强制我的 T-SQL 查询将不匹配的值输出为零?

在 T-SQL 中使用 PIVOT 需要帮助