简体   繁体   English

在SQL Server中使用CTE查询进行递归匹配

[英]Recursive Matching using CTE Query in SQL Server

I have two tables (they are defined below and you can use the SQL below to build them) 我有两个表(它们在下面定义,您可以使用下面的SQL来构建它们)

IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'ETab')
DROP TABLE ETab;
GO

CREATE TABLE ETab 
    ([MRN] varchar(20), [LSPEC] varchar(2), [ADT] DATETIME, [SDT] DATETIME, [Source] varchar(20), [Enum] varchar(20));
GO

INSERT INTO ETab ([MRN], [LSPEC], [ADT], [SDT], [Source], [Enum]) 
VALUES 
    ('HOMECARE',    'HM',       CONVERT(datetime, '2017-04-01 00:00:00.000', 20),   CONVERT(datetime, '2017-04-30 00:00:00.000', 20),   'PRODPAT',  'HOMEBLD04'),
    ('HOMECARE',    'HM',       CONVERT(datetime, '2017-05-01 00:00:00.000', 20),   CONVERT(datetime, '2017-05-31 00:00:00.000', 20),   'PRODPAT',  'HOMEBLD05'),
    ('HOMECARE',    'HM',       CONVERT(datetime, '2017-06-01 00:00:00.000', 20),   CONVERT(datetime, '2017-06-30 00:00:00.000', 20),   'PRODPAT',  'HOMEBLD06'),
    ('HOMECARE',    'HM',       CONVERT(datetime, '2017-07-01 00:00:00.000', 20),   CONVERT(datetime, '2017-07-31 00:00:00.000', 20),   'PRODPAT',  'HOMEBLD07'),
    ('HOMECARE',    'HM',       CONVERT(datetime, '2017-08-01 00:00:00.000', 20),   CONVERT(datetime, '2017-08-31 00:00:00.000', 20),   'PRODPAT',  'HOMEBLD08'),
    ('HOMECARE',    'HM',       CONVERT(datetime, '2017-09-01 00:00:00.000', 20),   CONVERT(datetime, '2017-09-30 00:00:00.000', 20),   'PRODPAT',  'HOMEBLD09'),
    ('HOMECARE',    'HQ',       CONVERT(datetime, '2017-04-01 00:00:00.000', 20),   CONVERT(datetime, '2017-04-30 00:00:00.000', 20),   'PRODPAT',  'HOMEDRG04HM'),
    ('HOMECARE',    'HM',       CONVERT(datetime, '2017-05-01 00:00:00.000', 20),   CONVERT(datetime, '2017-05-31 00:00:00.000', 20),   'PRODPAT',  'HOMEDRG05HM'),
    ('HOMECARE',    'HM',       CONVERT(datetime, '2017-06-01 00:00:00.000', 20),   CONVERT(datetime, '2017-06-30 00:00:00.000', 20),   'PRODPAT',  'HOMEDRG06HM'),
    ('HOMECARE',    'HM',       CONVERT(datetime, '2017-07-01 00:00:00.000', 20),   CONVERT(datetime, '2017-07-31 00:00:00.000', 20),   'PRODPAT',  'HOMEDRG07HM'),
    ('HOMECARE',    'HM',       CONVERT(datetime, '2017-08-01 00:00:00.000', 20),   CONVERT(datetime, '2017-08-31 00:00:00.000', 20),   'PRODPAT',  'HOMEDRG08HM'),
    ('HOMECARE',    'HM',       CONVERT(datetime, '2017-09-01 00:00:00.000', 20),   CONVERT(datetime, '2017-09-30 00:00:00.000', 20),   'PRODPAT',  'HOMEDRG09HM'), 
    ('111824',      'UR',       CONVERT(datetime, '2017-09-22 00:00:00.000', 20),   CONVERT(datetime, '2017-09-22 00:00:00.000', 20),   'OP',   'OP1118240003'),
    ('111824',      'NL',       CONVERT(datetime, '2017-04-19 00:00:00.000', 20),   CONVERT(datetime, '2017-04-19 00:00:00.000', 20),   'OP',   'OP1118240001'),
    ('111824',      'MS',       CONVERT(datetime, '2017-06-30 00:00:00.000', 20),   CONVERT(datetime, '2017-06-30 00:00:00.000', 20),   'OP',   'OP1118240002'),
    ('111824',      'MS',       CONVERT(datetime, '2017-04-24 00:00:00.000', 20),   CONVERT(datetime, '2017-04-24 00:00:00.000', 20),   'IP',   'IP1118240001'),
    ('111824',      'MS',       CONVERT(datetime, '2017-04-28 00:00:00.000', 20),   CONVERT(datetime, '2017-04-28 00:00:00.000', 20),   'IP',   'IP1118240005'),
    ('111824',      'MS',       CONVERT(datetime, '2017-04-27 00:00:00.000', 20),   CONVERT(datetime, '2017-04-27 00:00:00.000', 20),   'IP',   'IP1118240004'),
    ('111824',      'MS',       CONVERT(datetime, '2017-04-26 00:00:00.000', 20),   CONVERT(datetime, '2017-04-26 00:00:00.000', 20),   'IP',   'IP1118240003'),
    ('111824',      'MS',       CONVERT(datetime, '2017-04-25 00:00:00.000', 20),   CONVERT(datetime, '2017-04-25 00:00:00.000', 20),   'IP',   'IP1118240002');
