Truy vấn không có vòng lặp WHILE


18

Chúng tôi có bảng hẹn như hình dưới đây. Mỗi cuộc hẹn cần được phân loại là "Mới" hoặc "Theo dõi". Bất kỳ cuộc hẹn nào (đối với bệnh nhân) trong vòng 30 ngày kể từ cuộc hẹn đầu tiên (của bệnh nhân đó) là Theo dõi. Sau 30 ngày, cuộc hẹn lại là "Mới". Bất kỳ cuộc hẹn nào trong vòng 30 ngày sẽ trở thành "Theo dõi".

Tôi hiện đang làm điều này bằng cách gõ vòng lặp while.
Làm thế nào để đạt được điều này mà không cần vòng lặp WHILE?

nhập mô tả hình ảnh ở đây

Bàn

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' 

Tôi không thể nhìn thấy hình ảnh của bạn, nhưng tôi muốn xác nhận, nếu có 3 cuộc hẹn, mỗi lần 20 ngày với nhau, lần cuối cùng vẫn là 'theo dõi', bởi vì mặc dù đã hơn 30 ngày kể từ lần đầu tiên, Vẫn còn chưa đầy 20 ngày nữa. Điều này có đúng không?
pwilcox

@pwilcox Số Người thứ ba sẽ là cuộc hẹn mới như trong hình
LCJ

Trong khi vòng lặp trên fast_forwardcon trỏ có thể là lựa chọn tốt nhất của bạn, hiệu suất khôn ngoan.
David TOUR TOUR Markovitz

Câu trả lời:


14

Bạn cần sử dụng truy vấn đệ quy.

Thời hạn 30 ngày được tính bắt đầu từ trước (và không thể thực hiện được nếu không có đệ quy / cập nhật kỳ quặc / vòng lặp). Đó là lý do tại sao tất cả các câu trả lời hiện có chỉ sử dụng ROW_NUMBERthất bại.

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 <> fiddle demo

Đầu ra:

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

Làm thế nào nó hoạt động:

  1. f - lấy điểm bắt đầu (neo - trên mỗi PatientId)
  2. rec - recursibe part, nếu chênh lệch giữa giá trị hiện tại và trước đó là> 30 thay đổi danh mục và điểm bắt đầu, trong bối cảnh của PatientId
  3. Chính - hiển thị kết quả sắp xếp

Lớp tương tự:

SUM có điều kiện trên Oracle - Giới hạn chức năng cửa sổ

Cửa sổ phiên (Phân tích luồng Azure)

Chạy Tổng cho đến khi điều kiện cụ thể là đúng - Cập nhật kỳ quặc


Phụ lục

Đừng bao giờ sử dụng mã này vào sản xuất!

Nhưng một lựa chọn khác, đáng nói đến ngoài việc sử dụng cte, là sử dụng bảng tạm thời và cập nhật trong "vòng"

Nó có thể được thực hiện trong vòng "đơn" (cập nhật kỳ quặc):

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

Truy vấn:

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 Cập nhật kỳ quặc


1
logic của bạn trông rất giống với tôi. Bạn có thể mô tả bất kỳ sự khác biệt đáng kể?
pwilcox

@pwilcox Khi tôi viết câu trả lời này, mọi người hiện đang sử dụng row_number đơn giản không hoạt động, đây là lý do tại sao tôi cung cấp phiên bản của riêng mình
Lukasz Szozda

Vâng, tôi đã quá nhanh chóng với câu trả lời. Thx cho ý kiến ​​về điều đó.
Irdis

2
Tôi tin rằng RCte là giải pháp duy nhất cho việc này cho đến khi máy chủ SQL thực hiện đúng RANGE x PRECEDINGmệnh đề.
Salman A

1
@LCJ Quirky cập nhật được dựa trên hành vi "không có giấy tờ" và nó có thể thay đổi trong thời điểm nào mà không cần thông báo ( red-gate.com/simple-talk/sql/learn-sql-server/... )
Lukasz Szozda

5

Bạn có thể làm điều này với một cte đệ quy. Trước tiên bạn nên đặt hàng theo apptDate trong mỗi bệnh nhân. Điều đó có thể được thực hiện bằng một cte run-of-the-mill.

