Запит без циклу WHILE


18

У нас є таблиця зустрічей, як показано нижче. Кожен прийом потрібно класифікувати як "Новий" або "Спостереження". Будь-яка зустріч (для пацієнта) протягом 30 днів з моменту першого зустрічі (цього пацієнта) є подальшим спостереженням. Через 30 днів призначення знову "Нове". Будь-яка зустріч протягом 30 днів стає "Продовженням".

Зараз я це роблю, набираючи цикл.
Як цього досягти без циклу 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' 

Я не бачу вашого зображення, але я хочу підтвердити, якщо є 3 зустрічі, кожні 20 днів один від одного, останнє все ще "слідкуйте за правим", тому що, хоча минуло більше 30 днів від першого, від середини все ще менше ніж 20 днів. Це правда?
pwilcox

@pwilcox №. Третім буде нове призначення, як показано на зображенні
LCJ

Хоча петля над fast_forwardкурсором, мабуть, буде найкращим варіантом, ефективність роботи.
Девід

Відповіді:


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 - вихідна точка (якір - на кожного пацієнта)
  2. rec - рекурсивна частина, якщо різниця між поточним значенням і попереднім> 30 змінити категорію і початкову точку в контексті PatientId
  3. Основна - відображення відсортованого набору результатів

Подібний клас:

Умовна SUM для Oracle - перенесення віконної функції

Вікно сесії (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 Коли я писав цю відповідь, кожен із існуючих використовував простий рядовий номер, який не працював, тому я надав свою власну версію
Lukasz Szozda

Так, я був надто швидкий з відповіддю. Thx для коментування цього.
Ірдіс

2
Я вважаю, що rcte є єдиним рішенням для цього, поки SQL-сервер не правильно реалізує RANGE x PRECEDINGпункт.
Салман

1
@LCJ Химерне оновлення базується на "недокументованій" поведінці, і воно може змінитися в будь-який момент без попереднього повідомлення ( red-gate.com/simple-talk/sql/learn-sql-server/… )
Лукаш Szozda

5

Ви можете зробити це за допомогою рекурсивного cte. Спочатку слід замовити apptDate у кожного пацієнта. Це можна досягти за допомогою 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;

Слід зазначити, що я спочатку видалив цю відповідь, тому що відповідь Абхієт Хандагале здавалося, що відповідає вашим потребам більш простим запитом (трохи переробивши його). Але з вашим коментарем до нього щодо ваших потреб у бізнесі та доданих зразкових даних, я визнав свою, бо вважаю, що ця відповідає вашим потребам.


4

Я не впевнений, що це саме те, що ви реалізували. Але ще один варіант, про який варто згадати, окрім використання cte, - це використовувати таблицю temp та оновлення в "раундах". Таким чином, ми збираємось оновити темп-таблицю, поки всі статуси не встановлені правильно, і створимо результат ітераційним шляхом. Ми можемо контролювати кількість ітерацій, використовуючи просто локальну змінну.

Отже, ми розділили кожну ітерацію на два етапи.

  1. Встановіть усі значення Followup, близькі до Нових записів. Це досить просто зробити за допомогою правильного фільтра.
  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

Оновлення. Прочитайте коментар Лукаша. Це набагато розумніший спосіб. Я залишаю свою відповідь лише як ідею.


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
Ну, питання дещо інше, і цей підхід є спрощеним. Але це приємний приклад того, як реалізовувати кумедне вікно
Лукаш Шозда

Йдеться і про продуктивність. Я вважаю, що рекурсивність повинна бути повільнішою.
gotqn

3

З належною повагою до всіх і в ІМХО,

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. Сьогодні можна домовитися про зустріч2020-09-06. Ось як я вирішую це питання. Спочатку отримайте першу зустріч, потім обчисліть різницю дат між кожною зустріччю та першою аплет. Якщо це 0, встановіть значення "Нове". Якщо <= 30 'Спостереження'. Якщо> 30, встановіть значення "Невирішений" і перевірте наступний раунд, поки не буде більше "Невизначено". А для цього вам дійсно потрібен цикл, але він не проходить через кожну дату зустрічі, а лише кілька наборів даних. Я перевірив план виконання. Незважаючи на те, що всього 10 рядків, вартість запиту значно нижча, ніж за допомогою рекурсивного CTE, але не така низька, як метод доповнення Лукаша Шозда.

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не є винятком для вихідних. Якщо це лише вихідні дні, вам потрібно буде створити власну функцію "Скалярне оцінювання".


1
Це не пов'язує два послідовних зустрічі, це посилання appt 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функції (яку ви дійсно повинні мати як ідентифікатор - це не гарантія нічого), її все одно можна легко зламати, запровадивши додаткові зустрічі. Наприклад, дивіться цю демонстраційну програму Rextester . Хоча добре спробуйте ...
Зохар Пелед

Дякую. Потрібно було використовувати дату замість ідентифікатора. Але чому це неправильно для apptID = 6 25.01.2020 - 12.02.2020 -> 18 днів -> подальші дії.
Digvijay S

2
Тому що це має бути а, Newа не а FollowUp. Минуло більше 30 днів з моменту першого призначення цього пацієнта ... Вам слід відлічити 30 днів з кожного Newприйому, а потім Newзнову використовувати ...
Зохар Пелед

Так. Дякую. :( Потрібно створити новий для перевірки дійсного періоду дати.
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

Моє правильно. Автори були невірними, див

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.