Requête sans boucle WHILE

18

Nous avons un tableau de rendez-vous comme indiqué ci-dessous. Chaque rendez-vous doit être classé comme "Nouveau" ou "Suivi". Tout rendez-vous (pour un patient) dans les 30 jours suivant le premier rendez-vous (de ce patient) est un suivi. Après 30 jours, le rendez-vous est à nouveau "Nouveau". Tout rendez-vous dans les 30 jours devient "Suivi".

Je fais actuellement cela en tapant en boucle.
Comment y parvenir sans boucle WHILE?

entrez la description de l'image ici

Table

CREATE TABLE #Appt1 (ApptID INT, PatientID INT, ApptDate DATE)
INSERT INTO #Appt1
SELECT  1,101,'2020-01-05' UNION
SELECT  2,505,'2020-01-06' UNION
SELECT  3,505,'2020-01-10' UNION
SELECT  4,505,'2020-01-20' UNION
SELECT  5,101,'2020-01-25' UNION
SELECT  6,101,'2020-02-12'  UNION
SELECT  7,101,'2020-02-20'  UNION
SELECT  8,101,'2020-03-30'  UNION
SELECT  9,303,'2020-01-28' UNION
SELECT  10,303,'2020-02-02' 
LCJ
la source
Je ne vois pas votre image, mais je veux confirmer, s'il y a 3 rendez-vous, tous les 20 jours les uns des autres, le dernier est toujours "suivi" à droite, car même si c'est plus de 30 jours à partir du premier, c'est encore à moins de 20 jours du milieu. Est-ce vrai?
pwilcox
@pwilcox No. Le troisième sera un nouveau rendez-vous comme indiqué dans l'image
LCJ
Bien que la boucle sur le fast_forwardcurseur soit probablement votre meilleure option, en termes de performances.
David דודו Markovitz

Réponses:

14

Vous devez utiliser une requête récursive.