GO

IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'UTab')
DROP TABLE UTab;
GO

CREATE TABLE UTab 
    (MRN varchar(20), SIDate DATETIME, LSPEC varchar(2), Source varchar(20), Enum varchar(20), Iteration varchar(20));
GO

INSERT INTO UTab 
    (MRN, SIDate, LSPEC, Source, Enum, Iteration) 
VALUES 
('HOMECARE', CONVERT(datetime, '2017-04-20 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-04-20 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-04-20 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-04-20 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-04-20 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-04-20 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-04-30 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-04-30 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-04-30 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-04-20 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-04-30 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-04-30 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-17 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-04-30 00:00:00.000', 20), 'HQ', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-04-30 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-04-30 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-30 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-01 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-01 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-01 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-01 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-01 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-01 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-30 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-01 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-26 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-26 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-26 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-26 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-26 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-26 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-26 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-26 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-26 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-26 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-26 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-26 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-26 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-26 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-26 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-26 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-26 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-06-26 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-30 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-30 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-04 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-04 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-04 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-04 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-04 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-04 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-04 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-04 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-04 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-04 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-04 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-04 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-04 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-04 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-04 00:00:00.000', 20), 'HM', 'N', NULL, NULL), 
('HOMECARE', CONVERT(datetime, '2017-05-04 00:00:00.000', 20), 'HM', 'N', NULL, NULL),
('111824', CONVERT(datetime, '2017-04-21 00:00:00.000', 20), 'MS', 'IP', NULL, NULL), 
('111824', CONVERT(datetime, '2017-04-24 00:00:00.000', 20), 'NL', 'OP', NULL, NULL), 
('111824', CONVERT(datetime, '2017-04-27 00:00:00.000', 20), 'NL', 'OP', NULL, NULL), 
('111824', CONVERT(datetime, '2017-04-20 00:00:00.000', 20), 'NL', 'OP', NULL, NULL), 
('111824', CONVERT(datetime, '2017-04-20 00:00:00.000', 20), 'NL', 'OP', NULL, NULL), 
('111824', CONVERT(datetime, '2017-04-20 00:00:00.000', 20), 'NL', 'OP', NULL, NULL);
GO

SELECT * FROM ETab
WHERE Source = 'PRODPAT' AND LSPEC = 'HM' 
GO

SELECT * FROM UTab  
WHERE LSPEC = 'HM';
GO

IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '__Tmp')
DROP TABLE __Tmp;
GO

