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 possui 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 fins de mês ausentes preenchidos (possivelmente criados a partir de uma dates
tabela útil que eu tenho disponível, com colunas úteis DateKey
, Date
e LastDayOfMonth
, [cortesia de @AaronBertrand]) 1 .
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 janelas é trivial, é adicionar as linhas "ausentes" com as quais estou lutando.
2017-05
porque ele a possuía2017-05-07
e não havia um titular subsequente?Respostas:
Uma abordagem para esse problema é fazer o seguinte:
LEAD
no SQL Server 2008. Você pode usarAPPLY
ou fazer uma conquista para isso.Modifiquei um pouco seus dados de teste para tornar os resultados determinísticos. Também foi adicionado um índice:
Aqui está a tabela de dimensões de data mais preguiçosa de todos os tempos:
Para a etapa 1, há várias maneiras de emular
LEAD
. Aqui está um método:Para a etapa 2, precisamos alterar os valores NULL para outra coisa. Você deseja incluir o mês final de cada conta, portanto, basta adicionar um mês à data de início:
Para a etapa 3, podemos ingressar na tabela de dimensões de data. A coluna da tabela de dimensões é exatamente a coluna necessária para o conjunto de resultados:
Não gostei da consulta que recebi quando reuni tudo. Pode haver problemas com a ordem de junção ao combinar
OUTER APPLY
eINNER JOIN
. Para obter a ordem de junção que eu queria, reescrevi-a com uma subconsulta:Não sei quantos dados você tem, por isso pode não ser importante para você. Mas o plano parece como eu quero:
Os resultados correspondem aos seus:
fonte
Aqui eu não uso tabela de calendário, mas uma tabela de números naturais nums.dbo.nums (espero que você também a tenha, se não, pode ser facilmente gerada)
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 especificou qual deve ser levada nessa situação.
fonte
Isso 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 outras pessoas terão consultas agradáveis, limpas e totalmente otimizadas para você).
fonte
Eu usei a tabela de dimensão de data de Aaron Bertrand, como você também mencionou na sua pergunta (que é uma tabela super útil para esses cenários) e escrevi o seguinte código:
Adicionei a
EndOfMonth
coluna à#dim
tabela (logo após aFirstOfMonth
coluna) usando o seguinte código:E a solução:
fonte
Triângulo JUNTE-SE à vitória!
Os resultados são:
Plano de execução interativo aqui.
Estatísticas de E / S e TIME (truncadas todos os valores zero após leituras lógicas):
Consulta para criar as tabelas temporárias 'necessárias e testar a instrução T-SQL que estou sugerindo:
fonte