Dados os seguintes dados:
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') ;
... que representa quando um determinado usuário foi atribuído a uma conta.
Estou procurando estabelecer quem possuía uma determinada conta no último dia de cada mês (a data atribuída é a data em que a conta transferiu a propriedade), com todos os finais de mês ausentes preenchidos (possivelmente criados a partir de uma tabela útil dates
que tenho disponível, com colunas úteis e DateKey
, [cortesia de @AaronBertrand]) 1 .Date
LastDayOfMonth
Os resultados desejados seriam:
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
Fazer a parte inicial disso com uma função de janela é trivial, é adicionar as linhas "ausentes" com as quais estou lutando.
Uma abordagem para esse problema é fazer o seguinte:
LEAD
no SQL Server 2008. Você pode usarAPPLY
ou uma suquery para isso.Modifiquei um pouco seus dados de teste para tornar os resultados determinísticos. Também adicionou um índice:
Aqui está a tabela de dimensão de data mais preguiçosa de todos os tempos:
For step 1, there are plenty of ways to emulate
LEAD
. Here's one method:For step 2, we need to change the NULL values to something else. You want to include the final month for each account, so adding one month to the starting date suffices:
For step 3, we can join to the date dimension table. The column from the dimension table is exactly the column you need for the result set:
I didn't like the query that I got when I put it all together. There can be issues with join order when combining
OUTER APPLY
andINNER JOIN
. To get the join order I wanted I rewrote it with a subquery:I don't know how much data you have so it might not matter for you. But the plan looks how I want it to:
The results match yours:
Aqui eu não uso tabela de calendário, mas uma tabela de números naturais nums.dbo.nums (espero que você também tenha, se não, pode ser facilmente gerado)
Eu tenho a resposta um pouco diferente da sua ('JOSH' <-> 'JAMES') porque seus dados contêm essas 2 linhas:
com a mesma conta e data atribuída e você não precisou qual deve ser tomada nesta situação.
Triângulo JUNTE-SE para a vitória!
Os resultados são:
Plano de Execução Interativo aqui.
Estatísticas de E/S e TEMPO (truncados todos os valores zero após leituras lógicas):
Consulta para criar 'tabelas temporárias necessárias e testar a instrução T-SQL que estou sugerindo:
Esta não é de forma alguma uma solução de aparência limpa, mas parece fornecer os resultados que você está procurando (tenho certeza de que outros terão consultas agradáveis, limpas e totalmente otimizadas para você).
Usei a tabela de dimensão de data do Aaron Bertrand, como você também mencionou em sua pergunta (que é uma tabela super prática para esses cenários) e escrevi o seguinte código:
Eu adicionei a
EndOfMonth
coluna à#dim
tabela (logo após aFirstOfMonth
coluna) usando o seguinte código:E a solução: