การค้นหาที่ไม่มีลูปในขณะที่


18

เรามีตารางนัดหมายที่แสดงด้านล่าง การนัดหมายแต่ละครั้งจะต้องจัดหมวดหมู่เป็น "ใหม่" หรือ "ติดตาม" การนัดหมายใด ๆ (สำหรับผู้ป่วย) ภายใน 30 วันนับจากการนัดหมายครั้งแรก (ของผู้ป่วยรายนั้น) คือการติดตาม หลังจาก 30 วันการนัดหมายจะเป็น "ใหม่" การนัดหมายใด ๆ ภายใน 30 วันจะกลายเป็น "การติดตาม"

ฉันกำลังทำสิ่งนี้โดยการพิมพ์ขณะวนซ้ำ
วิธีการบรรลุสิ่งนี้โดยไม่ต้องห่วงในขณะที่?

ป้อนคำอธิบายรูปภาพที่นี่

โต๊ะ

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' 

ฉันไม่เห็นภาพของคุณ แต่ฉันต้องการยืนยันหากมีการนัดหมาย 3 ครั้งทุก ๆ 20 วันจากกันการติดตามครั้งสุดท้ายยังคงถูกต้องเพราะแม้ว่ามันจะมากกว่า 30 วันจากครั้งแรก มันยังน้อยกว่า 20 วันจากกลาง มันเป็นเรื่องจริงเหรอ?
pwilcox

@pwilcox ไม่คนที่สามจะได้รับการแต่งตั้งใหม่ตามที่แสดงในภาพ
LCJ

ในขณะที่fast_forwardเคอร์เซอร์อยู่เหนือเคอร์เซอร์อาจเป็นตัวเลือกที่ดีที่สุดของคุณ
David דודו Markovitz

คำตอบ:


14

คุณต้องใช้แบบสอบถามซ้ำ

ระยะเวลา 30 วันนับจากวันที่ก่อนหน้านี้ (และไม่สามารถทำได้โดยไม่ต้องมีการเรียกซ้ำ / การอัพเดต / การเล่นโวหารซ้ำ) นั่นคือเหตุผลที่คำตอบที่มีอยู่ทั้งหมดใช้เพียงROW_NUMBERล้มเหลว

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 <> การสาธิตซอ

เอาท์พุท:

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

มันทำงานอย่างไร:

  1. f - รับจุดเริ่มต้น (จุดยึด - ต่อทุกรหัสผู้ป่วย)
  2. rec - ส่วนที่เรียกซ้ำถ้าความแตกต่างระหว่างค่าปัจจุบันและก่อนหน้าคือ> 30 เปลี่ยนหมวดหมู่และจุดเริ่มต้นในบริบทของ PatientId
  3. หลัก - แสดงชุดผลลัพธ์เรียง

ชั้นเรียนที่คล้ายกัน:

SUM ตามเงื่อนไขบน Oracle - การกำหนดฟังก์ชันหน้าต่าง

หน้าต่างเซสชัน (Azure Stream Analytics)

เรียกใช้ผลรวมจนกระทั่งเงื่อนไขเฉพาะเป็นจริง - การปรับปรุงที่เล่นโวหาร


ภาคผนวก

ไม่เคยใช้รหัสนี้ในการผลิต!

แต่ตัวเลือกอื่นที่มีมูลค่าการกล่าวถึงนอกเหนือจากการใช้ cte คือการใช้ตาราง temp และปรับปรุงใน "รอบ"

มันสามารถทำได้ในรอบ "เดี่ยว" (การปรับปรุงที่เล่นโวหาร):

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

ค้นหา:

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 ปรับปรุงการเล่นโวหาร


1
ตรรกะของคุณดูคล้ายกับของฉันมาก คุณสามารถอธิบายความแตกต่างที่สำคัญได้หรือไม่?
pwilcox

@pwilcox เมื่อฉันเขียนคำตอบนี้ทุกคนที่มีอยู่ก็ใช้ row_number ง่าย ๆ ที่ไม่ทำงานนี่คือเหตุผลที่ฉันให้รุ่นของฉันเอง
Lukasz Szozda

