Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

SQL:trova se le date delle colonne includono almeno in parte un intervallo di date

Penso che questo possa essere gestito come un lacune e isole problema. Considera i seguenti dati di input:(come i dati di esempio di OP più due righe aggiuntive)

id  company_id  status_id   effective_date
-------------------------------------------
1   10          1           2016-12-15
2   10          1           2016-12-30 
3   10          5           2017-02-04
4   10          4           2017-02-08
5   11          5           2017-06-05
6   11          1           2018-04-30

Puoi utilizzare la seguente query:

SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
FROM company_status_history AS t
OUTER APPLY 
(
   SELECT COUNT(*) AS cnt
   FROM company_status_history AS c
   WHERE c.status_id = 1 
         AND c.company_id  = t.company_id 
         AND c.effective_date < t.effective_date
) AS x
ORDER BY company_id, effective_date

per ottenere:

id  company_id  status_id   effective_date  grp
-----------------------------------------------
1   10          1           2016-12-15      0
2   10          1           2016-12-30      1
3   10          5           2017-02-04      2
4   10          4           2017-02-08      2
5   11          5           2017-06-05      0
6   11          1           2018-04-30      0

Ora puoi identificare status = 1 isole utilizzando:

;WITH CTE AS 
(
    SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
    FROM company_status_history AS t
    OUTER APPLY 
    (
       SELECT COUNT(*) AS cnt
       FROM company_status_history AS c
       WHERE c.status_id = 1 
             AND c.company_id  = t.company_id 
             AND c.effective_date < t.effective_date
    ) AS x
)
SELECT id, company_id, status_id, effective_date,
       ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY effective_date) - 
       cnt AS grp
FROM CTE 

Risultato:

id  company_id  status_id   effective_date  grp
-----------------------------------------------
1   10          1           2016-12-15      1
2   10          1           2016-12-30      1
3   10          5           2017-02-04      1
4   10          4           2017-02-08      2
5   11          5           2017-06-05      1
6   11          1           2018-04-30      2

Campo calcolato grp ci aiuterà a identificare quelle isole:

;WITH CTE AS 
(
    SELECT t.id, t.company_id, t.status_id, t.effective_date, x.cnt
    FROM company_status_history AS t
    OUTER APPLY 
    (
       SELECT COUNT(*) AS cnt
       FROM company_status_history AS c
       WHERE c.status_id = 1 
             AND c.company_id  = t.company_id 
             AND c.effective_date < t.effective_date
    ) AS x
), CTE2 AS 
(
   SELECT id, company_id, status_id, effective_date,
          ROW_NUMBER() OVER (PARTITION BY company_id ORDER BY effective_date) - 
          cnt AS grp
   FROM CTE
)
SELECT company_id, 
       MIN(effective_date) AS start_date, 
       CASE 
          WHEN COUNT(*) > 1 THEN DATEADD(DAY, -1, MAX(effective_date))
          ELSE MIN(effective_date)
       END AS end_date
FROM CTE2 
GROUP BY company_id, grp
HAVING COUNT(CASE WHEN status_id = 1 THEN 1 END) > 0

Risultato:

company_id  start_date  end_date
-----------------------------------
10          2016-12-15  2017-02-03 
11          2018-04-30  2018-04-30 

Tutto quello che vuoi sapere sono quei record dall'alto che si sovrappongono all'intervallo specificato.

Dimostrazione qui con un caso d'uso un po' più complicato.