WHILE 루프가없는 쿼리


18

아래와 같이 약속 표가 있습니다. 각 약속은 "신규"또는 "추종"으로 분류해야합니다. (환자에 대한) 첫 예약 후 30 일 이내에 (환자에 대한) 약속은 후속 조치입니다. 30 일 후에 약속은 다시 "신규"입니다. 30 일 이내의 약속은 "사후 관리"가됩니다.

현재 while 루프를 입력 하여이 작업을 수행하고 있습니다.
WHILE 루프없이 이것을 달성하는 방법?

여기에 이미지 설명을 입력하십시오

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' 

이미지를 볼 수 없지만 서로 20 일마다 3 개의 약속이있는 경우 마지막 약속은 여전히 ​​'추적'입니다. 처음부터 30 일 이상이되었지만 아직 중간에서 20 일도 안 남았습니다. 이것이 사실입니까?
pwilcox

@pwilcox 아니요. 세 번째는 이미지에 표시된대로 새로운 약속입니다.
LCJ

루프 오버 fast_forward커서가 아마도 최선의 선택이지만 성능은 현명합니다.
David דודו Markovitz

답변:


14

재귀 쿼리를 사용해야합니다.

30 일 기간은 prev부터 계산됩니다 (재귀 / 기발한 업데이트 / 루프 없이는 수행 할 수 없음). 그렇기 때문에 모든 기존 답변 만 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-시작점 확보 (앵커-모든 환자 ID 당)
  2. rec-recursibe 부분, 현재 값과 이전의 차이가> 30 인 경우 PatientId와 관련하여 범주와 시작점을 변경합니다.
  3. 메인-디스플레이 결과 정렬

비슷한 수업 :

Oracle의 조건부 SUM- 윈도우 함수 캡핑

세션 창 (Azure Stream Analytics)

특정 조건이 충족 될 때까지 총계 실행 -기발한 업데이트


추가

프로덕션에서이 코드를 사용하지 마십시오!

그러나 cte를 사용하는 것 외에 언급 할 가치가있는 또 다른 옵션은 임시 테이블을 사용하고 "라운드"로 업데이트하는 것입니다

"단일"라운드에서 수행 할 수 있습니다 (기발한 업데이트).

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 <> 바이올린 기발한 업데이트


1
당신의 논리는 나의 것과 매우 비슷해 보입니다. 중요한 차이점을 설명 할 수 있습니까?
pwilcox

@pwilcox이 답변을 쓸 때 기존의 모든 답변이 작동하지 않는 간단한 row_number를 사용하고 있었기 때문에 내 버전을 제공했습니다.
Lukasz Szozda

네, 답변이 너무 빠릅니다. 그것에 대해 언급 해 주셔서 감사합니다.
Irdis

2
SQL Server가 RANGE x PRECEDING절을 올바르게 구현할 때까지 rcte가 유일한 해결책이라고 생각합니다 .
살만 A

1
@LCJ Quirky 업데이트는 "언급되지 않은"동작을 기반으로하며 통지없이 언제든지 변경 될 수 있습니다 ( red-gate.com/simple-talk/sql/learn-sql-server/… )
Lukasz Szozda

5

재귀 cte 로이 작업을 수행 할 수 있습니다. 먼저 각 환자 내에서 apptDate로 주문해야합니다. 그것은 방아쇠 cte에 의해 달성 될 수 있습니다.

그런 다음 재귀 cte의 앵커 부분에서 각 환자의 첫 번째 주문을 선택하고 상태를 '신규'로 표시하고 apptDate를 가장 최근의 '신규'레코드의 날짜로 표시하십시오.

재귀 cte의 재귀 부분에서 다음 약속까지 증가하고 현재 약속과 가장 최근의 '새'약속 날짜 사이의 일 차이를 계산하십시오. 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를 사용하는 것 외에 언급 할 가치가있는 또 다른 옵션은 임시 테이블을 사용하고 "라운드"로 업데이트하는 것입니다. 따라서 모든 상태가 올바르게 설정되지 않고 임시 테이블을 업데이트하고 반복적 인 방식으로 결과를 작성합니다. 단순히 로컬 변수를 사용하여 반복 횟수를 제어 할 수 있습니다.

따라서 각 반복을 두 단계로 나눕니다.

  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시작 날짜를 찾고 new나머지를로 남겨두고 사용합니다 follow ups.


2
글쎄, 질문은 약간 다르며이 접근 방식은 지나치게 단순화되었습니다. 그러나 그것은 넘어지는 창
Lukasz Szozda

성능도 중요합니다. 나는 재귀가 느려 야한다고 생각합니다.
gotqn

3