ใช่ฉันเร็วเกินไปกับคำตอบ ขอบคุณสำหรับการแสดงความคิดเห็นในที่
Irdis

2
ฉันเชื่อว่า rcte เป็นทางออกเดียวสำหรับสิ่งนี้จนกว่าเซิร์ฟเวอร์ SQL จะใช้RANGE x PRECEDINGประโยคอย่างถูกต้อง
Salman

1
@LCJ การอัปเดตที่แปลกประหลาดขึ้นอยู่กับพฤติกรรม "ไม่มีเอกสารประกอบ" และสามารถเปลี่ยนแปลงได้ในทุกช่วงเวลาโดยไม่ต้องแจ้งให้ทราบล่วงหน้า ( red-gate.com/simple-talk/sql/learn-sql-server/ ...... )
Lukasz Szozda

5

คุณสามารถทำเช่นนี้กับ curs แบบเรียกซ้ำ คุณควรสั่งซื้อโดย apptDate ภายในผู้ป่วยแต่ละราย ที่สามารถทำได้โดย run-of-the-mill cte

จากนั้นในส่วนจุดยึดของ curs แบบเรียกซ้ำของคุณให้เลือกลำดับแรกสำหรับผู้ป่วยแต่ละรายทำเครื่องหมายสถานะเป็น 'ใหม่' และทำเครื่องหมาย apptDate เป็นวันที่บันทึก 'ใหม่' ล่าสุดล่าสุด

ในส่วนวนซ้ำของ curs ซ้ำของคุณเพิ่มขึ้นเป็นนัดถัดไปคำนวณความแตกต่างในวันระหว่างการนัดหมายปัจจุบันและวันที่นัดหมายใหม่ล่าสุด หากมากกว่า 30 วันให้ทำเครื่องหมายว่า 'ใหม่' และรีเซ็ตวันที่นัดหมายใหม่ล่าสุด มิฉะนั้นให้ทำเครื่องหมายว่า 'ติดตาม' และผ่านไปตามวันที่มีอยู่นับตั้งแต่วันที่นัดใหม่

ในแบบสอบถามฐานเพียงเลือกคอลัมน์ที่คุณต้องการ

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 ดูเหมือนจะตอบสนองความต้องการของคุณด้วยการสืบค้นที่ง่ายขึ้น แต่ด้วยความคิดเห็นของคุณถึงเขาเกี่ยวกับความต้องการทางธุรกิจของคุณและข้อมูลตัวอย่างที่เพิ่มเข้ามาของคุณฉันได้ยกเลิกการลบทิ้งเพราะเชื่อว่าสิ่งนี้ตรงตามความต้องการของคุณ


4

ฉันไม่แน่ใจว่าเป็นสิ่งที่คุณใช้งานจริง แต่อีกทางเลือกหนึ่งที่น่ากล่าวถึงนอกเหนือจากการใช้ cte คือการใช้ตาราง temp และอัปเดตใน "รอบ" ดังนั้นเราจะอัปเดตตาราง temp ในขณะที่สถานะทั้งหมดไม่ได้ตั้งค่าอย่างถูกต้องและสร้างผลลัพธ์ในแบบวนซ้ำ เราสามารถควบคุมจำนวนการวนซ้ำโดยใช้ตัวแปรเฉพาะที่

ดังนั้นเราจึงแบ่งการวนซ้ำเป็นสองขั้นตอน

  1. ตั้งค่าการติดตามทั้งหมดที่อยู่ใกล้กับระเบียนใหม่ ค่อนข้างง่ายที่จะทำเพียงแค่ใช้ตัวกรองที่ถูกต้อง
  2. สำหรับส่วนที่เหลือของระเบียนที่ไม่มีการตั้งค่าสถานะเราสามารถเลือกครั้งแรกในกลุ่มที่มี PatientID เดียวกัน และบอกว่าพวกเขาใหม่เพราะพวกเขาไม่ได้ดำเนินการตามขั้นตอนแรก

ดังนั้น

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

ปรับปรุง อ่านความคิดเห็นที่จัดทำโดย Lukasz มันเป็นวิธีที่ฉลาดกว่า ฉันทิ้งคำตอบไว้เป็นความคิด