I have a C# application in which the user requests certian matching clauses at run-time. 我有一个C#应用程序,用户在运行时请求certian匹配子句。 Using the information specified by the user, I generate some SQL that is executed and updates UTab . 使用用户指定的信息,我生成一些执行的SQL并更新UTab

Via the generated SQL, I wish to perform some matching which updates the [UTab].[Enum] based on the link field [MRN] and whether [UTab].[SIDate] lies within [ETab].[ADT] and [ETab].[SDT] +- some days before or after which are specified by the user. 通过生成的SQL,我希望执行一些匹配,根据链接字段[MRN]更新[UTab].[Enum] [MRN]以及[UTab].[SIDate]位于[ETab].[ADT][ETab].[SDT] [ETab].[ADT] [ETab].[SDT] + - 用户指定的前几天或之后的某天。 So, the user might specify two commands, the first: 因此,用户可以指定两个命令,第一个:

MatchCmd:MRN,LSPEC:ETab:UTab:ADT:SDT:SIDate:0:1:'Iteration#1':WHERE [UTab].[Source] = 'OP' AND [UTab].[LSPEC] = [ETab].[LSPEC] 

This says, match on MRN AND LSPEC from source table ETab , updating UTab where SIDate > ADT - 0 day(s) and SIDate < SDT + 1 day(s) and where [UTab].[Source] = 'OP' - any matches I mark with the "iteration number" Iteration#1 . 这说,从源表ETab匹配MRNLSPEC ,更新UTab ,其中SIDate > ADT - 0 day(s)SIDate < SDT + 1 day(s)[UTab].[Source] = 'OP' - 任何匹配我用“迭代次数” Iteration#1标记。

My code generates the following SQL: 我的代码生成以下SQL:

;WITH cte AS (
    SELECT [ETab].[Enum] AS Enum, 
           [ETab].[MRN] AS Link, 
           [ETab].[ADT] AS ADT, 
           [ETab].[SDT] AS SDT, 
           [UTab].[SIDate] AS DT, 
           [ETab].[MRN] AS [MRN], 
           [ETab].[LSPEC] AS [LSPEC], 
           [ETab].[Source] AS [Source], 
           ROW_NUMBER() OVER (PARTITION BY [UTab].[MRN], [UTab].[LSPEC], [UTab].[SIDate] 
                ORDER BY ABS(DATEDIFF(mi, [UTab].[SIDate], [ETab].[ADT]))) AS Idx, 
           ABS(DATEDIFF(mi, [UTab].[SIDate], [ETab].[ADT])) AS Diff 
    FROM [UTab] 
        LEFT JOIN [ETab] ON [UTab].[MRN] = [ETab].[MRN] 
    WHERE ([UTab].[SIDate] BETWEEN 
        DATEADD(dd, -0, [ETab].[ADT]) AND 
        DATEADD(dd, 1, [ETab].[SDT]) AND [Iteration] IS NULL) 
            AND ETab.Source = 'OP' 
)  SELECT * 
   INTO __Tmp 
   FROM cte; 
GO

To get all the records that match the criteria (laying within the time window and abiding by custom where clauses). 获取符合条件的所有记录(在时间窗口内放置并遵守自定义where子句)。 Then I Update the [UTab].[Enum] and [UTab].[Iteration] via another generated query 然后我通过另一个生成的查询更新[UTab].[Enum][UTab].[Iteration]

UPDATE [UTab] 
SET [ENum] = [__Tmp].[ENum], [Iteration] = N'Iteration#1' 
--SELECT __Tmp.ENum, __Tmp.Link, __Tmp.LSPEC, __Tmp.ADT, __Tmp.SDT, __Tmp.DT, __Tmp.Idx 
FROM [UTab] AS up 
    INNER JOIN [__Tmp] 
        ON [up].[MRN] = [__Tmp].[Link] 
           AND [up].[SIDate] = [__Tmp].[DT] 
           AND [up].[LSPEC] = [__Tmp].[LSPEC] 
