У меня есть роботы с сертификатами. Есть два вида сертификатов. Для каждого типа сертификата (обозначенного Certif_ID
) для каждого робота мне нужен самый последний сертифицированный интервал дат.
Обновление для ясности: интервалы дат, которые не перекрываются, но являются смежными, рассматриваются как один интервал. См. Первые две записи в таблице-образце вверху кода.
Интервалы дат могут перекрываться! Их следует рассматривать как один диапазон. Вот где у меня проблема.
в SQL Server 2012 запустите этот код как есть, чтобы узнать, что происходит.
BEGIN -- #certif_span
IF OBJECT_ID('TEMPDB..#certif_span') IS NOT NULL DROP TABLE #certif_span;
CREATE TABLE #certif_span
( Robot_ID CHAR(3)
, Certif_ID SMALLINT
, d_Start SMALLDATETIME
, d_End SMALLDATETIME );
INSERT INTO #certif_span VALUES ('210', '1', '2000-01-01', '2001-02-02');
INSERT INTO #certif_span VALUES ('210', '1', '2001-02-03', '2001-12-31');
INSERT INTO #certif_span VALUES ('210', '1', '2000-01-01', '2000-12-31');
INSERT INTO #certif_span VALUES ('880', '1', '2001-01-01', '2001-12-31');
INSERT INTO #certif_span VALUES ('880', '1', '2002-02-02', '2003-02-01');
INSERT INTO #certif_span VALUES ('880', '1', '2003-01-01', '2004-12-31'); -- *
INSERT INTO #certif_span VALUES ('880', '7', '2010-05-05', '2011-05-04');
INSERT INTO #certif_span VALUES ('880', '7', '2011-05-05', '2012-02-10');
INSERT INTO #certif_span VALUES ('880', '7', '2013-03-03', '2013-04-04');
INSERT INTO #certif_span VALUES ('880', '7', '2013-04-01', '2013-05-05'); -- *
-- * This line has dates that overlap with the line above
END
SELECT Robot_ID
, Certif_ID
, d_Start = FORMAT(d_Start, 'yyyy-MM-dd')
, d_End = FORMAT(d_End, 'yyyy-MM-dd')
, commentary = 'Here is the raw data'
FROM #certif_span AS cs
ORDER BY Robot_ID
, Certif_ID
, d_End
IF OBJECT_ID('TEMPDB..#prac_date_span') IS NOT NULL DROP TABLE #prac_date_span;
SELECT DISTINCT
cs.Robot_ID
, cs.Certif_ID
, cs.d_Start
, cs.d_End
INTO
--DROP TABLE --SELECT * FROM
#prac_date_span
FROM
#certif_span AS cs
GROUP BY
cs.Robot_ID
, cs.Certif_ID
, cs.d_Start
, cs.d_End
ORDER BY 1, 2, 3;
BEGIN
IF OBJECT_ID('TEMPDB..#prac_date_span_grp') IS NOT NULL
DROP TABLE #prac_date_span_grp;
WITH cte as (
SELECT
a.Robot_ID, a.Certif_ID
, a.d_Start, a.d_End
FROM
#prac_date_span a
LEFT JOIN #prac_date_span b
ON a.Robot_ID = b.Robot_ID
AND b.Certif_ID = a.Certif_ID
AND a.d_Start - 1 = b.d_End
WHERE
b.Robot_ID IS NULL
UNION ALL -----------------------------
SELECT
a.Robot_ID, a.Certif_ID
, a.d_Start, b.d_End
FROM
cte a
JOIN
#prac_date_span b
ON a.Robot_ID = b.Robot_ID
AND b.Certif_ID = a.Certif_ID
AND b.d_Start - 1 = a.d_End
)
SELECT
Robot_ID
, Certif_ID
, d_Start
, d_End = MAX(d_End)
INTO
--drop table --select * from
#prac_date_span_grp
FROM cte
GROUP BY Robot_ID, Certif_ID, d_Start
ORDER BY Robot_ID, Certif_ID;
END
SELECT
Robot_ID
, Certif_ID
, d_Start = FORMAT(d_Start, 'yyyy-MM-dd')
, d_End = FORMAT(d_End, 'yyyy-MM-dd')
, commentary = 'Here is the grouped data (flawed)'
FROM #prac_date_span_grp
SELECT
Robot_ID
, Certif_ID
, d_Start = FORMAT(MAX(d_Start), 'yyyy-MM-dd')
, d_End = FORMAT(MAX(d_End), 'yyyy-MM-dd')
, commentary = 'Final result: Start date ' +
CASE FORMAT(MAX(d_Start), 'yyyy-MM-dd')
WHEN '2003-01-01' THEN 'should be 2002-02-02'
WHEN '2013-04-01' THEN 'should be 2013-03-03'
ELSE 'good' END
FROM #prac_date_span_grp
GROUP BY Robot_ID, Certif_ID
Конечный результат должен быть:
Robot_ID Certif_ID d_Start d_End
210 1 2000-01-01 2001-12-31
880 1 2002-02-02 2004-12-31
880 7 2013-03-03 2013-05-05
Я возился со сравнением дат. В этом бите из cte
-1
выглядит так, как будто он допускает однодневный сдвиг в датах:
AND b.Certif_ID = a.Certif_ID
AND a.d_Start - 1 = b.d_End
...
AND b.Certif_ID = a.Certif_ID
AND b.d_Start - 1 = a.d_End
Я уверен, что это вопрос, который нужно исправить. Я пробовал изменить дату по сравнению с >=
. (Это требует, чтобы я имел дело с максимальной рекурсией.) Группировка меняется, но неверна.