모든 사람과 IMHO와 관련하여

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

사용할 때 많은 성능 향상이 없습니다 Recursive CTEWindow Partition function모든 일인치

Appid해야 할 int identity(1,1), 또는 계속 증가해야한다 clustered index.

다른 이점과는 별도로 APPDate해당 환자의 모든 연속 행 이 더 커야합니다.

이렇게하면 APPDate에>, <와 같은 연산자를 APPID배치하는 것보다 효율적인 쿼리에서 쉽게 재생할 수 있습니다 inequality. inequalityAPPID에>, <와 같은 연산자를 넣으면 Sql Optimizer에 도움이됩니다.

또한 테이블에 두 개의 날짜 열이 있어야합니다.

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

이것들은 가장 중요한 테이블에서 가장 중요한 열이므로 변환하지 마십시오.

그래서 Non clustered indexAppdate 만들 수 있습니다

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

다른 샘플 데이터로 스크립트를 테스트하고 lemme은 작동하지 않는 샘플 데이터를 알고 있습니다. 작동하지 않더라도 스크립트 논리 자체에서 수정할 수 있다고 확신합니다.

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. 이 문제를 해결하는 방법은 다음과 같습니다. 먼저 첫 번째 약속을 얻은 다음 각 약속과 첫 번째 appt 간의 날짜 차이를 계산하십시오. 0이면 'New'로 설정하십시오. <= 30 '사후 관리'인 경우. > 30 인 경우 '미정'으로 설정하고 '미정'이 더 이상 없을 때까지 다음 라운드 점검을 수행하십시오. 이를 위해서는 실제로 while 루프가 필요하지만 각 약속 날짜를 반복하지는 않지만 몇 가지 데이터 세트 만 반복합니다. 실행 계획을 확인했습니다. 행이 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 월 2 일 약속이 후속 조치 여야합니다. 그러나 귀하의 질문에
따르면

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

문제는이 범주가 초기 약속 또는 이전의 약속을 기준으로 할당되어야합니까? 즉, 환자가 3 명의 약속을 가졌다면 우리는 세 번째 약속을 첫 번째 약속 또는 두 번째 약속과 비교해야합니까?

당신은 문제에 대해 먼저 대답합니다. 그렇지 않은 경우 사용하고 싶을 것입니다lag 입니다.

또한 DateDiff주말에는 예외가 아닙니다. 이것이 평일 만되어야한다면, 자신 만의 Scalar-Valued 함수를 만들어야합니다.


1
이것은 두 개의 순차적 약속을 연결하지 않으며, appt 1을 모든 후속 약속에 연결하고 모든 약속 사이의 날짜를 계산합니다. appt 1이 2, 3, 4와 관계가 있고 appt 2가 3, 4와 관계가 있기 때문에이 방법으로 너무 많은 레코드를 리턴 할 것입니다.
steenbergh

좋은 지적. A1에 대한 하위 선택을 수행하기 위해 답변을 업데이트했습니다.
사용자

1
예상 된 결과를 제공하지 않습니다. 2 월 20 일 약속은 "추적"이어야합니다
LCJ

질문이 불분명합니다 ... 포스터 설명은 다음과 같습니다 : "환자의 예약 후 30 일 이내에 (환자에 대한) 약속은 후속 조치입니다. 30 일 후에 약속은 다시"신규 "입니다. "사후 관리"가됩니다. " 1 월 5 일은 2 월 20 일부터 30 일 이상입니다. 그러나 2 월 12 일부터 30 일은 없습니다. 나는 제공된 표가 아니라 그가 쓴 것에 대한 해결책을 제시한다. 사용자가 테이블이 제공하는 내용에 맞추려면 지연을 사용해야합니다. 그들은 또한 명확히해야합니다.
사용자

1

지연 기능 사용


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
현재 샘플 데이터에서 작동하지만 다른 샘플 데이터에서 잘못된 결과를 얻을 수 있습니다. 함수 apptDateorder by열로 사용하더라도 lag(실제로 id는 아무것도 보장하지 않으므로) 후속 약속을 더 많이 도입하면 쉽게 깨질 수 있습니다. 예를 들어이 Rextester 데모 를 참조하십시오 . 좋은 시도이지만 ...
Zohar Peled

감사합니다. ID 대신 날짜를 사용해야합니다. 그러나 왜 apptID = 6 25.01.2020-12.02.2020-> 18 일-> 후속 조치에 대해 잘못된 것입니까?
Digvijay S

2
왜냐하면 a New가 아니 어야하기 때문 FollowUp입니다. 환자의 첫 예약 이후 30 일 이상이 지났습니다 ... 각 New약속 이후 30 일을 세었다 가 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.