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
.
Je présente ma solution pour T-SQL.
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;
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