Sau đó, trong phần neo của cte đệ quy của bạn, chọn thứ tự đầu tiên cho mỗi bệnh nhân, đánh dấu trạng thái là 'mới' và cũng đánh dấu apptDate là ngày của bản ghi 'mới' gần đây nhất.

Trong phần đệ quy của cte đệ quy của bạn, tăng đến cuộc hẹn tiếp theo, hãy tính chênh lệch số ngày giữa cuộc hẹn hiện tại và ngày hẹn 'mới' gần đây nhất. Nếu quá 30 ngày, hãy đánh dấu là 'mới' và đặt lại ngày hẹn mới gần nhất. Mặt khác, đánh dấu nó là 'theo dõi' và chỉ cần vượt qua những ngày hiện tại kể từ ngày hẹn mới.

Cuối cùng, trong truy vấn cơ sở, chỉ cần chọn các cột bạn muốn.

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;

Tôi nên đề cập rằng ban đầu tôi đã xóa câu trả lời này vì câu trả lời của Abhijeet Khandagale dường như đáp ứng nhu cầu của bạn với một truy vấn đơn giản hơn (sau khi làm lại một chút). Nhưng với nhận xét của bạn cho anh ấy về yêu cầu kinh doanh của bạn và dữ liệu mẫu đã thêm của bạn, tôi đã hoàn tác của tôi vì tin rằng điều này đáp ứng nhu cầu của bạn.


4

Tôi không chắc chắn rằng đó chính xác là những gì bạn thực hiện. Nhưng một lựa chọn khác, đáng nói đến ngoài việc sử dụng cte, là sử dụng bảng tạm thời và cập nhật trong "vòng". Vì vậy, chúng tôi sẽ cập nhật bảng tạm thời trong khi tất cả các trạng thái không được đặt chính xác và xây dựng kết quả theo cách lặp. Chúng ta có thể kiểm soát số lần lặp bằng cách sử dụng biến cục bộ đơn giản.

Vì vậy, chúng tôi chia mỗi lần lặp thành hai giai đoạn.

  1. Đặt tất cả các giá trị Theo dõi gần với Bản ghi mới. Điều đó khá dễ dàng để làm chỉ bằng cách sử dụng bộ lọc đúng.
  2. Đối với phần còn lại của các bản ghi không có trạng thái được đặt, chúng tôi có thể chọn đầu tiên trong nhóm có cùng PatientID. Và nói rằng chúng là mới vì chúng không được xử lý bởi giai đoạn đầu tiên.

Vì thế

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

Cập nhật. Đọc bình luận được cung cấp bởi Lukasz. Đó là cách thông minh hơn nhiều. Tôi để lại câu trả lời của tôi như một ý tưởng.


4

Tôi tin rằng biểu thức chung đệ quy là cách tuyệt vời để tối ưu hóa các truy vấn tránh các vòng lặp, nhưng trong một số trường hợp, nó có thể dẫn đến hiệu suất kém và nên tránh nếu có thể.

Tôi sử dụng mã dưới đây để giải quyết vấn đề và kiểm tra nó sẽ có nhiều giá trị hơn, nhưng khuyến khích bạn cũng kiểm tra nó với dữ liệu thực của bạn.

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

nhập mô tả hình ảnh ở đây

Ý tưởng khá đơn giản - Tôi muốn tách các bản ghi trong nhóm (30 ngày), trong đó bản ghi nhỏ nhất trong nhóm, các bản ghi newkhác follow ups. Kiểm tra cách tuyên bố được xây dựng:

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

nhập mô tả hình ảnh ở đây

Vì thế:

  1. đầu tiên, chúng tôi sẽ có được ngày đầu tiên, cho mỗi nhóm và tính toán sự khác biệt trong ngày với ngày hiện tại
  2. sau đó, chúng tôi muốn có được các nhóm - * 1.0 / 30được thêm vào
  3. trong 30, 60, 90, v.v. chúng tôi đang nhận được toàn bộ số và chúng tôi muốn bắt đầu một giai đoạn mới, tôi đã thêm vào + 0.000001; Ngoài ra, chúng tôi đang sử dụng chức năng trần để có đượcsmallest integer greater than, or equal to, the specified numeric expression

