web-dev-qa-db-fra.com

Sélectionnez toutes les gammes de chevauchement de la plage de démarrage

Ma question est similaire à - celle-ci avec (je pense) une différence suffisante significative. J'ai une plage de base et je veux trouver toutes les autres gammes dans une table qui en conflit avec elle et l'autre. Ou plutôt les articles qui forment les gammes, mais cela ne fait pas vraiment de différence.

La ligne avec un astérisque est la plage de départ. Les plages 1,2 et 3 sont celles qui sont censées l'étendre. La gamme de résultats devrait être le X.

1 | 
3 |    ===1====            ====
5 | ==2==    ====*====           ====
6 |             ====3====     =====          
--+-------------------------------------
  | |<--------X-------->|

J'ai écrit ceci:

WITH   cte
AS (
    SELECT DATA1.ID, DATA1.STARTDATE, DATA1.ENDDATE
    FROM DATA1
    WHERE 
    DATA1.ID = @PARID AND 
    DATA1.STARTDATE > @STARTDATE AND 
    DATA1.ENDDATE < @ENDDATE 

    UNION ALL

    SELECT DATA1.ID, DATA1.STARTDATE, DATA1.ENDDATE
    FROM cte 
    INNER JOIN DATA1 ON (DATA1.ID = cte.ID)
    WHERE 
    DATA1.ID = @PARID AND 
    (cte.STARTDATE < DATA1.ENDDATE AND cte.ENDDATE > DATA1.ENDDATE)
)
SELECT *
FROM cte

Mais après que certains fidèles réalisaient que cela ne puisse pas vraiment fonctionner, car le 2e Select ne sait pas ce que STARTDATE et ENDDATE il doit utiliser à partir du cte. Et je ne peux pas utiliser les sous-requêtes dans SQL récursif.

Nous avons précédemment calculé cela dans le client dans .NET via un ensemble de fonctions récursives, mais à O (n ^ 2), il était stupidement lent. L'intention ici est de déplacer les calculs sur le serveur et d'optimiser le calcul.

C'est ce que j'essaie de faire ici du tout viable?


SQL FIDDLE . J'ai des problèmes pour que la requête soit exécutée comme sur ma machine (où j'ai utilisé une structure de données moins simple), mais au moins j'ai ajouté des données d'exemple. Je vais continuer à essayer de le faire produire le même résultat que j'ai eu sur ma machine.

Avec une plage d'entrée de 2018-08-24 12:00:00 à 2018-08-31 12:00:00, la sortie correcte doit être IDS 2, 3, 4, 6.

5
rancor1223

Je présente ma solution pour T-SQL.

Fiddle SQL Server 2017

Le problème:

Recherchez tous les enregistrements d'une table d'entrée connectée à temps dont au moins 1 élément chevauche avec une période demandée.

la solution:

Étape 1: Déterminez les gammes de date pour lesquelles tous les éléments sont connectés à temps (en se chevauchant ou à la rencontre).

with packed as (
    select  min(StartDate) StartDate, max(EndDate) EndDate from (
        select StartDate, EndDate, sum(GroupStart) over (order by StartDate) Grp from (
            select StartDate, EndDate
                , case when max(EndDate) 
                    over (order by EndDate, StartDate rows between unbounded preceding and 1 preceding)
                        > StartDate
                    then 0 else 1 end GroupStart
            from dbo.DATA1
        ) p0
    ) p1
    group by Grp
)

Étape 2: Déterminez la plage de dates correspondante accordée à votre période demandée

relevant as (
        select * from packed
        where StartDate < @ENDDATE
        and @STARTDATE < EndDate
)

Étape 3: Sortiez tous les éléments connectés dans la plage de date correspondante

select d.* from dbo.DATA1 d
inner join relevant r on d.StartDate < r.EndDate and r.StartDate < d.EndDate
where d.StartDate < @ENDDATE and @STARTDATE < d.EndDate
order by d.StartDate, d.EndDate, d.Id;

Installer:

CREATE TABLE dbo.DATA1
(
  ID integer PRIMARY KEY,
  STARTDATE datetime NOT NULL,
  ENDDATE datetime NOT NULL,
);

CREATE UNIQUE INDEX i1 ON dbo.DATA1 (STARTDATE DESC, ID ASC) INCLUDE (ENDDATE);
CREATE UNIQUE INDEX i2 ON dbo.DATA1 (ENDDATE ASC, ID DESC) INCLUDE (STARTDATE);

INSERT INTO DATA1 (ID, STARTDATE, ENDDATE) VALUES 
  (1, '2018-08-17 12:00:00', '2018-08-20 12:00:00'),
  (2, '2018-08-22 12:00:00', '2018-08-25 12:00:00'),
  (3, '2018-08-24 12:00:00', '2018-08-29 12:00:00'),
  (4, '2018-08-26 12:00:00', '2018-09-02 12:00:00'),
  (5, '2018-09-05 12:00:00', '2018-09-25 12:00:00'),
  (6, '2018-08-23 12:00:00', '2018-08-30 12:00:00'),
  (7, '2018-08-17 12:00:00', '2018-08-20 12:00:00'),
  (8, '2018-08-22 12:00:00', '2018-08-25 12:00:00'),
  (9, '2018-08-24 12:00:00', '2018-08-29 12:00:00'),
  (10, '2018-08-26 12:00:00', '2018-09-02 12:00:00'),
  (11, '2018-09-05 12:00:00', '2018-09-25 12:00:00'),
  (12, '2018-08-23 12:00:00', '2018-08-30 12:00:00');

Querre complète:

DECLARE @STARTDATE DATETIME = '2018-08-24 12:00:00';
DECLARE @ENDDATE DATETIME = '2018-08-31 12:00:00';

with packed as (
    select  min(StartDate) StartDate, max(EndDate) EndDate from (
        select StartDate, EndDate, sum(GroupStart) over (order by StartDate) Grp from (
            select StartDate, EndDate
                , case when max(EndDate) 
                    over (order by EndDate, StartDate rows between unbounded preceding and 1 preceding)
                        > StartDate
                    then 0 else 1 end GroupStart
            from dbo.DATA1
        ) p0
    ) p1
    group by Grp
)
, relevant as (
        select * from packed
        where StartDate < @ENDDATE
        and @STARTDATE < EndDate
)
select d.* from dbo.DATA1 d
inner join relevant r on d.StartDate < r.EndDate and r.StartDate < d.EndDate
where d.StartDate < @ENDDATE and @STARTDATE < d.EndDate
order by d.StartDate, d.EndDate, d.Id;
0
user179304

En attente avec impatience un SQL Fiddle pour permettre les tests, veuillez consulter - http://sqlfiddle.com/#!18/10AB4/5

DECLARE @STARTDATE DATETIME = '2018-08-24 12:00:00';
DECLARE @ENDDATE DATETIME = '2018-08-29 12:00:00';
declare @PARID int = 3;

with ghp as (
  SELECT DATA1.ID, DATA1.STARTDATE, DATA1.ENDDATE
    from DATA1
    where ID = @PARID ),
     ghp2 as (
 select 'A' class, min(data1.startdate) minstart, max(data1.enddate) maxend
    FROM DATA1, ghp
    WHERE data1.enddate >= ghp.startdate
      and data1.startdate <= ghp.enddate
 union all
 select 'B', data1.startdate, data1.enddate
    from data1, ghp2
    WHERE (    data1.enddate   >= ghp2.minstart
           and data1.startdate <  ghp2.minstart )),
     ghp3 as (
 select 'C' class, min(data1.startdate) minstart, max(data1.enddate) maxend
    FROM DATA1, ghp
    WHERE data1.enddate >= ghp.startdate
      and data1.startdate <= ghp.enddate
 union all
 select 'D', data1.startdate, data1.enddate
    from data1, ghp2
    WHERE (    data1.enddate   > ghp2.maxend
           and data1.startdate <=  ghp2.maxend )
 )
select * from ghp2
union all
select * from ghp3
0
Gerard H. Pille