Abfrage ohne WHILE-Schleife

18

Wir haben eine Termintabelle wie unten gezeigt. Jeder Termin muss als "Neu" oder "Follow-up" eingestuft werden. Jeder Termin (für einen Patienten) innerhalb von 30 Tagen nach dem ersten Termin (für diesen Patienten) ist Follow-up. Nach 30 Tagen ist der Termin wieder "Neu". Jeder Termin innerhalb von 30 Tagen wird zu "Follow-up".

Ich mache dies derzeit durch Eingabe von while-Schleife.
Wie kann dies ohne WHILE-Schleife erreicht werden?

Geben Sie hier die Bildbeschreibung ein

Tabelle

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
quelle
Ich kann Ihr Bild nicht sehen, aber ich möchte bestätigen, dass, wenn es 3 Termine gibt, alle 20 Tage voneinander entfernt, der letzte immer noch "nachverfolgt" wird, denn obwohl es mehr als 30 Tage vom ersten sind, Es ist noch weniger als 20 Tage von der Mitte entfernt. Ist das wahr?
pwilcox
@pwilcox Nein. Der dritte Termin ist ein neuer Termin, wie im Bild gezeigt
LCJ
Während Loop-Over- fast_forwardCursor in Bezug auf die Leistung wahrscheinlich die beste Option wäre.
David דודו Markovitz

Antworten:

14

Sie müssen eine rekursive Abfrage verwenden.

Der 30-Tage-Zeitraum wird ab prev gezählt (und nein, es ist nicht möglich, dies ohne Rekursion / skurriles Update / Schleife zu tun). Aus diesem Grund ist die gesamte vorhandene Antwort nur ROW_NUMBERfehlgeschlagen.

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;  

db <> Geigen-Demo

Ausgabe:

+---------+------------+-------------+----------+
| 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 |
+---------+------------+-------------+----------+

Wie es funktioniert:

  1. f - Startpunkt erhalten (Anker - pro Patienten-ID)
  2. rekursiver Teil, wenn die Differenz zwischen aktuellem Wert und prev> 30 ist, ändern Sie die Kategorie und den Startpunkt im Kontext von PatientId
  3. Hauptanzeige sortierte Ergebnismenge

Ähnliche Klasse:

Bedingte SUMME unter Oracle - Verschließen einer Fensterfunktion

Sitzungsfenster (Azure Stream Analytics)

Ausführen von Total, bis eine bestimmte Bedingung erfüllt ist - Skurriles Update


Nachtrag

Verwenden Sie diesen Code niemals in der Produktion!

Eine andere Option, die neben der Verwendung von cte erwähnenswert ist, ist die Verwendung der temporären Tabelle und die Aktualisierung in "Runden".

Es könnte in "einzelner" Runde gemacht werden (skurriles Update):

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);

Abfrage:

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;

db <> fiddle Quirky Update

Lukasz Szozda
quelle
1
Ihre Logik sieht meiner sehr ähnlich. Können Sie signifikante Unterschiede beschreiben?
pwilcox
@pwilcox Als ich diese Antwort schrieb, verwendete jede vorhandene einfache Zeilennummer, die nicht funktionierte. Deshalb habe ich meine eigene Version bereitgestellt
Lukasz Szozda
Ja, ich war zu schnell mit der Antwort. Danke, dass du das kommentiert hast.
Irdis
2
Ich glaube, rcte ist die einzige Lösung dafür, bis SQL Server die RANGE x PRECEDINGKlausel korrekt implementiert .
Salman A
1
Das @LCJ Quirky-Update basiert auf "undokumentiertem" Verhalten und kann sich jederzeit ohne vorherige Ankündigung ändern ( red-gate.com/simple-talk/sql/learn-sql-server/… )
Lukasz Szozda
5

Sie könnten dies mit einem rekursiven cte tun. Sie sollten zuerst bei jedem Patienten nach apptDate bestellen. Dies kann durch ein gewöhnliches Verfahren erreicht werden.