Đó là nó. Có nhóm như vậy, chúng tôi chỉ đơn giản là sử dụng ROW_NUMBERđể tìm ngày bắt đầu của chúng tôi và làm cho nó newvà rời khỏi phần còn lại như follow ups.


2
Vâng, câu hỏi là một chút khác nhau và apporach này là quá đơn giản. Nhưng đó là một ví dụ hay về cách triển khai cửa sổ lộn xộn
Lukasz Szozda

Đó là về hiệu suất, quá. Tôi tin rằng đệ quy nên chậm hơn.
gotqn

3

Với sự tôn trọng đối với mọi người và trong IMHO,

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

Không có nhiều hiệu suất đạt được khi sử dụng Recursive CTEWindow Partition functiontất cả trong một.

Appidnên được int identity(1,1), hoặc nó sẽ ngày càng tăng clustered index.

Ngoài lợi ích khác, nó cũng đảm bảo rằng tất cả các hàng liên tiếp APPDatecủa bệnh nhân đó phải lớn hơn.

Bằng cách này, bạn có thể dễ dàng chơi với APPIDtruy vấn của mình sẽ hiệu quả hơn so với việc đặt inequalitytoán tử như>, <trong APPDate. Đưa inequalitytoán tử như>, <trong APPID sẽ hỗ trợ Trình tối ưu hóa Sql.

Ngoài ra nên có hai cột ngày trong bảng như

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

Vì đây là những cột quan trọng nhất trong bảng quan trọng nhất, nên không có nhiều cast, convert.

Vì vậy, Non clustered indexcó thể được tạo ra trên Appdate

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

Kiểm tra tập lệnh của tôi với dữ liệu mẫu khác và tôi biết dữ liệu mẫu nào không hoạt động. Ngay cả khi nó không hoạt động thì tôi chắc chắn nó có thể được sửa trong chính logic script của tôi.

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

Mặc dù nó không được giải quyết rõ ràng trong câu hỏi, thật dễ dàng để nhận ra rằng ngày hẹn không thể được phân loại đơn giản theo nhóm 30 ngày. Nó làm cho không có ý nghĩa kinh doanh. Và bạn cũng không thể sử dụng id appt. Một người có thể làm một cuộc hẹn mới ngày hôm nay cho2020-09-06. Đây là cách tôi giải quyết vấn đề này. Đầu tiên, có được cuộc hẹn đầu tiên, sau đó tính chênh lệch ngày giữa mỗi cuộc hẹn và lần đầu tiên. Nếu là 0, hãy đặt thành 'Mới'. Nếu <= 30 'Theo dõi'. Nếu> 30, đặt thành 'Chưa quyết định' và thực hiện kiểm tra vòng tiếp theo cho đến khi không còn 'Chưa quyết định'. Và để làm điều đó, bạn thực sự cần một vòng lặp while, nhưng nó không lặp qua từng ngày hẹn, mà chỉ có một vài bộ dữ liệu. Tôi đã kiểm tra kế hoạch thực hiện. Mặc dù chỉ có 10 hàng, chi phí truy vấn thấp hơn đáng kể so với sử dụng CTE đệ quy, nhưng không thấp như phương pháp phụ lục của 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

Tôi hy vọng điều này sẽ giúp bạn.

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

Cảm ơn @ x00 vì đã chỉnh sửa mã ở định dạng có thể đọc được, tôi đang sử dụng điện thoại di động của mình để đăng câu trả lời nên không thể đưa ra các thụt lề thích hợp.
Abhijeet Khandagale

Tôi nghĩ rằng đây là bản chất câu trả lời đúng. Nhưng đó là một câu trả lời kém chất lượng ở chỗ nó không được giải thích và mã có một truy vấn bên ngoài không cần thiết khi việc sửa đổi phần bên trong sẽ hoạt động tốt. Nếu bạn có thể giải quyết những vấn đề đó, tôi sẽ vui lòng bỏ phiếu cho bạn.
pwilcox

1
@pwilcox, cảm ơn vì lời đề nghị có giá trị, tôi đã chỉnh sửa câu trả lời và đăng nó ngay từ bây giờ. Khi tôi đi du lịch và tôi không có máy tính xách tay bên mình, tôi sẽ đăng bài giải thích trong một hoặc hai ngày.
Abhijeet Khandagale

