Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)

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. Using the information specified by the user, I generate some SQL that is executed and updates 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. 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.

My code generates the following 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). Then I Update the [UTab].[Enum] and [UTab].[Iteration] via another generated query

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?

Thanks for your time.

like image 344
MoonKnight Avatar asked Feb 28 '18 14:02

MoonKnight


1 Answers

The CTE usage was a bit strange to me, since you're not really doing much with it in the subsequent query. I'd move that to the UPDATE.

The query doesn't really join on the 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.

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
like image 59
Nik Shenoy Avatar answered Oct 26 '22 05:10

Nik Shenoy