WHILE Döngü Olmadan Sorgu


18

Aşağıda gösterildiği gibi randevu tablomuz var. Her randevunun "Yeni" veya "Takip" olarak sınıflandırılması gerekir. İlk randevunun (o hastanın) 30 gün içinde yapılacak herhangi bir randevu (bir hasta için) Takiptir. 30 gün sonra randevu tekrar "Yeni" olur. 30 gün içinde herhangi bir randevu "Takip" haline gelir.

Şu anda while loop yazarak yapıyorum.
WHILE döngüsü olmadan bunu nasıl başarabilirim?

resim açıklamasını buraya girin

tablo

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' 

Görüntünüzü göremiyorum, ancak 3 randevu varsa, birbirinden her 20 günde bir, sonuncusu hala 'takip' hakkıdır, çünkü ilkinden 30 günden fazla olmasına rağmen, hala ortadan 20 günden az. Bu doğru mu?
pwilcox

Üçüncüsü, resimde gösterildiği gibi yeni bir randevu olacak
LCJ

fast_forwardİmlecin üzerindeki döngü muhtemelen en iyi seçeneğiniz olsa da, performans açısından akıllıca olacaktır.
David דודו Markovitz

Yanıtlar:


14

Özyinelemeli sorgu kullanmanız gerekir.

30 günlük süre, önceki tarihten başlayarak sayılır (ve yineleme / ilginç güncelleme / döngü olmadan bunu yapmak mümkün değildir). Bu yüzden sadece mevcut tüm cevaplar ROW_NUMBERbaşarısız oldu.

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 <> keman demosu

Çıktı:

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

Nasıl çalışır:

  1. f - başlangıç ​​noktası alın (çapa - her Hasta Kimliği için)
  2. rec - recursibe kısmı, eğer geçerli değer ve önceki arasındaki fark> 30 ise kategoriyi ve başlangıç ​​noktasını PatientId bağlamında değiştirin
  3. Ana - sıralı sonuç kümesini görüntüle

Benzer sınıf:

Oracle'da Koşullu SUM - Pencereli bir işlevi kapatma

Oturum penceresi (Azure Stream Analytics)

Belirli bir durum gerçekleşene kadar Toplam Koşu - İlginç güncelleme


ek

Bu kodu asla üretimde kullanmayın!

Ancak başka bir seçenek, cte kullanmanın yanı sıra, geçici tablo kullanmak ve "mermi" de güncellemektir.

"Tek" turda yapılabilir (ilginç güncelleme):

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

Sorgu:

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 <> keman İlginç güncelleme


1
mantığın benimkine çok benziyor. Önemli farklılıkları tanımlayabilir misiniz?
pwilcox

@pwilcox Bu yanıtı yazdığımda, mevcut her biri çalışmayan basit satır_sayısı kullanıyordu, bu yüzden kendi
sürümümü sağladım

Evet, cevapta çok hızlıydım. Bu konuda yorum yaptığınız için teşekkürler.
Irdis

2
SQL sunucusu doğru RANGE x PRECEDINGmaddeyi uygulayana kadar rcte bunun için tek çözüm olduğuna inanıyorum .
Salman A

1
@LCJ Quirky güncellemesi "belgelenmemiş" davranışa dayanmaktadır ve bildirim yapılmaksızın her an değişebilir ( red-gate.com/simple-talk/sql/learn-sql-server/… )
Lukasz Szozda

5

Bunu yinelemeli bir cte ile yapabilirsiniz. Her hastaya önce apptDate ile sipariş vermelisiniz. Bu, değirmen işletmesi cte ile gerçekleştirilebilir.

Ardından, özyinelemeli ktetanizin bağlantı bölümünde, her hasta için ilk siparişi seçin, durumu 'yeni' olarak işaretleyin ve ayrıca apptDate'i en son 'yeni' kaydın tarihi olarak işaretleyin.

Yinelemeli kantınızın özyinelemeli bölümünde, bir sonraki randevuya artış, mevcut randevu ile en son 'yeni' randevu tarihi arasındaki gün farkını hesaplayın. 30 günden uzunsa, 'yeni' olarak işaretleyin ve en yeni randevu tarihini sıfırlayın. Aksi takdirde 'takip' olarak işaretleyin ve yeni randevu tarihinden bu yana var olan günleri geçin.

Son olarak, temel sorguda istediğiniz sütunları seçmeniz yeterlidir.

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;

Abhijeet Khandagale'nin cevabı daha basit bir sorgu ile ihtiyaçlarınızı karşılıyor gibi görünüyordu çünkü başlangıçta bu cevabı sildim. Ancak, iş gereksiniminiz ve ek örnek verileriniz hakkında ona yaptığınız yorumla, benimkini geri aldım çünkü bunun ihtiyaçlarınızı karşıladığına inanıyorum.


4