4

ฉันเชื่อว่านิพจน์ทั่วไปแบบเรียกซ้ำเป็นวิธีที่ดีในการเพิ่มประสิทธิภาพการค้นหาแบบหลีกเลี่ยงการวนซ้ำ แต่ในบางกรณีอาจทำให้ประสิทธิภาพการทำงานแย่ลงและควรหลีกเลี่ยงหากเป็นไปได้

ฉันใช้รหัสด้านล่างเพื่อแก้ปัญหาและทดสอบว่ามันจะมีค่ามากกว่านี้ แต่ขอแนะนำให้คุณทดสอบด้วยข้อมูลจริงของคุณด้วย

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

ป้อนคำอธิบายรูปภาพที่นี่

ความคิดคือสวยเรียบง่าย - ฉันต้องการแยกระเบียนในกลุ่ม (30 วัน) ซึ่งในกลุ่มบันทึกที่เล็กที่สุดคือnewคนอื่น ๆ follow upsที่มี ตรวจสอบว่าคำสั่งสร้างขึ้นอย่างไร:

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

ป้อนคำอธิบายรูปภาพที่นี่

ดังนั้น:

  1. แรกเราจะได้รับวันแรกสำหรับแต่ละกลุ่มและคำนวณความแตกต่างในวันกับวันปัจจุบัน
  2. จากนั้นเราต้องการรับกลุ่ม - * 1.0 / 30ถูกเพิ่มเข้ามา
  3. เป็นเวลา 30, 60, 90, ฯลฯ วันที่เราจะได้รับจำนวนทั้งหมดและเราอยากจะเริ่มต้นใหม่เป็นช่วงเวลาที่ผมได้เพิ่ม+ 0.000001; นอกจากนี้เรากำลังใช้ฟังก์ชั่นเพดานเพื่อรับsmallest integer greater than, or equal to, the specified numeric expression

แค่นั้นแหละ. มีกลุ่มดังกล่าวเราก็ใช้ROW_NUMBERเพื่อหาวันที่เริ่มต้นของเราและทำให้มันเป็นและออกจากส่วนที่เหลือเป็นnewfollow ups


2
คำถามก็แตกต่างกันเล็กน้อยและสิ่งนี้เป็นสิ่งที่ทำให้เข้าใจผิด แต่มันเป็นตัวอย่างที่ดีในการติดตั้งหน้าต่างไม้ลอย
Lukasz Szozda

มันเกี่ยวกับประสิทธิภาพด้วย ฉันเชื่อว่าการเรียกซ้ำควรช้าลง
gotqn

3

ด้วยความเคารพต่อทุกคนและใน IMHO

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

ไม่มีการเพิ่มประสิทธิภาพเมื่อใช้งานRecursive CTEและWindow Partition functionทั้งหมดในที่เดียว

Appidควรจะเป็นint identity(1,1)หรือควรจะเพิ่มขึ้นเรื่อยclustered index

นอกเหนือจากผลประโยชน์อื่น ๆ แล้วยังช่วยให้มั่นใจได้ว่าแถวต่อเนื่องทั้งหมดAPPDateของผู้ป่วยนั้นจะต้องมากขึ้น

วิธีนี้คุณสามารถเล่นAPPIDในแบบสอบถามได้อย่างง่ายดายซึ่งจะมีประสิทธิภาพมากกว่าการใส่inequalityโอเปอเรเตอร์เช่น>, <ใน APPDate การวาง inequalityโอเปอเรเตอร์เช่น>, <ใน APPID จะช่วยเพิ่มประสิทธิภาพ Sql

นอกจากนี้ควรมีคอลัมน์วันที่สองคอลัมน์ในตารางดังนี้

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

เนื่องจากสิ่งเหล่านี้เป็นคอลัมน์ที่สำคัญที่สุดในตารางที่สำคัญที่สุดดังนั้นจึงไม่แปลงนักแสดงมากนัก

ดังนั้น Non clustered indexสามารถสร้างได้ใน Appdate

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