La période de 30 jours est comptée à partir de prev (et non il n'est pas possible de le faire sans récursion / mise à jour / boucle excentrique). C'est pourquoi toutes les réponses existantes en utilisant seulement ont ROW_NUMBERéchoué.

WITH f AS (
  SELECT *, rn = ROW_NUMBER() OVER(PARTITION BY PatientId ORDER BY ApptDate) 
  FROM Appt1
), rec AS (
  SELECT Category = CAST('New' AS NVARCHAR(20)), ApptId, PatientId, ApptDate, rn, startDate = ApptDate
  FROM f
  WHERE rn = 1
  UNION ALL
  SELECT CAST(CASE WHEN DATEDIFF(DAY,  rec.startDate,f.ApptDate) <= 30 THEN N'FollowUp' ELSE N'New' END AS NVARCHAR(20)), 
         f.ApptId,f.PatientId,f.ApptDate, f.rn,
         CASE WHEN DATEDIFF(DAY, rec.startDate, f.ApptDate) <= 30 THEN rec.startDate ELSE f.ApptDate END
  FROM rec
  JOIN f
    ON rec.rn = f.rn - 1
   AND rec.PatientId = f.PatientId
)
SELECT ApptId, PatientId, ApptDate, Category
FROM rec
ORDER BY PatientId, ApptDate;  

démo db <> violon

Production:

+---------+------------+-------------+----------+
| ApptId  | PatientId  |  ApptDate   | Category |
+---------+------------+-------------+----------+
|      1  |       101  | 2020-01-05  | New      |
|      5  |       101  | 2020-01-25  | FollowUp |
|      6  |       101  | 2020-02-12  | New      |
|      7  |       101  | 2020-02-20  | FollowUp |
|      8  |       101  | 2020-03-30  | New      |
|      9  |       303  | 2020-01-28  | New      |
|     10  |       303  | 2020-02-02  | FollowUp |
|      2  |       505  | 2020-01-06  | New      |
|      3  |       505  | 2020-01-10  | FollowUp |
|      4  |       505  | 2020-01-20  | FollowUp |
+---------+------------+-------------+----------+

Comment ça fonctionne:

  1. f - obtenir le point de départ (ancre - pour chaque PatientId)
  2. rec - partie recursive, si la différence entre la valeur actuelle et prev est> 30 changer la catégorie et le point de départ, dans le contexte de PatientId
  3. Principal - afficher les résultats triés

Classe similaire:

Somme conditionnelle sur Oracle - Plafonnement d'une fonction fenêtrée

Fenêtre de session (Azure Stream Analytics)

Exécution de Total jusqu'à ce qu'une condition spécifique soit remplie - Mise à jour Quirky


Addenda

N'utilisez jamais ce code en production!

Mais une autre option, qui mérite d'être mentionnée en plus d'utiliser cte, est d'utiliser la table temporaire et la mise à jour en "rounds"

Cela pourrait être fait en "simple" round (mise à jour excentrique):

CREATE TABLE Appt_temp (ApptID INT , PatientID INT, ApptDate DATE, Category NVARCHAR(10))

INSERT INTO Appt_temp(ApptId, PatientId, ApptDate)
SELECT ApptId, PatientId, ApptDate
FROM Appt1;

CREATE CLUSTERED INDEX Idx_appt ON Appt_temp(PatientID, ApptDate);

Requete:

DECLARE @PatientId INT = 0,
        @PrevPatientId INT,
        @FirstApptDate DATE = NULL;

UPDATE Appt_temp
SET  @PrevPatientId = @PatientId
    ,@PatientId     = PatientID 
    ,@FirstApptDate = CASE WHEN @PrevPatientId <> @PatientId THEN ApptDate
                           WHEN DATEDIFF(DAY, @FirstApptDate, ApptDate)>30 THEN ApptDate
                           ELSE @FirstApptDate
                      END
    ,Category       = CASE WHEN @PrevPatientId <> @PatientId THEN 'New'
                           WHEN @FirstApptDate = ApptDate THEN 'New'
                           ELSE 'FollowUp' 
                      END
FROM Appt_temp WITH(INDEX(Idx_appt))
OPTION (MAXDOP 1);

SELECT * FROM  Appt_temp ORDER BY PatientId, ApptDate;

mise à jour db <> fiddle Quirky

Lukasz Szozda
la source
1
votre logique ressemble beaucoup à la mienne. Pouvez-vous décrire des différences importantes?
pwilcox
@pwilcox Lorsque j'ai écrit cette réponse, toutes les réponses existantes utilisaient un simple row_number qui ne fonctionnait pas, c'est pourquoi j'ai fourni ma propre version
Lukasz Szozda
Ouais, j'étais trop rapide avec la réponse. Merci d'avoir commenté cela.
Irdis
2
Je crois que rcte est la seule solution pour cela jusqu'à ce que SQL Server implémente correctement la RANGE x PRECEDINGclause.
Salman A
1
La mise à jour de @LCJ Quirky est basée sur un comportement "non documenté" et peut changer à tout moment sans préavis ( red-gate.com/simple-talk/sql/learn-sql-server/… )
Lukasz Szozda
5

Vous pouvez le faire avec un cte récursif. Vous devez d'abord commander par apptDate au sein de chaque patient. Cela peut être accompli par un cte ordinaire.

Ensuite, dans la partie d'ancrage de votre cte récursif, sélectionnez la première commande pour chaque patient, marquez le statut comme `` nouveau '' et marquez également l'apptDate comme date du `` nouveau '' enregistrement le plus récent.

Dans la partie récursive de votre cte récursif, augmentez jusqu'au prochain rendez-vous, calculez la différence en jours entre le rendez-vous actuel et la «nouvelle» date de rendez-vous la plus récente. S'il est supérieur à 30 jours, marquez-le comme «nouveau» et réinitialisez la date de rendez-vous la plus récente. Sinon, marquez-le comme «suivi» et transmettez simplement les jours existants depuis la nouvelle date de rendez-vous.

Enfin, dans la requête de base, sélectionnez simplement les colonnes souhaitées.

with orderings as (

    select       *, 
                 rn = row_number() over(
                     partition by patientId 
                     order by apptDate
                 ) 
    from         #appt1 a

),

markings as (

    select       apptId, 
                 patientId, 
                 apptDate, 
                 rn, 
                 type = convert(varchar(10),'new'),
                 dateOfNew = apptDate
    from         orderings 
    where        rn = 1

    union all
    select       o.apptId, o.patientId, o.apptDate, o.rn,
                 type = convert(varchar(10),iif(ap.daysSinceNew > 30, 'new', 'follow up')),
                 dateOfNew = iif(ap.daysSinceNew > 30, o.apptDate, m.dateOfNew)
    from         markings m
    join         orderings o 
                     on m.patientId = o.patientId 
                     and m.rn + 1 = o.rn
    cross apply  (select daysSinceNew = datediff(day, m.dateOfNew, o.apptDate)) ap

)

select    apptId, patientId, apptDate, type
from      markings
order by  patientId, rn;

Je dois mentionner que j'ai initialement supprimé cette réponse car la réponse d'Abhijeet Khandagale semblait répondre à vos besoins avec une requête plus simple (après l'avoir un peu retravaillée). Mais avec votre commentaire sur vos besoins commerciaux et vos données d'échantillon ajoutées, j'ai supprimé le mien car je crois que celui-ci répond à vos besoins.

pwilcox
la source
4

Je ne suis pas sûr que c'est exactement ce que vous avez mis en œuvre. Mais une autre option, qui mérite d'être mentionnée en plus d'utiliser cte, est d'utiliser la table temporaire et la mise à jour en "rounds". Nous allons donc mettre à jour la table temporaire alors que tous les statuts ne sont pas définis correctement et générer le résultat de manière itérative. Nous pouvons contrôler le nombre d'itérations en utilisant simplement une variable locale.

Nous avons donc divisé chaque itération en deux étapes.

  1. Définissez toutes les valeurs de suivi qui sont proches de Nouveaux enregistrements. C'est assez facile à faire en utilisant simplement le bon filtre.
  2. Pour le reste des enregistrements qui n'ont pas de statut défini, nous pouvons sélectionner d'abord dans le groupe avec le même PatientID. Et dire qu'ils sont nouveaux puisqu'ils n'ont pas été traités par la première étape.

Donc

CREATE TABLE #Appt2 (ApptID INT, PatientID INT, ApptDate DATE, AppStatus nvarchar(100))

select * from #Appt1
insert into #Appt2 (ApptID, PatientID, ApptDate, AppStatus)
select a1.ApptID, a1.PatientID, a1.ApptDate, null from #Appt1 a1
declare @limit int = 0;

while (exists(select * from #Appt2 where AppStatus IS NULL) and @limit < 1000)
begin
  set @limit = @limit+1;
  update a2
  set
    a2.AppStatus = IIF(exists(
        select * 
        from #Appt2 a 
        where 
          0 > DATEDIFF(day, a2.ApptDate, a.ApptDate) 
          and DATEDIFF(day, a2.ApptDate, a.ApptDate) > -30 
          and a.ApptID != a2.ApptID 
          and a.PatientID = a2.PatientID
          and a.AppStatus = 'New'
          ), 'Followup', a2.AppStatus)
  from #Appt2 a2

  --select * from #Appt2

  update a2
  set a2.AppStatus = 'New'
  from #Appt2 a2 join (select a.*, ROW_NUMBER() over (Partition By PatientId order by ApptId) rn from (select * from #Appt2 where AppStatus IS NULL) a) ar
  on a2.ApptID = ar.ApptID
  and ar.rn = 1

  --select * from #Appt2

end

select * from #Appt2 order by PatientID, ApptDate

drop table #Appt1
drop table #Appt2

Mise à jour. Lisez le commentaire fourni par Lukasz. C'est de loin beaucoup plus intelligent. Je laisse ma réponse juste comme une idée.

Irdis
la source
4

Je pense que l'expression commune récursive est un excellent moyen d'optimiser les requêtes en évitant les boucles, mais dans certains cas, elle peut entraîner de mauvaises performances et doit être évitée si possible.

J'utilise le code ci-dessous pour résoudre le problème et le tester avec plus de valeurs, mais je vous encourage également à le tester avec vos données réelles.

WITH DataSource AS
(
    SELECT *
          ,CEILING(DATEDIFF(DAY, MIN([ApptDate]) OVER (PARTITION BY [PatientID]), [ApptDate]) * 1.0 / 30 + 0.000001) AS [GroupID]
    FROM #Appt1
)
SELECT *
     ,IIF(ROW_NUMBER() OVER (PARTITION BY [PatientID], [GroupID] ORDER BY [ApptDate]) = 1, 'New', 'Followup')
FROM DataSource
ORDER BY [PatientID]
        ,[ApptDate];

entrez la description de l'image ici

L'idée est assez simple - je veux séparer les enregistrements en groupe (30 jours), dans quel groupe se trouve le plus petit enregistrement new, les autres le sont follow ups. Vérifiez comment l'instruction est construite:

SELECT *
      ,DATEDIFF(DAY, MIN([ApptDate]) OVER (PARTITION BY [PatientID]), [ApptDate])
      ,DATEDIFF(DAY, MIN([ApptDate]) OVER (PARTITION BY [PatientID]), [ApptDate]) * 1.0 / 30
      ,CEILING(DATEDIFF(DAY, MIN([ApptDate]) OVER (PARTITION BY [PatientID]), [ApptDate]) * 1.0 / 30 + 0.000001) 
FROM #Appt1
ORDER BY [PatientID]
        ,[ApptDate];

entrez la description de l'image ici

Donc:

  1. tout d'abord, nous obtenons la première date, pour chaque groupe et calculons les différences de jours avec le jour actuel
  2. ensuite, nous voulons avoir des groupes - * 1.0 / 30est ajouté
  3. comme pour 30, 60, 90, etc. jours, nous obtenons un nombre entier et nous voulions commencer une nouvelle période, j'ai ajouté + 0.000001; aussi, nous utilisons la fonction plafond pour obtenir lesmallest integer greater than, or equal to, the specified numeric expression

C'est ça. Ayant un tel groupe, nous utilisons simplement ROW_NUMBERpour trouver notre date de début et le faire comme newet laisser le reste comme follow ups.

gotqn
la source
2
Eh bien, la question est un peu différente et cette approche est une simplification excessive. Mais c'est un bel exemple comment implémenter une fenêtre de culbutage
Lukasz Szozda
C'est aussi une question de performance. Je pense que la récursivité devrait être plus lente.
gotqn
3

Avec tout le respect que je vous dois et à mon humble avis,

There is not much difference between While LOOP and Recursive CTE in terms of RBAR

Il n'y a pas beaucoup de gain de performances lors de l'utilisation Recursive CTEet Window Partition functiontout en un.

Appiddevrait être int identity(1,1), ou il devrait être en constante augmentation clustered index.

Outre d'autres avantages, il garantit également que toutes les rangées successives APPDatede ce patient doivent être plus grandes.

De cette façon, vous pouvez facilement jouer avec APPIDvotre requête, ce qui sera plus efficace que de mettre un inequalityopérateur comme>, <dans APPDate. Mettre l' inequalityopérateur comme>, <dans APPID aidera Sql Optimizer.

Il devrait également y avoir deux colonnes de date dans le tableau comme

APPDateTime datetime2(0) not null,
Appdate date not null

Comme ce sont les colonnes les plus importantes du tableau le plus important, donc pas beaucoup de conversion, convertissez.

Non clustered indexPeut donc être créé sur Appdate

Create NonClustered index ix_PID_AppDate_App  on APP (patientid,APPDate) include(other column which is not i predicate except APPID)

Testez mon script avec d'autres exemples de données et laissez-moi savoir pour quels exemples de données il ne fonctionne pas. Même si cela ne fonctionne pas, je suis sûr qu'il peut être corrigé dans ma logique de script elle-même.

CREATE TABLE #Appt1 (ApptID INT, PatientID INT, ApptDate DATE)
INSERT INTO #Appt1
SELECT  1,101,'2020-01-05'  UNION ALL
SELECT  2,505,'2020-01-06'  UNION ALL
SELECT  3,505,'2020-01-10'  UNION ALL
SELECT  4,505,'2020-01-20'  UNION ALL
SELECT  5,101,'2020-01-25'  UNION ALL
SELECT  6,101,'2020-02-12'  UNION ALL
SELECT  7,101,'2020-02-20'  UNION ALL
SELECT  8,101,'2020-03-30'  UNION ALL
SELECT  9,303,'2020-01-28'  UNION ALL
SELECT  10,303,'2020-02-02' 

;With CTE as
(
select a1.* ,a2.ApptDate as NewApptDate
from #Appt1 a1
outer apply(select top 1 a2.ApptID ,a2.ApptDate
from #Appt1 A2 
where a1.PatientID=a2.PatientID and a1.ApptID>a2.ApptID 
and DATEDIFF(day,a2.ApptDate, a1.ApptDate)>30
order by a2.ApptID desc )A2
)
,CTE1 as
(
select a1.*, a2.ApptDate as FollowApptDate
from CTE A1
outer apply(select top 1 a2.ApptID ,a2.ApptDate
from #Appt1 A2 
where a1.PatientID=a2.PatientID and a1.ApptID>a2.ApptID 
and DATEDIFF(day,a2.ApptDate, a1.ApptDate)<=30
order by a2.ApptID desc )A2
)
select  * 
,case when FollowApptDate is null then 'New' 
when NewApptDate is not null and FollowApptDate is not null 
and DATEDIFF(day,NewApptDate, FollowApptDate)<=30 then 'New'
else 'Followup' end
 as Category
from cte1 a1
order by a1.PatientID

drop table #Appt1
KumarHarsh
la source
3

Bien que cela ne soit pas clairement abordé dans la question, il est facile de comprendre que les dates de rendez-vous ne peuvent pas être simplement classées par groupes de 30 jours. Cela n'a aucun sens commercial. Et vous ne pouvez pas non plus utiliser l'ID appt. On peut prendre un nouveau rendez-vous aujourd'hui pour2020-09-06. Voici comment j'aborde ce problème. Tout d'abord, obtenez le premier rendez-vous, puis calculez la différence de date entre chaque rendez-vous et le premier rendez-vous. Si c'est 0, réglez sur 'Nouveau'. Si <= 30 'Suivi'. Si> 30, définissez comme «Indécis» et effectuez le prochain tour jusqu'à ce qu'il n'y ait plus «Indécis». Et pour cela, vous avez vraiment besoin d'une boucle while, mais elle ne passe pas par chaque date de rendez-vous, plutôt que quelques ensembles de données. J'ai vérifié le plan d'exécution. Même s'il n'y a que 10 lignes, le coût de la requête est nettement inférieur à celui utilisant le CTE récursif, mais pas aussi bas que la méthode d'addendum de Lukasz Szozda.

IF OBJECT_ID('tempdb..#TEMPTABLE') IS NOT NULL DROP TABLE #TEMPTABLE
SELECT ApptID, PatientID, ApptDate
    ,CASE WHEN (DATEDIFF(DAY, MIN(ApptDate) OVER (PARTITION BY PatientID), ApptDate) = 0) THEN 'New' 
    WHEN (DATEDIFF(DAY, MIN(ApptDate) OVER (PARTITION BY PatientID), ApptDate) <= 30) THEN 'Followup'
    ELSE 'Undecided' END AS Category
INTO #TEMPTABLE
FROM #Appt1

WHILE EXISTS(SELECT TOP 1 * FROM #TEMPTABLE WHERE Category = 'Undecided') BEGIN
    ;WITH CTE AS (
        SELECT ApptID, PatientID, ApptDate 
            ,CASE WHEN (DATEDIFF(DAY, MIN(ApptDate) OVER (PARTITION BY PatientID), ApptDate) = 0) THEN 'New' 
            WHEN (DATEDIFF(DAY, MIN(ApptDate) OVER (PARTITION BY PatientID), ApptDate) <= 30) THEN 'Followup'
            ELSE 'Undecided' END AS Category    
        FROM #TEMPTABLE
        WHERE Category = 'Undecided'
    )
    UPDATE #TEMPTABLE
    SET Category = CTE.Category
    FROM #TEMPTABLE t
        LEFT JOIN CTE ON CTE.ApptID = t.ApptID
    WHERE t.Category = 'Undecided'
END

SELECT ApptID, PatientID, ApptDate, Category 
FROM #TEMPTABLE
Weihui Guo
la source
2

J'espère que cela t'aidera.

WITH CTE AS
(
    SELECT #Appt1.*, RowNum = ROW_NUMBER() OVER (PARTITION BY PatientID ORDER BY ApptDate, ApptID) FROM #Appt1
)

SELECT A.ApptID , A.PatientID , A.ApptDate ,
Expected_Category = CASE WHEN (DATEDIFF(MONTH, B.ApptDate, A.ApptDate) > 0) THEN 'New' 
WHEN (DATEDIFF(DAY, B.ApptDate, A.ApptDate) <= 30) then 'Followup' 
ELSE 'New' END
FROM CTE A
LEFT OUTER JOIN CTE B on A.PatientID = B.PatientID 
AND A.rownum = B.rownum + 1
ORDER BY A.PatientID, A.ApptDate
Abhijeet Khandagale
la source
Merci @ x00 d'avoir édité le code dans un format lisible, j'utilise mon téléphone portable pour poster des réponses et je n'ai donc pas pu donner d'indentations appropriées.
Abhijeet Khandagale
Je pense que c'est essentiellement la bonne réponse. Mais c'est une réponse de mauvaise qualité dans la mesure où elle n'est pas expliquée et le code a une requête externe inutile lorsqu'une modification de la partie interne fera l'affaire. Si vous pouvez résoudre ces problèmes, je serai heureux de vous voter.
pwilcox
1
@pwilcox, merci pour la précieuse suggestion, j'ai édité la réponse et l'ai publiée dès maintenant. Comme je voyage et que je n'ai pas d'ordinateur portable avec moi, je posterai des explications dans un jour ou deux.
Abhijeet Khandagale
1
@AbhijeetKhandagale Cela ne répond pas complètement aux exigences commerciales. J'ai ajouté un scénario d'échec dans la question. Pour le patient 303, le rendez-vous du 2 février devrait être un suivi; mais votre requête indique que c'est "Nouveau"
LCJ
1

Vous pouvez utiliser une Casedéclaration .

select 
      *, 
      CASE 
          WHEN DATEDIFF(d,A1.ApptDate,A2.ApptDate)>30 THEN 'New' 
          ELSE 'FollowUp' 
      END 'Category'
from 
      (SELECT PatientId, MIN(ApptId) 'ApptId', MIN(ApptDate) 'ApptDate' FROM #Appt1 GROUP BY PatientID)  A1, 
      #Appt1 A2 
where 
     A1.PatientID=A2.PatientID AND A1.ApptID<A2.ApptID

La question est de savoir si cette catégorie devrait être attribuée en fonction de la nomination initiale ou de la précédente. Autrement dit, si un patient a eu trois rendez-vous, devrions-nous comparer le troisième rendez-vous au premier ou au deuxième?

Votre problème énonce le premier, c'est ainsi que j'ai répondu. Si ce n'est pas le cas, vous voudrez l'utiliser lag.

N'oubliez DateDiffpas non plus que les week-ends ne font pas exception. Si cela ne doit être que les jours de la semaine, vous devrez créer votre propre fonction à valeur scalaire.

utilisateur
la source
1
Cela ne lie pas deux rendez-vous séquentiels, cela relie l'appt 1 à tous les rendez-vous suivants et calcule les jours entre tous. Vous renverriez trop d'enregistrements de cette façon, car appt 1 a maintenant une relation avec 2, 3, 4, appt 2 a une relation avec 3, 4 ...
steenbergh
Bon point. J'ai mis à jour ma réponse pour faire une sous-sélection pour A1.
utilisateur
1
Il ne donne pas le résultat attendu. Le rendez-vous du 20 février devrait être "Suivi"
LCJ
La question n'est pas claire ... La description de l'affiche est la suivante: "Tout rendez-vous (pour un patient) dans les 30 jours suivant le premier rendez-vous (de ce patient) est suivi. Après 30 jours, le rendez-vous est à nouveau" Nouveau ". Tout rendez-vous dans les 30 jours devenir "Suivi". " Le 5 janvier est certainement à plus de 30 jours du 20 février, c'est-à-dire Nouveau. Ce n'est PAS à 30 jours du 12 février. J'offre une solution à ce qu'il a écrit, pas le tableau fourni. Si l'utilisateur souhaite s'aligner sur ce que la table fournit, il doit utiliser le décalage. Ils devraient également clarifier ...
utilisateur
1

en utilisant la fonction Lag


select  apptID, PatientID , Apptdate ,  
    case when date_diff IS NULL THEN 'NEW' 
         when date_diff < 30 and (date_diff_2 IS NULL or date_diff_2 < 30) THEN  'Follow Up'
         ELSE 'NEW'
    END AS STATUS FROM 
(
select 
apptID, PatientID , Apptdate , 
DATEDIFF (day,lag(Apptdate) over (PARTITION BY PatientID order by ApptID asc),Apptdate) date_diff ,
DATEDIFF(day,lag(Apptdate,2) over (PARTITION BY PatientID order by ApptID asc),Apptdate) date_diff_2
  from #Appt1
) SRC

Démo -> https://rextester.com/TNW43808

Digvijay S
la source
2
Cela fonctionne sur les données d'échantillon actuelles, mais peut donner des résultats incorrects pour des données d'échantillon différentes. Même si vous utilisez apptDatecomme order bycolonne de la lagfonction (ce que vous devriez vraiment, car l'ID n'est une garantie de rien), elle peut toujours être facilement rompue en introduisant plus de rendez-vous de suivi. Voir cette démo Rextester par exemple. Bon essai, cependant ...
Zohar Peled
Je vous remercie. Doit avoir utilisé la date au lieu de l'ID. Mais pourquoi c'est faux pour apptID = 6 25.01.2020 - 12.02.2020 -> 18 jours -> suivi.
Digvijay S
2
Parce que ce devrait être un Newet non un FollowUp. Cela fait plus de 30 jours depuis le premier rendez-vous de ce patient ... Vous devriez compter 30 jours depuis chaque Newrendez-vous, puis utiliser à Newnouveau ...
Zohar Peled
Oui. Je vous remercie. :( Besoin de créer un nouveau pour vérifier la période de date valide.
Digvijay S
1
with cte
as
(
select 
tmp.*, 
IsNull(Lag(ApptDate) Over (partition by PatientID Order by  PatientID,ApptDate),ApptDate) PriorApptDate
 from #Appt1 tmp
)
select 
PatientID, 
ApptDate, 
PriorApptDate, 
DateDiff(d,PriorApptDate,ApptDate) Elapsed,
Case when DateDiff(d,PriorApptDate,ApptDate)>30 
or DateDiff(d,PriorApptDate,ApptDate)=0 then 'New' else 'Followup'   end Category   from cte

Le mien est correct. Les auteurs étaient incorrects, voir écoulés

Lion d'or
la source