Tam olarak uyguladığınızdan emin değilim. Ancak başka bir seçenek, cte kullanmanın yanı sıra, geçici tablo kullanmak ve "mermi" güncellemesidir. Bu nedenle, tüm durumlar doğru şekilde ayarlanmamışken geçici tabloyu güncelleyeceğiz ve sonucu yinelemeli bir şekilde oluşturacağız. Sadece yerel değişkeni kullanarak yineleme sayısını kontrol edebiliriz.

Böylece her yinelemeyi iki aşamaya ayırıyoruz.

  1. Yeni kayıtların yakınındaki tüm İzleme değerlerini ayarlayın. Sadece doğru filtreyi kullanarak bunu yapmak oldukça kolay.
  2. Durum ayarlaması olmayan kayıtların geri kalanı için ilk olarak aynı PatientID olan grupta seçim yapabiliriz. Ve ilk aşamada işlenmedikleri için yeni olduklarını söyleyin.

Yani

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

Güncelleme. Lukasz tarafından sağlanan yorumu okuyun. Bu çok daha akıllıca bir yol. Cevabımı bir fikir olarak bırakıyorum.


4

Yinelemeli ortak ifadenin döngülerden kaçınarak sorguları optimize etmenin harika bir yolu olduğuna inanıyorum, ancak bazı durumlarda kötü performansa neden olabilir ve mümkünse kaçınılmalıdır.

Sorunu çözmek ve daha fazla değer olacağını test etmek için aşağıdaki kodu kullanıyorum, ancak gerçek verilerinizle de test etmenizi öneririz.

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

resim açıklamasını buraya girin

Fikir oldukça basit - kayıtları grupta ayırmak istiyorum (30 gün), hangi grupta en küçük kayıt, newdiğerleri follow ups. İfadenin nasıl oluşturulduğunu kontrol edin:

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

resim açıklamasını buraya girin

Yani:

  1. ilk olarak, her grup için ilk tarihi alıyoruz ve mevcut grupla günler arasındaki farkları hesaplıyoruz
  2. sonra, grup almak istiyoruz - * 1.0 / 30eklendi
  3. 30, 60, 90 vb. günler için tam sayı alıyoruz ve yeni bir dönem başlatmak istedik, ekledim + 0.000001; ayrıca, biz almak için tavan fonksiyonu kullanmaksmallest integer greater than, or equal to, the specified numeric expression

Bu kadar. Böyle bir gruba sahip olmak ROW_NUMBER, başlangıç ​​tarihimizi bulmak ve bunu yapmak newve geri kalanını olduğu gibi bırakmak için kullanıyoruz follow ups.


2
Soru biraz farklı ve bu değerlendirme aşırı basitleştirme. Ama yuvarlanan pencerenin
Lukasz Szozda

Performansla da ilgili. Özyinelemenin daha yavaş olması gerektiğine inanıyorum.
gotqn

3

Herkese ve IMHO'ya saygıyla,

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

Kullanırken çok fazla performans kazancı yok Recursive CTEve Window Partition functionhepsi bir arada.

Appidolmalı int identity(1,1)ya da sürekli artmalıdır clustered index.

Diğer faydalarının yanı sıra APPDate, hastanın ardışık tüm sırasının daha büyük olması da sağlar.

Bu şekilde APPIDsorgunuzda kolayca oynayabilirsiniz ki bu inequality, APPDate'e>, <gibi bir operatör koymaktan daha verimli olacaktır . inequalityOperatörü APPID'ye>, <gibi koymak Sql Optimizer'a yardımcı olacaktır.

Ayrıca tabloda iki tarih sütunu olmalıdır.

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

Bunlar en önemli tabloda en önemli sütunlar olduğundan, çok fazla döküm değil, dönüştürün.

Yani Non clustered index Appdate üzerinde oluşturulabilir

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

Scriptimi diğer örnek verilerle test edin ve hangi örnek veriler için çalışmadığını biliyorum. Çalışmasa bile, komut dosyası mantığımın kendisinde düzeltilebileceğinden eminim.

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

3

Soruda açıkça ele alınmasa da, randevu tarihlerinin 30 günlük gruplar tarafından basit bir şekilde kategorize edilemeyeceğini anlamak kolaydır. Hiçbir iş anlamsız. Ayrıca appt kimliğini de kullanamazsınız. Bugün için yeni bir randevu alınabilir2020-09-06. İşte bu sorunu nasıl ele alacağım. İlk olarak, ilk randevuyu alın, ardından her randevu ile ilk uygulama arasındaki tarih farkını hesaplayın. 0 ise, 'Yeni' olarak ayarlayın. <= 30 'Takip' ise. > 30 ise, 'Kararsız' olarak ayarlayın ve 'Kararsız' kalmayıncaya kadar bir sonraki tur kontrolünü yapın. Ve bunun için gerçekten bir while döngüsüne ihtiyacınız var, ancak her randevu tarihinde değil, sadece birkaç veri kümesinde döngü oluşturuyor. İnfaz planını kontrol ettim. Sadece 10 satır olmasına rağmen, sorgulama maliyeti, özyinelemeli CTE kullanandan önemli ölçüde daha düşüktür, ancak Lukasz Szozda'nın ek yöntemi kadar düşük değildir.

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

