SQL:CTE查询速度

时间:2016-10-05 11:43:01

标签: sql sql-server recursion common-table-expression recursive-query

我正在使用SQL Server 2008,并试图提高我的查询速度。查询根据重新开始日期为患者分配点数。

示例:患者在1 / 2,1 / 5,1 / 7,1 / 8,1 / 9,2 / 4处可见。我想先在3天内进行小组访问。 1 / 2-5分组,1 / 7-9分组。 1/5不与1/7分组,因为1/5的实际访问日期是1/2。 1/7将获得3分,因为它是1/2的重新接受。 2/4也会获得3分,因为它是1/7的重新接受。日期分组后,第一个日期是实际访问日期。

大多数文章建议限制数据集或添加索引以提高速度。我已将行数限制为约15,000并添加了索引。当运行具有45个测试访问日期/ 3个测试患者的查询时,查询需要1.5分钟才能运行。使用我的实际数据集,需要> 8小时 如何让此查询运行< 1小时?有没有更好的方法来编写我的查询?我的索引看起来是否正确?任何帮助将不胜感激。

查询下方的预期结果示例。

;CREATE TABLE RiskReadmits(MRN INT, VisitDate DATE, Category VARCHAR(15))
;CREATE CLUSTERED INDEX Risk_Readmits_Index ON RiskReadmits(VisitDate)

;INSERT RiskReadmits(MRN,VisitDate,CATEGORY)
VALUES
(1, '1/2/2016','Inpatient'),
(1, '1/5/2016','Inpatient'),  
(1, '1/7/2016','Inpatient'),  
(1, '1/8/2016','Inpatient'), 
(1, '1/9/2016','Inpatient'),  
(1, '2/4/2016','Inpatient'), 
(1, '6/2/2016','Inpatient'),
(1, '6/3/2016','Inpatient'),
(1, '6/5/2016','Inpatient'),  
(1, '6/6/2016','Inpatient'), 
(1, '6/8/2016','Inpatient'),  
(1, '7/1/2016','Inpatient'),  
(1, '8/1/2016','Inpatient'),  
(1, '8/4/2016','Inpatient'),  
(1, '8/15/2016','Inpatient'), 
(1, '8/18/2016','Inpatient'), 
(1, '8/28/2016','Inpatient'),
(1, '10/12/2016','Inpatient'),
(1, '10/15/2016','Inpatient'),
(1, '11/17/2016','Inpatient'),
(1, '12/20/2016','Inpatient')

;WITH a AS (
    SELECT
          z1.VisitDate
        , z1.MRN
        , (SELECT MIN(VisitDate) FROM  RiskReadmits WHERE VisitDate > DATEADD(day, 3, z1.VisitDate)) AS NextDay
    FROM
        RiskReadmits z1 
    WHERE
        CATEGORY = 'Inpatient'
), a1 AS ( 
    SELECT
          MRN
        , MIN(VisitDate) AS VisitDate
        , MIN(NextDay) AS NextDay
    FROM
        a
    GROUP BY
        MRN
), b AS (
    SELECT
          VisitDate
        , MRN
        , NextDay
        , 1 AS OrderRow
    FROM
        a1

    UNION ALL

    SELECT
          a.VisitDate
        , a.MRN
        , a.NextDay
        , b.OrderRow +1 AS OrderRow
    FROM
        a
        JOIN b
        ON a.VisitDate = b.NextDay
), c AS (
SELECT
    MRN,
    VisitDate
    , (SELECT MAX(VisitDate) FROM b WHERE b1.VisitDate > VisitDate AND b.MRN = b1.MRN) AS PreviousVisitDate 
FROM
    b b1
)
SELECT distinct
    c1.MRN,
    c1.VisitDate
    , CASE 
        WHEN DATEDIFF(day,c1.PreviousVisitDate,c1.VisitDate) < 30 THEN PreviousVisitDate
        ELSE NULL
     END AS ReAdmissionFrom
    , CASE
        WHEN DATEDIFF(day,c1.PreviousVisitDate,c1.VisitDate) < 30 THEN 3
        ELSE 0
    END AS Points
FROM
    c c1
ORDER BY c1.MRN

预期结果:

MRN VisitDate   ReAdmissionFrom Points
1   2016-01-02  NULL            0
1   2016-01-07  2016-01-02      3
1   2016-02-04  2016-01-07      3
1   2016-06-02  NULL            0
1   2016-06-06  2016-06-02      3
1   2016-07-01  2016-06-06      3
1   2016-08-01  NULL            0
1   2016-08-15  2016-08-01      3
1   2016-08-28  2016-08-15      3
1   2016-10-12  NULL            0
1   2016-11-17  NULL            0
1   2016-12-20  NULL            0

2 个答案:

答案 0 :(得分:1)

哎呀我改变了几个cte的名字(帖子搞砸了什么是代码)

应该是这样的:

b AS(     选择           VisitDate         ,MRN         , 明天         ,1 AS OrderRow     从         A1

UNION ALL

SELECT
      a.VisitDate
    , a.MRN
    , a.NextDay
    , b.OrderRow +1 AS OrderRow
FROM
    a AS a
    JOIN b
    ON a.VisitDate = b.NextDay AND a.MRN = b.MRN

答案 1 :(得分:0)

我会在这里猜测,并说你想把b cte更改为 将AND a.MRN = b.MRN作为第二个选择查询中的第二个条件,如下所示:

, b AS (
    SELECT
          VisitDate
        , MRN
        , NextDay
        , 1 AS OrderRow
    FROM
        firstVisitAndFollowUp

    UNION ALL

    SELECT
          a.VisitDate
        , a.MRN
        , a.NextDay
        , b.OrderRow +1 AS OrderRow
    FROM
        visitsDistance3daysOrMore AS a
        JOIN b
        ON a.VisitDate = b.NextDay AND a.MRN = b.MRN
)