Pergunta
Em MySQL, Se eu tenho uma lista de intervalos de datas (gama-start e gama-end). por exemplo.
10/06/1983 to 14/06/1983
15/07/1983 to 16/07/1983
18/07/1983 to 18/07/1983
E eu quero verificar se outro intervalo de data contém qualquer um dos intervalos já na lista, como eu faria isso?
por exemplo.
06/06/1983 to 18/06/1983 = IN LIST
10/06/1983 to 11/06/1983 = IN LIST
14/07/1983 to 14/07/1983 = NOT IN LIST
Solução
Este é um problema clássico, e é realmente mais fácil se você inverter a lógica.
Deixe-me dar um exemplo.
Vou postar um período de tempo aqui, e todas as diferentes variações de outros períodos que se sobrepõem de alguma forma.
|-------------------| compare to this one
|---------| contained within
|----------| contained within, equal start
|-----------| contained within, equal end
|-------------------| contained within, equal start+end
|------------| not fully contained, overlaps start
|---------------| not fully contained, overlaps end
|-------------------------| overlaps start, bigger
|-----------------------| overlaps end, bigger
|------------------------------| overlaps entire period
Por outro lado, deixe-me postar todos aqueles que não se sobrepõe:
|-------------------| compare to this one
|---| ends before
|---| starts after
Então, se você simples reduzir a comparação com:
starts after end
ends before start
então você vai encontrar todos aqueles que não se sobrepõe, e então você vai encontrar todos os períodos não correspondentes.
Para o seu final não no exemplo lista, você pode ver que ele corresponda essas duas regras.
Você terá de decidir wether períodos seguintes estão dentro ou fora seus intervalos:
|-------------|
|-------| equal end with start of comparison period
|-----| equal start with end of comparison period
Se a tabela tem colunas chamadas RANGE_END e RANGE_START, aqui estão algumas SQL simples para recuperar todas as linhas correspondentes:
SELECT *
FROM periods
WHERE NOT (range_start > @check_period_end
OR range_end < @check_period_start)
Observe o não lá. Desde as duas regras simples encontra todos os não correspondentes linhas, um simples não irá reverter dizer: , se não é uma das linhas não correspondentes, tem que ser um dos ones de harmonização .
A aplicação lógica de reversão simples aqui para se livrar do NÃO e você vai acabar com:
SELECT *
FROM periods
WHERE range_start <= @check_period_end
AND range_end >= @check_period_start
Outras dicas
Tendo sua gama exemplo de 06/06/1983 a 18/06/1983 e supondo que você tem colunas chamadas start e final para as suas faixas, você poderia usar um cláusula como esta ??p>
where ('1983-06-06' <= end) and ('1983-06-18' >= start)
i. verificar o início de sua faixa de teste é antes do final do intervalo de banco de dados, e que o fim de sua faixa de teste é depois ou no início do intervalo de banco de dados.
Se o seu RDBMS suporta a função de sobreposição (), então isso se torna trivial - sem necessidade de soluções caseiras. (No Oracle que aparentemente funciona, mas não é documentado).
Em seus resultados esperados você diz
06/06/1983 a 18/06/1983 = IN LISTA
No entanto, este período não contém nem é contido por qualquer um dos períodos em sua mesa (não lista!) De períodos. É, no entanto, se sobrepõem ao período de 1983/10/06 a 14/06/1983.
Você pode encontrar o livro Snodgrass ( http: //www.cs. arizona.edu/people/rts/tdbbook.pdf ) útil: datas pré-mySQL, mas o conceito de tempo não mudou; -)
Eu criei função para lidar com este problema no MySQL. Apenas converter as datas para segundos antes do uso.
DELIMITER ;;
CREATE FUNCTION overlap_interval(x INT,y INT,a INT,b INT)
RETURNS INTEGER DETERMINISTIC
BEGIN
DECLARE
overlap_amount INTEGER;
IF (((x <= a) AND (a < y)) OR ((x < b) AND (b <= y)) OR (a < x AND y < b)) THEN
IF (x < a) THEN
IF (y < b) THEN
SET overlap_amount = y - a;
ELSE
SET overlap_amount = b - a;
END IF;
ELSE
IF (y < b) THEN
SET overlap_amount = y - x;
ELSE
SET overlap_amount = b - x;
END IF;
END IF;
ELSE
SET overlap_amount = 0;
END IF;
RETURN overlap_amount;
END ;;
DELIMITER ;
Olhe para o exemplo a seguir. Será útil para você.
SELECT DISTINCT RelatedTo,CAST(NotificationContent as nvarchar(max)) as NotificationContent,
ID,
Url,
NotificationPrefix,
NotificationDate
FROM NotificationMaster as nfm
inner join NotificationSettingsSubscriptionLog as nfl on nfm.NotificationDate between nfl.LastSubscribedDate and isnull(nfl.LastUnSubscribedDate,GETDATE())
where ID not in(SELECT NotificationID from removednotificationsmaster where Userid=@userid) and nfl.UserId = @userid and nfl.RelatedSettingColumn = RelatedTo
Tente isto em MS SQL
WITH date_range (calc_date) AS (
SELECT DATEADD(DAY, DATEDIFF(DAY, 0, [ending date]) - DATEDIFF(DAY, [start date], [ending date]), 0)
UNION ALL SELECT DATEADD(DAY, 1, calc_date)
FROM date_range
WHERE DATEADD(DAY, 1, calc_date) <= [ending date])
SELECT P.[fieldstartdate], P.[fieldenddate]
FROM date_range R JOIN [yourBaseTable] P on Convert(date, R.calc_date) BETWEEN convert(date, P.[fieldstartdate]) and convert(date, P.[fieldenddate])
GROUP BY P.[fieldstartdate], P.[fieldenddate];
CREATE FUNCTION overlap_date(s DATE, e DATE, a DATE, b DATE)
RETURNS BOOLEAN DETERMINISTIC
RETURN s BETWEEN a AND b or e BETWEEN a and b or a BETWEEN s and e;
Outro método usando ENTRE instrução SQL
Períodos incluído:
SELECT *
FROM periods
WHERE @check_period_start BETWEEN range_start AND range_end
AND @check_period_end BETWEEN range_start AND range_end
Períodos excluídos:
SELECT *
FROM periods
WHERE (@check_period_start NOT BETWEEN range_start AND range_end
OR @check_period_end NOT BETWEEN range_start AND range_end)
SELECT *
FROM tabla a
WHERE ( @Fini <= a.dFechaFin AND @Ffin >= a.dFechaIni )
AND ( (@Fini >= a.dFechaIni AND @Ffin <= a.dFechaFin) OR (@Fini >= a.dFechaIni AND @Ffin >= a.dFechaFin) OR (a.dFechaIni>=@Fini AND a.dFechaFin <=@Ffin) OR
(a.dFechaIni>=@Fini AND a.dFechaFin >=@Ffin) )