WHERE __Tmp.Idx = 1;

This seems to work okay, but some questions: 这似乎工作正常,但有些问题:

Q. Is there anything clearly ary with the method/SQL I am using? 问:我使用的方法/ SQL有什么明显的问题吗?

Thanks for your time. 谢谢你的时间。

up.L != cte.L since you are looking for anything conforming your conditions and filtering by rn . up.L != cte.L因为您正在寻找符合您条件并按rn过滤的任何内容。

SELECT cte.E, [Iteration] = N'00-00-00-CA', *
FROM [Up] 
    INNER JOIN cte ON [Up].[M] = [cte].[M] AND [cte].[rn] = 1
WHERE [cte].[E] IS NOT NULL AND (
    [Up].[DTE] BETWEEN 
        DATEADD(dd, -0, [cte].[ADT]) AND 
        DATEADD(dd, 0, [cte].[SDT])) 
            AND [Up].[F] = 'Y' 
            AND [Up].[S] = 'HC' 
            ----comment this line
            --AND [Up].[L] = [cte].[L]; -- <<<<<<<<<<<<<<<<

http://sqlfiddle.com/#!18/d1483/2/0 http://sqlfiddle.com/#!18/d1483/2/0

I modified some data: added ZZ which broke your query totally. 我修改了一些数据:添加ZZ完全破坏了你的查询。 Only two rows became matching. 只有两行匹配。

ps fixed insert issue with length of E column and col name listed in first insert. ps固定插入问题,第一个插入中列出了E列和列名称的长度。

The CTE usage was a bit strange to me, since you're not really doing much with it in the subsequent query. CTE的用法对我来说有点奇怪,因为在后续查询中你并没有真正做很多事情。 I'd move that to the UPDATE. 我会把它移到更新。

The query doesn't really join on the Source . 该查询并未真正加入Source I'm not sure if it's meant to do that or not. 我不确定这是否意味着这样做。 If UTab has multiple sources for MRN/LSPEC combination, that might result in an issue. 如果UTab有多个MRN / LSPEC组合源,则可能会导致问题。

So, I come up with something like: 所以,我想出了类似的东西:

DECLARE @ADT_Adjustment INT = 0;
DECLARE @SDT_Adjustment INT = 1;
DECLARE @Iteration INT = 1;

WITH SequencedJoin AS (
    SELECT
        ETab.MRN, ETab.LSPEC, ETab.ADT, ETab.SDT, UTab.SIDate, ETab.Enum, ETab.[Source], UTab.Enum AS WriteEnum, UTab.Iteration AS WriteIteration
    ,   DENSE_RANK() OVER ( 
            PARTITION BY UTab.MRN, UTab.LSPEC, UTab.[Source], UTab.SIDate 
            ORDER BY ABS( DATEDIFF( MINUTE, UTab.SIDate, ETab.ADT ) ) 
        ) AS Ordinal
    FROM
        @UTab AS UTab
    JOIN    @ETab AS ETab ON (
            ETab.MRN = UTab.MRN
        AND ETab.LSPEC = UTab.LSPEC
        AND ETab.[Source] = UTab.[Source]
        AND UTab.SIDate BETWEEN DATEADD( dd, -@ADT_Adjustment, ETab.ADT ) AND DATEADD( dd, @SDT_Adjustment, ETab.SDT )
        )
    WHERE
        UTab.Iteration IS NULL
)
UPDATE
    SequencedJoin
SET
    WriteEnum = SequencedJoin.Enum
,   WriteIteration = N'Iteration#' + CAST( @Iteration AS VARCHAR( 2 ) )
WHERE
    SequencedJoin.[Source] = 'OP'
AND SequencedJoin.Ordinal = 1

Not a complete answer as such, but this index will speed up you CTE: 这不是一个完整的答案,但这个指数会加速你CTE:

CREATE INDEX T1 ON UTAB (
    MRN,
    SIDATE
)
INCLUDE
(
    LSPEC,
    Iteration
)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM