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
Foi útil?

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

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) )
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top