Wählen Sie dann im Ankerteil Ihres rekursiven cte die erste Bestellung für jeden Patienten aus, markieren Sie den Status als "neu" und markieren Sie das apptDate als Datum des letzten "neuen" Datensatzes.

Berechnen Sie im rekursiven Teil Ihres rekursiven Cte, inkrementieren Sie zum nächsten Termin, die Differenz in Tagen zwischen dem aktuellen Termin und dem letzten "neuen" Termin. Wenn es länger als 30 Tage ist, markieren Sie es als "neu" und setzen Sie das letzte neue Termindatum zurück. Andernfalls markieren Sie es als "Follow-up" und geben Sie einfach die vorhandenen Tage seit dem neuen Termin weiter.

Schließlich wählen Sie in der Basisabfrage einfach die gewünschten Spalten aus.

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;

Ich sollte erwähnen, dass ich diese Antwort ursprünglich gelöscht habe, da die Antwort von Abhijeet Khandagale Ihren Anforderungen mit einer einfacheren Abfrage zu entsprechen schien (nachdem ich sie ein wenig überarbeitet hatte). Aber mit Ihrem Kommentar zu Ihrer Geschäftsanforderung und Ihren hinzugefügten Beispieldaten habe ich meine nicht gelöscht, weil ich glaube, dass diese Ihren Anforderungen entspricht.

pwilcox
quelle
4

Ich bin mir nicht sicher, ob es genau das ist, was Sie implementiert haben. Eine andere Option, die neben der Verwendung von cte erwähnenswert ist, ist die Verwendung der temporären Tabelle und die Aktualisierung in "Runden". Wir werden also die temporäre Tabelle aktualisieren, während nicht alle Status korrekt eingestellt sind, und das Ergebnis iterativ erstellen. Wir können die Anzahl der Iterationen einfach mit einer lokalen Variablen steuern.

Also teilen wir jede Iteration in zwei Stufen auf.

  1. Stellen Sie alle Folgewerte ein, die sich in der Nähe von Neue Datensätze befinden. Das ist ziemlich einfach, wenn man nur den richtigen Filter verwendet.
  2. Für den Rest der Datensätze, für die kein Status festgelegt wurde, können wir zuerst in der Gruppe mit derselben Patienten-ID auswählen. Und sagen Sie, dass sie neu sind, da sie in der ersten Phase nicht verarbeitet wurden.

Damit

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

Aktualisieren. Lesen Sie den Kommentar von Lukasz. Es ist bei weitem klüger. Ich hinterlasse meine Antwort nur als Idee.

Irdis
quelle
4

Ich glaube, der rekursive allgemeine Ausdruck ist eine großartige Möglichkeit, Abfragen zu optimieren und Schleifen zu vermeiden. In einigen Fällen kann er jedoch zu einer schlechten Leistung führen und sollte nach Möglichkeit vermieden werden.

Ich verwende den folgenden Code, um das Problem zu lösen und zu testen, um mehr Werte zu erhalten. Ich empfehle Ihnen jedoch, es auch mit Ihren realen Daten zu testen.

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];

Geben Sie hier die Bildbeschreibung ein

Die Idee ist ziemlich einfach - ich möchte die Datensätze in Gruppen (30 Tage) trennen, in welcher Gruppe der kleinste Datensatz ist new, die anderen follow ups. Überprüfen Sie, wie die Anweisung erstellt wird:

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];

Geben Sie hier die Bildbeschreibung ein

Damit:

  1. Zuerst erhalten wir das erste Datum für jede Gruppe und berechnen die Unterschiede in Tagen mit dem aktuellen
  2. dann wollen wir Gruppen bekommen - * 1.0 / 30 wird hinzugefügt
  3. 30, 60, 90 usw. Tage bekommen wir eine ganze Zahl und wir wollten eine neue Periode beginnen, habe ich hinzugefügt + 0.000001; Außerdem verwenden wir die Deckenfunktion, um die zu erhaltensmallest integer greater than, or equal to, the specified numeric expression