2

Umarım bu sana yardımcı olmuştur.

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

@ X00 okunabilir biçimde kod düzenleme için teşekkürler, cevapları göndermek için cep telefonumu kullanıyorum, bu yüzden uygun girintileri veremedik.
Abhijeet Khandagale

Bence bu aslında doğru cevap. Ancak, açıklanmaması ve iç kısımda bir değişiklik yapıldığında kodun gereksiz bir dış sorgusu olması nedeniyle kalitesiz bir cevaptır. Bu sorunları çözebilirseniz size oy vermekten mutluluk duyarım.
pwilcox

1
@pwilcox, değerli öneri için teşekkürler, cevabı düzenledim ve şimdi gönderdim. Seyahat ettiğim ve yanımda dizüstü bilgisayarım olmadığı için, bir iki gün içinde açıklama yayınlayacağım.
Abhijeet Khandagale

1
@AbhijeetKhandagale Bu, işletme gereksinimlerini tam olarak karşılamıyor. Soruya başarısız bir senaryo ekledim. Hasta 303 için 2 Şubat randevusu Takip olmalıdır; ancak sorgunuz bunun "Yeni" olduğunu söylüyor
LCJ

1

Bir Caseifade kullanabilirsiniz .

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

Soru, bu kategorinin ilk randevuya mı yoksa önceki kategoriye göre mi atanmasıdır? Yani, bir Hastanın üç randevusu varsa, üçüncü randevuyu birinciyle mi yoksa ikincisiyle mi karşılaştırmalıyız?

Sorununuz ilkini belirtiyor, ben de böyle cevapladım. Eğer durum böyle değilse, kullanmak isteyeceksiniz lag.

Ayrıca, DateDiffhafta sonları için bir istisna teşkil etmediğini unutmayın . Bunun yalnızca hafta içi olması gerekiyorsa, kendi Skaler Değerli işlevinizi oluşturmanız gerekir.


1
Bu iki ardışık randevuyu ilişkilendirmez, bu uygulama 1'i takip eden tüm randevulara bağlar ve hepsi için günleri hesaplar. Bu şekilde çok fazla kayıt döndürürdünüz, çünkü appt 1'in artık 2, 3, 4 ile ilişkisi var, appt 2'nin 3, 4 ile ilişkisi var ...
steenbergh

İyi bir nokta. A1 için alt seçim yapmak için cevabımı güncelledim.
kullanıcı

1
Beklenen sonucu vermez. 20 Şubat randevu "Takip" olmalıdır
LCJ

Soru belirsiz ... Poster açıklaması şu şekildedir: " İlk randevunun (o hastanın) 30 gün içinde herhangi bir randevu (bir hasta için) Takip'tir. 30 gün sonra randevu tekrar" Yeni "dir. 30 gün içinde herhangi bir randevu "Takip" haline gelir. " 5 Ocak, 20 Şubat'tan itibaren 30 günden fazla, yani Yeni. Ancak, 12 Şubat'tan 30 gün uzakta değil. Yazdığı şeye bir çözüm öneriyorum, verilen tabloya değil. Kullanıcı, tablonun sağladığı malzemeyle hizalamak isterse gecikmeyi kullanmalıdır. Ayrıca açıklığa kavuşturmalılar ...
Kullanıcı

1

Gecikme işlevini kullanma


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


2
Bu, geçerli örnek veriler üzerinde çalışır, ancak farklı bir örnek veri verildiğinde yanlış sonuçlar verebilir. Fonksiyonun sütunu apptDateolarak kullansanız bile (gerçekten id olarak yapmanız gereken hiçbir şeyin garantisi değildir), daha fazla takip randevusu getirerek kolayca kırılabilir. Örneğin bu Rextester demosuna bakın . İyi deneyin, ama ...order bylag
Zohar Peled

Teşekkür ederim. Kimlik yerine tarih kullanmalıydı. Ama neden apptID = 6 25.01.2020 - 12.02.2020 -> 18 gün -> takip için yanlış.
Digvijay S

2
Çünkü a Newdeğil, a olmalıdır FollowUp. Bu hastanın ilk randevusundan bu yana 30 günden fazla ... Her Newrandevudan bu yana 30 gün saymalı ve sonra Newtekrar kullanmalısınız ...
Zohar Peled

Evet. Teşekkür ederim. :( Geçerli tarih dönemini kontrol etmek için yeni bir tane oluşturmanız gerekir
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

Benimki doğru. Yazarlar yanlıştı, bkz. Geçen

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.