ทดสอบสคริปต์ของฉันกับข้อมูลตัวอย่างอื่นและรู้ว่าตัวอย่างข้อมูลใดที่ไม่ทำงาน แม้ว่ามันจะไม่ได้ผลก็ตามฉันก็มั่นใจว่ามันสามารถแก้ไขได้ในสคริปต์สคริปต์ของฉันเอง

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

แม้ว่าจะไม่ได้ระบุไว้อย่างชัดเจนในคำถาม แต่ก็ง่ายที่จะเข้าใจว่าวันที่นัดหมายไม่สามารถจัดกลุ่มตามกลุ่ม 30 วันได้อย่างง่ายดาย มันไม่สมเหตุสมผลเลย และคุณไม่สามารถใช้ appt id ได้เช่นกัน หนึ่งสามารถนัดหมายใหม่วันนี้สำหรับ2020-09-06. นี่คือวิธีที่ฉันแก้ไขปัญหานี้ ขั้นแรกรับการนัดหมายครั้งแรกจากนั้นคำนวณความแตกต่างวันที่ระหว่างการนัดหมายแต่ละครั้งกับแอปแรก หากเป็น 0 ให้ตั้งค่าเป็น 'ใหม่' หาก <= 30 'การติดตาม' หาก> 30 ให้ตั้งค่าเป็น 'Undecided' และทำการตรวจสอบรอบถัดไปจนกว่าจะไม่มี 'Undecided' อีกต่อไป และสำหรับสิ่งนั้นคุณต้องใช้การวนรอบสักครู่ แต่ไม่ได้วนซ้ำในแต่ละวันที่นัดหมาย แต่จะมีเพียงไม่กี่ชุดข้อมูล ฉันตรวจสอบแผนการดำเนินการ แม้ว่าจะมีเพียง 10 แถว แต่ค่าใช้จ่ายในการสืบค้นนั้นต่ำกว่าการใช้ CTE แบบเรียกซ้ำ แต่ไม่ต่ำกว่าวิธีการเพิ่มเติมของ 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

2

ฉันหวังว่านี่จะช่วยคุณได้

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 สำหรับการแก้ไขโค้ดในรูปแบบที่อ่านได้ฉันใช้โทรศัพท์มือถือเพื่อโพสต์คำตอบดังนั้นจึงไม่สามารถให้การเยื้องที่เหมาะสมได้
Abhijeet Khandagale

ฉันคิดว่านี่เป็นสาระสำคัญคำตอบที่ถูกต้อง แต่มันเป็นคำตอบที่มีคุณภาพต่ำซึ่งไม่ได้อธิบายและรหัสนั้นมีการสืบค้นภายนอกโดยไม่จำเป็นเมื่อการแก้ไขส่วนภายในจะทำได้ดี หากคุณสามารถแก้ไขปัญหาเหล่านั้นได้เรายินดีที่จะให้คะแนนคุณ
pwilcox

1
@pwilcox ขอบคุณสำหรับคำแนะนำที่มีค่าฉันได้แก้ไขคำตอบแล้วโพสต์ในตอนนี้ ขณะที่ฉันกำลังเดินทางและฉันไม่มีแล็ปท็อปกับฉันฉันจะโพสต์คำอธิบายในหนึ่งหรือสองวัน
Abhijeet Khandagale

1
@AbhijeetKhandagale สิ่งนี้ไม่ตรงตามความต้องการทางธุรกิจอย่างสมบูรณ์ ฉันได้เพิ่มสถานการณ์สมมติที่ล้มเหลวในคำถาม สำหรับผู้ป่วยที่ 303 นัดที่ 2 ก.พ. ควรติดตาม แต่ข้อความค้นหาของคุณบอกว่าเป็น "ใหม่"
LCJ

1

คุณสามารถใช้คำสั่งCase

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

คำถามคือควรกำหนดหมวดหมู่นี้ตามการแต่งตั้งครั้งแรกหรือก่อนหน้านี้หรือไม่ นั่นคือถ้าผู้ป่วยมีนัดสามครั้งเราควรเปรียบเทียบนัดที่สามกับนัดแรกหรือนัดที่สอง?