Das ist es. Mit einer solchen Gruppe finden wir einfach ROW_NUMBERunser Startdatum und machen es als newund lassen den Rest als follow ups.

gotqn
quelle
2
Nun, die Frage ist etwas anders und dieser Ansatz ist zu einfach. Aber es ist ein schönes Beispiel, wie man ein Taumelfenster
Lukasz Szozda
Es geht auch um Leistung. Ich glaube, rekursiv sollte langsamer sein.
Gotqn
3

Mit gebührendem Respekt an alle und an IMHO,

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

Es gibt nicht viel Leistungsgewinn bei der Verwendung Recursive CTEund Window Partition functionalles in einem.

Appidsollte sein int identity(1,1), oder es sollte immer größer werden clustered index.

Neben anderen Vorteilen wird auch sichergestellt, dass alle aufeinanderfolgenden Reihen APPDatedieses Patienten größer sein müssen.

Auf diese Weise können Sie problemlos mit APPIDIhrer Abfrage spielen, was effizienter ist, als inequalityOperatoren wie>, <in APPDate einzufügen. Das Einfügen von inequalityOperatoren wie>, <in APPID hilft dem SQL-Optimierer.

Außerdem sollte es in der Tabelle zwei Datumsspalten geben

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

Da dies die wichtigsten Spalten in der wichtigsten Tabelle sind, konvertieren Sie nicht viel.

So Non clustered indexkann auf Appdate erstellt werden

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

Testen Sie mein Skript mit anderen Beispieldaten und ich weiß, für welche Beispieldaten es nicht funktioniert. Auch wenn es nicht funktioniert, bin ich sicher, dass es in meiner Skriptlogik selbst behoben werden kann.

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
quelle
3

Obwohl es in der Frage nicht klar angesprochen wird, ist es leicht herauszufinden, dass die Termine nicht einfach nach 30-Tage-Gruppen kategorisiert werden können. Es macht keinen geschäftlichen Sinn. Und Sie können die Appt-ID auch nicht verwenden. Man kann heute einen neuen Termin für vereinbaren2020-09-06. Hier ist, wie ich dieses Problem anspreche. Holen Sie sich zuerst den ersten Termin und berechnen Sie dann die Datumsdifferenz zwischen jedem Termin und der ersten App. Wenn es 0 ist, setzen Sie auf 'Neu'. Wenn <= 30 'Follow-up'. Wenn> 30, setzen Sie als "Unentschlossen" und überprüfen Sie die nächste Runde, bis "Unentschlossen" nicht mehr vorhanden ist. Und dafür brauchen Sie wirklich eine while-Schleife, aber sie durchläuft nicht jedes Termindatum, sondern nur einige wenige Datensätze. Ich habe den Ausführungsplan überprüft. Obwohl es nur 10 Zeilen gibt, sind die Abfragekosten erheblich niedriger als bei Verwendung von rekursivem CTE, jedoch nicht so niedrig wie bei der Nachtragsmethode von 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
quelle
2

Ich hoffe, dies wird dir helfen.

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
quelle
Vielen Dank an @ x00 für die Bearbeitung des Codes in lesbarem Format. Ich verwende mein Mobiltelefon, um Antworten zu veröffentlichen, sodass ich keine richtigen Einrückungen vornehmen konnte.
Abhijeet Khandagale
Ich denke, das ist im Wesentlichen die richtige Antwort. Aber es ist eine Antwort von schlechter Qualität, da sie nicht erklärt wird und der Code eine unnötige äußere Abfrage hat, wenn eine Änderung des inneren Teils gut ausreicht. Wenn Sie diese Probleme lösen können, stimme ich Ihnen gerne zu.
pwilcox
1
@pwilcox, danke für den wertvollen Vorschlag, ich habe die Antwort bearbeitet und ab sofort gepostet. Da ich auf Reisen bin und keinen Laptop dabei habe, werde ich in ein oder zwei Tagen Erklärungen veröffentlichen.
Abhijeet Khandagale
1
@AbhijeetKhandagale Dies entspricht nicht vollständig den Geschäftsanforderungen. Ich habe der Frage ein fehlgeschlagenes Szenario hinzugefügt. Für Patienten 303 sollte der Termin am 2. Februar Follow-up sein; Ihre Anfrage lautet jedoch "Neu"
LCJ
1

