Определите не покрытые дни на основе полей, содержащих даты «От» и «До» - PullRequest
2 голосов
/ 07 июня 2019

Сервер : Microsoft SQL Server

SQLFiddle : http://www.sqlfiddle.com/#!18/cdfa3/1/0

Если у меня есть строки, содержащие «начальную» дату и «конечную дату», как написать SQL-запрос, в котором будут перечислены дни, которые не содержатся между этими датами.

Пример (см. Ссылку на SQLFiddle выше для воспроизводимой демонстрации):

startdate                  enddate
2019-06-06 00:00:00.000    2019-06-08 00:00:00.000
2019-06-10 00:00:00.000    2019-06-11 00:00:00.000
2019-06-12 00:00:00.000    2019-06-13 00:00:00.000

У нас есть разрыв в покрытии 9 июня, потому что у нас есть покрытие с 6 июня по 8 июня, а затем с 10 июня по 13 июня.

Как можно идентифицировать дату 9 июня как не имеющую покрытия на основе строк с диапазонами дат?

Ответы [ 3 ]

2 голосов
/ 07 июня 2019

Вы можете использовать сгенерированную таблицу календаря и LEFT JOIN:

DECLARE @min DATE, @max DATE;
SELECT @min = MIN(workingdatestart), @max = MAX(workingdateend) FROM workingdates;

WITH cte AS (
  SELECT DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY 1/0), @min) AS d
  FROM sys.objects s, sys.objects s2
)
SELECT c.d AS gap
FROM cte c
LEFT JOIN workingdates w ON c.d BETWEEN w.workingdatestart and w.workingdateend
WHERE c.d < @max AND w.workingDateId IS NULL;

db <> fiddle demo

1 голос
/ 07 июня 2019

@ Лукаш Шозда украл мой гром.Мой ответ похож, но не использует переменные (я не предполагаю, хорошо это или плохо .. просто выкрикиваю).

Вы можете создать функцию таблицы календаря (см. Пример ниже), а затем выполнить LEFT ANTI SEMI JOIN для своей таблицы рабочих дней.Преимуществом этого решения является то, что календарная таблица генерирует 0 IO.

Решение:

WITH r(L,H) AS
(
  SELECT CAST(MIN(w.workingdatestart) AS DATE), CAST(MAX(w.workingdateend) AS DATE)
  FROM dbo.workingdates AS w
),
cal AS
(
  SELECT c.Dt
  FROM   r
  CROSS APPLY dbo.calendar(r.L,r.H) AS c
)
SELECT c.Dt
FROM   cal AS c
EXCEPT
SELECT c.Dt
FROM   cal AS c
JOIN   dbo.workingdates AS w 
  ON c.Dt BETWEEN w.workingdatestart AND w.workingdateend;

.. и функция:

CREATE FUNCTION dbo.calendar(@startdate DATE, @enddate DATE)
RETURNS TABLE WITH SCHEMABINDING AS RETURN
WITH E1(N) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS x(x)),
 iTally(N) AS 
(
  SELECT 0 UNION ALL
  SELECT TOP (DATEDIFF(DAY,@startDate,@endDate)) ROW_NUMBER() OVER (ORDER BY (SELECT 1)) 
  FROM E1 a, E1 b, E1 c
)
SELECT sortKey = i.N, Dt = DATEADD(DAY, i.N, @startDate)
FROM iTally AS i;
0 голосов
/ 07 июня 2019

Для этого вам нужна таблица, которая содержит все даты между минимальной и максимальной датами.Затем просто отфильтруйте строки, которые не существуют в workingdates, используя LEFT JOIN.

declare @minDate date = (select min([workingdatestart]) from [workingdates])
declare @maxDate date = (select max([workingdateend]) from [workingdates])
declare @Date date = @minDate
create table #rangeOfDates (dat date)

while @Date <= @maxDate
begin 
    insert into #rangeOfDates values (@Date)
    set @Date = dateadd(day , 1, @Date)
end

select r.dat
from #rangeOfDates as r
left join  workingdates as w
    on r.dat between w.workingdatestart and w.workingdateend
where w.workingdateID is null

Результат:

dat
2019-06-09
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...