คุณระบุปัญหาแรกซึ่งเป็นวิธีที่ฉันตอบ lagหากที่ไม่กรณีที่คุณจะต้องการที่จะใช้

นอกจากนี้โปรดทราบDateDiffว่าไม่ได้ยกเว้นสำหรับวันหยุดสุดสัปดาห์ หากนี่ควรเป็นวันธรรมดาเท่านั้นคุณจะต้องสร้างฟังก์ชั่น Scalar-Valued ของคุณเอง


1
สิ่งนี้ไม่เชื่อมโยงการนัดหมายตามลำดับสองครั้งลิงก์นี้จะเชื่อมโยง 1 กับการนัดหมายต่อไปนี้ทั้งหมดและคำนวณวันระหว่างวันทั้งหมด คุณจะส่งคืนระเบียนมากเกินไปด้วยวิธีนี้เนื่องจาก appt 1 มีความสัมพันธ์กับ 2, 3, 4, appt 2 มีความสัมพันธ์กับ 3, 4 ...
steenbergh

จุดดี. ฉันอัพเดตคำตอบเพื่อทำการเลือกย่อยสำหรับ A1
ผู้ใช้

1
มันไม่ได้ให้ผลลัพธ์ที่คาดหวัง การนัดหมาย 20 ก.พ. ควรเป็น "การติดตาม"
LCJ

คำถามไม่ชัดเจน ... คำอธิบายโปสเตอร์คือ: "การนัดหมายใด ๆ (สำหรับผู้ป่วย) ภายใน 30 วันของการนัดหมายครั้งแรก (ผู้ป่วยรายนั้น) คือการติดตามหลังจาก 30 วันการนัดหมายจะเป็น" ใหม่ "การนัดหมายใด ๆ ภายใน 30 วัน กลายเป็น "การติดตาม" วันที่ 5 มกราคมเป็นมากกว่า 30 วันจากวันที่ 20 กุมภาพันธ์เช่นใหม่ อย่างไรก็ตามไม่ใช่วันที่ 30 กุมภาพันธ์จากวันที่ 12 กุมภาพันธ์ ฉันเสนอวิธีแก้ไขสิ่งที่เขาเขียนไม่ใช่ตารางที่ให้ หากผู้ใช้ต้องการจัดตำแหน่งกับสิ่งที่ตารางให้พวกเขาควรใช้ความล่าช้า พวกเขาควรชี้แจง ...
ผู้ใช้

1

ใช้ฟังก์ชั่น 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

การสาธิต -> https://rextester.com/TNW43808


2
สิ่งนี้ใช้ได้กับข้อมูลตัวอย่างปัจจุบัน แต่อาจให้ผลลัพธ์ที่ไม่ถูกต้องตามข้อมูลตัวอย่างที่แตกต่างกัน แม้ว่าคุณจะใช้apptDateเป็นorder byคอลัมน์ของlagฟังก์ชั่น (ซึ่งคุณควรเป็น id ไม่ได้รับประกันอะไรเลย) มันก็ยังสามารถแตกได้ง่ายโดยการแนะนำการนัดหมายติดตามเพิ่มเติม ดูนี้สาธิต Rextesterตัวอย่างเช่น แม้ว่าจะดีลอง ...
Zohar Peled

ขอบคุณ. ควรใช้วันที่แทนรหัส แต่ทำไมมันถึงผิดสำหรับ apptID = 6 25.01.2020 - 12.02.2020 -> 18 วัน -> ติดตาม
Digvijay S

2
เพราะมันควรจะเป็นและไม่ได้เป็นNew FollowUpเป็นเวลามากกว่า 30 วันนับตั้งแต่การนัดหมายครั้งแรกของผู้ป่วยรายนั้น ... คุณควรนับ 30 วันนับตั้งแต่Newการนัดหมายแต่ละครั้งและใช้Newอีกครั้ง ...
Zohar Peled

ใช่. ขอบคุณ. :( จำเป็นต้องสร้างใหม่เพื่อตรวจสอบระยะเวลาที่ถูกต้อง
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

ของฉันถูกต้อง ผู้เขียนไม่ถูกต้องให้ดูที่ผ่านไป

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.