Sie könnten eine CaseAnweisung verwenden .

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

Die Frage ist, sollte diese Kategorie basierend auf dem ersten Termin oder dem vorherigen zugewiesen werden? Das heißt, wenn ein Patient drei Termine hatte, sollten wir den dritten Termin mit dem ersten oder dem zweiten vergleichen?

Ihr Problem gibt das erste an, so habe ich geantwortet. Wenn dies nicht der Fall ist, möchten Sie verwenden lag.

Denken Sie auch daran, DateDiffdass dies an Wochenenden keine Ausnahme macht. Wenn dies nur Wochentage sein sollen, müssen Sie Ihre eigene skalarwertige Funktion erstellen.

Benutzer
quelle
1
Dadurch werden nicht zwei aufeinanderfolgende Termine verknüpft, sondern Appt 1 mit allen folgenden Terminen verknüpft und die dazwischen liegenden Tage für alle berechnet. Auf diese Weise würden Sie zu viele Datensätze zurückgeben, da Appt 1 jetzt eine Beziehung zu 2, 3, 4 hat, Appt 2 eine Beziehung zu 3, 4 ...
steenbergh
Guter Punkt. Ich habe meine Antwort aktualisiert, um eine Unterauswahl für A1 vorzunehmen.
Benutzer
1
Es gibt kein erwartetes Ergebnis. Der Termin am 20. Februar sollte "Follow-up" sein
LCJ
Die Frage ist unklar ... Die Posterbeschreibung lautet wie folgt: "Jeder Termin (für einen Patienten) innerhalb von 30 Tagen nach dem ersten Termin (dieses Patienten) ist Follow-up. Nach 30 Tagen ist der Termin wieder" neu ". Jeder Termin innerhalb von 30 Tagen werde "Followup". " Der 5. Januar ist sicherlich mehr als 30 Tage vom 20. Februar entfernt, dh neu. Ab dem 12. Februar sind es jedoch KEINE 30 Tage. Ich biete eine Lösung für das an, was er geschrieben hat, nicht für die gelieferte Tabelle. Wenn der Benutzer sich an dem ausrichten möchte, was die Tabelle liefert, sollte er lag verwenden. Sie sollten auch klarstellen ...
Benutzer
1

mit Lag-Funktion


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

Demo -> https://rextester.com/TNW43808

Digvijay S.
quelle
2
Dies funktioniert mit den aktuellen Probendaten, kann jedoch bei anderen Probendaten zu falschen Ergebnissen führen. Selbst wenn Sie apptDateals order bySpalte der lagFunktion verwenden (was Sie eigentlich als ID bezeichnen sollten, ist dies keine Garantie für irgendetwas), kann sie durch die Einführung weiterer Folgetermine leicht beschädigt werden. Sehen Sie sich zum Beispiel diese Rextester-Demo an. Guter Versuch ...
Zohar Peled
Vielen Dank. Sollte Datum anstelle von ID verwendet haben. Aber warum ist es falsch für apptID = 6 25.01.2020 - 12.02.2020 -> 18 Tage -> Follow-up.
Digvijay S
2
Weil es ein Newund kein sein sollte FollowUp. Es sind mehr als 30 Tage seit dem ersten Termin dieses Patienten vergangen ... Sie sollten 30 Tage seit jedem NewTermin zählen und dann Newwieder einen verwenden ...
Zohar Peled
Ja. Vielen Dank. :( Sie müssen einen neuen erstellen, um den gültigen Zeitraum zu überprüfen.
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

Meins ist richtig. Die Autoren waren falsch, siehe abgelaufen

Goldener Löwe
quelle