Biorąc pod uwagę następujące dane:
create table #histories
(
username varchar(10),
account varchar(10),
assigned date
);
insert into #histories
values
('PHIL','ACCOUNT1','2017-01-04'),
('PETER','ACCOUNT1','2017-01-15'),
('DAVE','ACCOUNT1','2017-03-04'),
('ANDY','ACCOUNT1','2017-05-06'),
('DAVE','ACCOUNT1','2017-05-07'),
('FRED','ACCOUNT1','2017-05-08'),
('JAMES','ACCOUNT1','2017-08-05'),
('DAVE','ACCOUNT2','2017-01-02'),
('PHIL','ACCOUNT2','2017-01-18'),
('JOSH','ACCOUNT2','2017-04-08'),
('JAMES','ACCOUNT2','2017-04-09'),
('DAVE','ACCOUNT2','2017-05-06'),
('PHIL','ACCOUNT2','2017-05-07') ;
... co oznacza, kiedy dany użytkownik został przypisany do konta.
Chcę ustalić, kto był właścicielem danego konta w ostatnim dniu każdego miesiąca (przypisaną datą jest data przeniesienia własności konta), z uzupełnieniem brakujących końcówek miesięcy (prawdopodobnie utworzonych z przydatnej dates
tabeli, którą mam, użytecznych kolumny DateKey
, Date
i LastDayOfMonth
[uprzejmości @AaronBertrand]) 1 .
Pożądane wyniki to:
PETER, ACCOUNT1, 2017-01-31
PETER, ACCOUNT1, 2017-02-28
DAVE, ACCOUNT1, 2017-03-31
DAVE, ACCOUNT1, 2017-04-30
FRED, ACCOUNT1, 2017-05-31
FRED, ACCOUNT1, 2017-06-30
FRED, ACCOUNT1, 2017-07-31
JAMES, ACCOUNT1, 2017-08-31
PHIL, ACCOUNT2, 2017-01-31
PHIL, ACCOUNT2, 2017-02-28
PHIL, ACCOUNT2, 2017-03-31
JAMES, ACCOUNT2, 2017-04-30
PHIL, ACCOUNT2, 2017-05-31
Wykonanie początkowej części tego z funkcją okienkowania jest trywialne, dodaje „brakujące” wiersze, z którymi walczę.
2017-05
ponieważ miał je na koncie2017-05-07
i nie było kolejnego właściciela?Odpowiedzi:
Jednym podejściem do tego problemu jest wykonanie następujących czynności:
LEAD
w SQL Server 2008. Możesz do tego użyćAPPLY
lub suquery.Zmodyfikowałem trochę twoje dane testowe, aby wyniki były deterministyczne. Dodano także indeks:
Oto najbardziej leniwa tabela wymiarów daty wszechczasów:
W kroku 1 istnieje wiele sposobów naśladowania
LEAD
. Oto jedna metoda:W kroku 2 musimy zmienić wartości NULL na coś innego. Chcesz dołączyć ostatni miesiąc dla każdego konta, więc wystarczy dodać miesiąc do daty początkowej:
W kroku 3 możemy dołączyć do tabeli wymiarów daty. Kolumna z tabeli wymiarów jest dokładnie kolumną potrzebną do zestawu wyników:
Nie podobało mi się zapytanie, które otrzymałem, kiedy wszystko to złożyłem. Mogą wystąpić problemy z kolejnością łączenia podczas łączenia
OUTER APPLY
iINNER JOIN
. Aby uzyskać kolejność łączenia, którą chciałem, przepisałem ją z podzapytaniem:Nie wiem, ile masz danych, więc może to nie mieć znaczenia. Ale plan wygląda tak, jak chcę:
Wyniki pasują do twoich:
źródło
Tutaj nie używam tabeli kalendarza, ale tabeli liczb naturalnych nums.dbo.nums (mam nadzieję, że ją masz, jeśli nie, można ją łatwo wygenerować)
Mam odpowiedź nieco inną niż twoja („JOSH” <-> „JAMES”), ponieważ twoje dane zawierają te 2 wiersze:
z tym samym kontem i przypisaną datą, a Ty nie sprecyzowałeś, który z nich należy podjąć w tej sytuacji.
źródło
Nie jest to wcale dobrze wyglądające rozwiązanie, ale wydaje się, że zapewnia wyniki, których szukasz (jestem pewien, że inni będą mieli dla Ciebie ładne, czyste, w pełni zoptymalizowane zapytania).
źródło
Użyłem tabeli wymiarów daty Aarona Bertranda, jak wspominasz w swoim pytaniu (która jest bardzo przydatną tabelą dla takich scenariuszy) i napisałem następujący kod:
Dodałem
EndOfMonth
kolumnę do#dim
tabeli (zaraz poFirstOfMonth
kolumnie), używając następującego kodu:I rozwiązanie:
źródło
Trójkąt DOŁĄCZ do wygranej!
Wyniki to:
Interaktywny plan wykonania tutaj.
Statystyki I / O i TIME (obcięte wszystkie wartości zerowe po odczytach logicznych):
Zapytanie, aby utworzyć wymagane tabele tymczasowe i przetestować instrukcję T-SQL Sugeruję:
źródło