Eu tenho duas mesas. Cada um contém alguns atributos para uma entidade comercial e o intervalo de datas para o qual esses atributos eram válidos. Eu quero combinar essas tabelas em uma, combinando linhas na chave de negócios comum e dividindo os intervalos de tempo.
O exemplo do mundo real são duas tabelas temporais de origem que alimentam uma tabela de dimensão tipo 2 no data warehouse.
A entidade pode estar presente em nenhum, um ou ambos os sistemas de origem em qualquer momento. Uma vez que uma entidade é registrada em um sistema de origem, os intervalos são bem comportados - sem lacunas, duplicatas ou outros negócios de macaco. A associação às fontes pode terminar em datas diferentes.
As regras de negócios afirmam que queremos apenas retornar intervalos em que a entidade esteja presente em ambas as fontes simultaneamente.
Qual consulta dará esse resultado?
Isso ilustra a situação:
Month J F M A M J J
Source A: <--><----------><----------><---->
Source B: <----><----><----------------><-->
Result: <----><----><----><---->
Dados de amostra
Para simplificar, usei intervalos de datas fechados; provavelmente qualquer solução poderia ser estendida para intervalos semi-abertos com um pouco de digitação.
drop table if exists dbo.SourceA;
drop table if exists dbo.SourceB;
go
create table dbo.SourceA
(
BusinessKey int,
StartDate date,
EndDate date,
Attribute char(9)
);
create table dbo.SourceB
(
BusinessKey int,
StartDate date,
EndDate date,
Attribute char(9)
);
GO
insert dbo.SourceA(BusinessKey, StartDate, EndDate, Attribute)
values
(1, '19990101', '19990113', 'black'),
(1, '19990114', '19990313', 'red'),
(1, '19990314', '19990513', 'blue'),
(1, '19990514', '19990613', 'green'),
(2, '20110714', '20110913', 'pink'),
(2, '20110914', '20111113', 'white'),
(2, '20111114', '20111213', 'gray');
insert dbo.SourceB(BusinessKey, StartDate, EndDate, Attribute)
values
(1, '19990214', '19990313', 'left'),
(1, '19990314', '19990413', 'right'),
(1, '19990414', '19990713', 'centre'),
(1, '19990714', '19990730', 'back'),
(2, '20110814', '20110913', 'top'),
(2, '20110914', '20111013', 'middle'),
(2, '20111014', '20120113', 'bottom');
Saída desejada
BusinessKey StartDate EndDate a_Colour b_Placement
----------- ---------- ---------- --------- -----------
1 1999-02-14 1999-03-13 red left
1 1999-03-14 1999-04-13 blue right
1 1999-04-14 1999-05-13 blue centre
1 1999-05-14 1999-06-13 green centre
2 2011-08-14 2011-09-13 pink top
2 2011-09-14 2011-10-13 white middle
2 2011-10-14 2011-11-13 white bottom
2 2011-11-14 2011-12-13 gray bottom
Posso ter entendido mal sua pergunta, mas os resultados parecem estar de acordo com sua pergunta:
Como os intervalos precisam se sobrepor, a maior parte do trabalho pode ser feita com uma junção com isso como predicado. Depois é só escolher a intersecção dos intervalos.
LEAST e GREATEST parecem estar faltando como funções, então usei uma expressão case.
violino
Esta solução desconstrói os intervalos de origem para apenas suas datas de início. Ao combinar essas duas listas, obtém-se um conjunto de datas de início do intervalo de saída. A partir destes, as datas finais de saída correspondentes são calculadas por uma função de janela. Como o intervalo de saída final deve terminar quando um dos dois intervalos de entrada terminar, há um processamento especial para determinar esse valor.
O CTE "Datas" usa UNION em vez de UNION ALL para eliminar duplicatas. Se ambas as fontes mudarem na mesma data, queremos apenas uma linha de saída correspondente.
Como queremos fechar a saída quando qualquer fonte fecha a terceira consulta em "Datas", adiciona a data de término mais antiga, ou seja, o MIN do MAX de EndDates. Como é um EndDate disfarçado de StartDate, deve ter outro dia adicionado a ele. Sua finalidade é permitir que a função de janela calcule o final do intervalo anterior. Será eliminado no predicado final.
O uso de associações internas para a consulta final elimina os intervalos de origem para os quais não há valor correspondente na outra origem.
Existem muitas soluções interessantes para este problema (declaradas em termos diferentes) aqui e nas páginas anteriores. Lá, ele é apresentado como oferta e demanda correspondentes em um leilão. As unidades fornecidas/demandas são diretamente análogas aos dias em um intervalo desta questão, então a solução se traduz. Eu deixei nos termos usados no site vinculado, no entanto.
Dados de amostra.
As soluções expostas reduzem o tempo decorrido para seus dados de amostra de 400k linhas de ingênuos 11 segundos para 0,4s. O mais rápido é de Paul White (desta paróquia), mostrado aqui.