1
@AbhijeetKhandagale Điều này không đáp ứng hoàn toàn yêu cầu kinh doanh. Tôi đã thêm một kịch bản thất bại trong câu hỏi. Đối với bệnh nhân 303, cuộc hẹn ngày 2 tháng 2 nên được theo dõi; nhưng truy vấn của bạn cho biết đó là "Mới"
LCJ

1

Bạn có thể sử dụng một Casetuyên bố .

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

Câu hỏi là, loại này nên được chỉ định dựa trên cuộc hẹn ban đầu, hoặc loại trước? Đó là, nếu một Bệnh nhân đã có ba cuộc hẹn, chúng ta nên so sánh cuộc hẹn thứ ba với cuộc hẹn thứ nhất hay lần thứ hai?

Vấn đề của bạn là vấn đề đầu tiên, đó là cách tôi đã trả lời. Nếu đó không phải là trường hợp, bạn sẽ muốn sử dụng lag.

Ngoài ra, hãy nhớ rằng DateDiffkhông ngoại lệ cho cuối tuần. Nếu đây chỉ là ngày trong tuần, bạn sẽ cần tạo chức năng Giá trị vô hướng của riêng mình.


1
Điều này không liên kết hai cuộc hẹn liên tiếp, liên kết này nối 1 với tất cả các cuộc hẹn sau và tính toán giữa các ngày cho tất cả các cuộc hẹn. Bạn sẽ trả lại quá nhiều hồ sơ theo cách này, vì appt 1 hiện có mối quan hệ với 2, 3, 4, appt 2 có mối quan hệ với 3, 4 ...
steenbergh

Điểm tốt. Tôi đã cập nhật câu trả lời của mình để thực hiện chọn phụ cho A1.
người dùng

1
Nó không cho kết quả như mong đợi. Cuộc hẹn ngày 20 tháng 2 sẽ là "Theo dõi"
LCJ

Câu hỏi không rõ ràng ... Mô tả poster là: "Bất kỳ cuộc hẹn nào (đối với bệnh nhân) trong vòng 30 ngày kể từ cuộc hẹn đầu tiên (của bệnh nhân đó) là Theo dõi. Sau 30 ngày, cuộc hẹn lại là" Mới ". trở thành "Theo dõi". " Ngày 5 tháng 1 chắc chắn là hơn 30 ngày kể từ ngày 20 tháng 2, tức là Mới. Tuy nhiên, nó không phải là 30 ngày nữa kể từ ngày 12 tháng 2. Tôi đưa ra một giải pháp cho những gì anh ấy viết, không phải bảng được cung cấp. Nếu người dùng muốn căn chỉnh với những gì bảng cung cấp, họ nên sử dụng độ trễ. Họ cũng nên làm rõ ...
người dùng

1

sử dụng chức năng 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

Bản trình diễn -> https://rextester.com/TNW43808


2
Điều này hoạt động trên dữ liệu mẫu hiện tại nhưng có thể mang lại kết quả sai với dữ liệu mẫu khác. Ngay cả khi bạn sử dụng apptDatelàm order bycột của laghàm (mà bạn thực sự nên là id không phải là một sự đảm bảo cho bất cứ điều gì), nó vẫn có thể dễ dàng bị phá vỡ bằng cách giới thiệu thêm các cuộc hẹn tiếp theo. Xem bản demo Rextester này chẳng hạn. Cố gắng tốt, mặc dù ...
Zohar Peled

Cảm ơn bạn. Nên đã sử dụng ngày thay vì ID. Nhưng tại sao nó sai cho apptID = 6 25.01.2020 - 12.02.2020 -> 18 ngày -> theo dõi.
Digvijay S

2
Bởi vì nó nên là một Newvà không phải là a FollowUp. Đã hơn 30 ngày kể từ cuộc hẹn đầu tiên của bệnh nhân đó ... Bạn nên đếm 30 ngày kể từ mỗi Newcuộc hẹn và sau đó sử dụng Newlại ...
Zohar Peled

Đúng. Cảm ơn bạn. :( Cần tạo một cái mới để kiểm tra thời hạn hợp lệ của ngày.
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

Của tôi là chính xác. Các tác giả đã không chính xác, xem trôi qua

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.