Mamy tabelę spotkań, jak pokazano poniżej. Każde spotkanie należy sklasyfikować jako „Nowe” lub „Kontynuacja”. Każda wizyta (dla pacjenta) w ciągu 30 dni od pierwszej wizyty (tego pacjenta) jest kontynuacją. Po 30 dniach spotkanie jest ponownie „Nowe”. Każde spotkanie w ciągu 30 dni staje się „kontynuacją”.
Obecnie robię to, pisząc w pętli while.
Jak to zrobić bez PĘTLI?
Stół
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'
fast_forward
kursorem byłaby prawdopodobnie najlepszą opcją, pod względem wydajności.Odpowiedzi:
Musisz użyć zapytania rekurencyjnego.
30-dniowy okres liczony jest od poprzedniego (i nie, nie można tego zrobić bez rekurencji / dziwacznej aktualizacji / pętli). Właśnie dlatego wszystkie istniejące odpowiedzi przy użyciu tylko
ROW_NUMBER
nie powiodły się.db <> demo skrzypiec
Wynik:
Jak to działa:
Podobna klasa:
Warunkowe SUM na Oracle - Ograniczenie funkcji okna
Okno sesji (Azure Stream Analytics)
Działająca suma do momentu spełnienia określonego warunku - Dziwaczna aktualizacja
Uzupełnienie
Nigdy nie używaj tego kodu w produkcji!
Można to zrobić w rundzie pojedynczej (dziwaczna aktualizacja):
Pytanie:
db <> skrzypce Dziwaczna aktualizacja
źródło
RANGE x PRECEDING
klauzuli.Możesz to zrobić z rekurencyjnym cte. Najpierw należy zamówić według apptDate w obrębie każdego pacjenta. Można to osiągnąć za pomocą cte-run-of-the-mill.
Następnie, w części kotwiczącej swojego rekurencyjnego cte, wybierz pierwsze zamówienie dla każdego pacjenta, zaznacz status jako „nowy”, a także zaznacz apptDate jako datę najnowszego „nowego” rekordu.
W rekursywnej części Twojego rekurencyjnego cte, przyrostu do następnego spotkania, oblicz różnicę dni między obecnym spotkaniem a ostatnią „nową” datą spotkania. Jeśli jest on dłuższy niż 30 dni, zaznacz go jako „nowy” i zresetuj ostatnią nową datę spotkania. W przeciwnym razie zaznacz go jako „kontynuuj” i po prostu przekaż dni, które upłynęły od nowej daty spotkania.
Na koniec, w zapytaniu podstawowym, po prostu wybierz odpowiednie kolumny.
Powinienem wspomnieć, że początkowo usunąłem tę odpowiedź, ponieważ odpowiedź Abhijeeta Khandagale'a wydawała się odpowiadać twoim potrzebom za pomocą prostszego zapytania (po nieco przerobionej przeróbce). Ale wraz z twoim komentarzem na temat twoich wymagań biznesowych i dodanych przez ciebie przykładowych danych, usunąłem moje, ponieważ wierzę, że ten spełnia twoje potrzeby.
źródło
Nie jestem pewien, czy to dokładnie to, co zaimplementowałeś. Ale inną opcją, o której warto wspomnieć oprócz używania cte, jest użycie tabeli temp i aktualizacja w „rundach”. Zamierzamy więc zaktualizować tabelę temp, gdy wszystkie statusy nie są ustawione poprawnie, i zbudować wynik w sposób iteracyjny. Możemy kontrolować liczbę iteracji za pomocą po prostu zmiennej lokalnej.
Dlatego podzieliliśmy każdą iterację na dwa etapy.
Więc
Aktualizacja. Przeczytaj komentarz podany przez Łukasza. To zdecydowanie mądrzejszy sposób. Swoją odpowiedź zostawiam jako pomysł.
źródło
Uważam, że rekurencyjne wspólne wyrażenie jest świetnym sposobem na optymalizację zapytań, unikając pętli, ale w niektórych przypadkach może prowadzić do złej wydajności i należy tego unikać, jeśli to możliwe.
Korzystam z poniższego kodu, aby rozwiązać problem i przetestować, czy przyniesie więcej wartości, ale zachęcam do przetestowania go również na prawdziwych danych.
Pomysł jest dość prosty - chcę oddzielić rekordy w grupie (30 dni), w której grupie jest najmniejszy rekord
new
, a pozostałe sąfollow ups
. Sprawdź, jak zbudowana jest instrukcja:Więc:
* 1.0 / 30
dodaje+ 0.000001
; używamy również funkcji sufitu, aby uzyskaćsmallest integer greater than, or equal to, the specified numeric expression
Otóż to. Mając taką grupę, po prostu używamy,
ROW_NUMBER
aby znaleźć naszą datę rozpoczęcia i ustawić ją jako,new
a resztę pozostawić jakofollow ups
.źródło
Z należytym szacunkiem dla wszystkich i w IMHO,
Nie ma większego wzrostu wydajności podczas używania
Recursive CTE
iWindow Partition function
wszystko w jednym.Appid
powinno byćint identity(1,1)
lub powinno stale rosnąćclustered index
.Oprócz innych korzyści zapewnia również, że wszystkie kolejne rzędy
APPDate
tego pacjenta muszą być większe.W ten sposób możesz łatwo grać
APPID
w swoim zapytaniu, które będzie bardziej wydajne niż wstawianieinequality
operatora takiego jak>, <w APPDate. Umieszczenieinequality
operatora takiego jak>, <w APPID pomoże Sql Optimizer.W tabeli powinny znajdować się dwie kolumny daty
Ponieważ są to najważniejsze kolumny w najważniejszej tabeli, więc niewiele rzutów, konwersja.
Non clustered index
Można go więc utworzyć w AppdatePrzetestuj mój skrypt z innymi przykładowymi danymi i dowiedz się, dla których przykładowych danych nie działa. Nawet jeśli to nie działa, jestem pewien, że można to naprawić w samej logice skryptu.
źródło
Chociaż nie jest to wyraźnie określone w pytaniu, łatwo jest stwierdzić, że dat spotkań nie można po prostu podzielić na kategorie według grup 30-dniowych. To nie ma sensu biznesowego. Nie można również użyć identyfikatora appt. Można dziś umówić się na nowy termin
2020-09-06
. Oto jak rozwiązuję ten problem. Najpierw zdobądź pierwsze spotkanie, a następnie oblicz różnicę dat między każdym spotkaniem a pierwszym appt. Jeśli wynosi 0, ustaw „Nowy”. Jeśli <= 30 „Kontynuacja”. Jeśli> 30, ustaw jako „Niezdecydowany” i wykonaj kolejną kontrolę rundy, aż nie będzie już „Niezdecydowany”. I do tego naprawdę potrzebujesz pętli while, ale nie przechodzi ona przez każdą datę spotkania, a jedynie kilka zestawów danych. Sprawdziłem plan wykonania. Mimo że jest tylko 10 wierszy, koszt zapytania jest znacznie niższy niż przy użyciu rekurencyjnej CTE, ale nie tak niski, jak metoda addendum Łukasza Szozdy.źródło
Mam nadzieję, że to Ci pomoże.
źródło
Możesz użyć
Case
oświadczenia .Pytanie brzmi: czy tę kategorię należy przypisać na podstawie pierwszego spotkania, czy poprzedniego? To znaczy, jeśli pacjent miał trzy wizyty, czy powinniśmy porównać trzecie spotkanie z pierwszym, czy drugim?
Twój problem stwierdza pierwszy, a ja tak odpowiedziałem. Jeśli tak nie jest, będziesz chciał użyć
lag
.Pamiętaj też,
DateDiff
że nie stanowi to wyjątku w weekendy. Jeśli powinny to być tylko dni powszednie, musisz utworzyć własną funkcję Valared.źródło
za pomocą funkcji Lag
Demo -> https://rextester.com/TNW43808
źródło
apptDate
jakoorder by
kolumnylag
funkcji (co tak naprawdę powinieneś, ponieważ id nie jest gwarancją czegokolwiek), nadal można go łatwo złamać, wprowadzając kolejne spotkania kontrolne. Zobacz na przykład demo Rextester . Dobra próba ...New
a nieFollowUp
. Minęło ponad 30 dni od pierwszej wizyty tego pacjenta ... Powinieneś policzyć 30 dni od każdejNew
wizyty, a następnie użyćNew
ponownie ...Mój jest poprawny. Autorzy byli niepoprawni, patrz
źródło