Сравнение дат, чтобы найти недостающие даты

Я пытаюсь сравнить даты, чтобы определить, когда кто-то больше не имеет права. В этом примере они не имеют права участвовать в период с 1.10.23 по 19.10.23. Таким образом, от 30.09.23 exp_date до eff_date от 20.10.23. Как мне найти этот диапазон? Спасибо.

Сравнение дат, чтобы найти недостающие даты

CREATE TABLE ADMITS
(
ID_NUM INT
,AUTH_EFF_DATE  date null
,AUTH_EXP_DATE  date null
,MEM_EFF_DATE   date null
,MEM_EXP_DATE date null

)

INSERT INTO ADMITS (ID_NUM,AUTH_EFF_DATE,AUTH_EXP_DATE,MEM_EFF_DATE,MEM_EXP_DATE)
VALUES
 (118206307, '1/2/2023', '6/7/2023', '4/22/2022', '9/30/2023')
,(118206307, '8/30/2023', '2/17/2024', '4/22/2022', '9/30/2023')
,(118206307, '1/2/2023', '6/7/2023', '10/20/2023', '12/31/9999')
,(118206307, '8/30/2023', '2/17/2024', '10/20/2023', '12/31/9999')
;

Исходя из этого, я хотел бы сравнить этот диапазон дат с auth_eff_date и auth_exp_date, чтобы показать, в каких диапазонах аутентификации отсутствуют права. Я думал, может быть, опережаю или отстаю, но не был уверен, как и правильно ли это.

каков желаемый результат?

nbk 24.04.2024 19:54

@nbk... я бы хотел показать id_num, 30.08.23, 17.02.24, затем какие недостающие даты, 1.10.23 и 19.10.23.

user24472185 24.04.2024 20:06

Ваши данные уже представляют собой перекрестное соединение дат AUTH и дат MEM. Я предлагаю вам публиковать исходные данные до того, как они будут обработаны с помощью (возможно, плохо построенного) запроса.

T N 24.04.2024 20:13

это может помочь stackoverflow.com/questions/143552/comparing-date-ranges

Bart McEndree 24.04.2024 21:34

Было предложено множество решений. Приведите больше примеров, чтобы мы могли проверить решения, выявить крайние случаи и оценить производительность.

Bart McEndree 24.04.2024 22:15

В соответствии с руководством по вопросам не публикуйте изображения кода, данных, сообщений об ошибках и т. д. — скопируйте или введите текст в вопрос. Пожалуйста, ограничьте использование изображений для диаграмм или демонстрации ошибок рендеринга, вещей, которые невозможно точно описать с помощью текста.

Dale K 24.04.2024 22:30
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
6
81
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Первый шаг — использовать LAG, как вы предложили, чтобы определить, когда кто-то больше не имеет права, и диапазон авторизации. Затем мы можем применить стратегию сравнения диапазонов, предложенную Сравнение диапазонов дат

WITH Previous as
(  
 SELECT *,
   LAG(MEM_EXP_DATE) OVER(PARTITION BY ID_NUM ORDER BY MEM_EFF_DATE,AUTH_EFF_DATE)  as PrevExprDate,
   LAG(AUTH_EFF_DATE) OVER(PARTITION BY ID_NUM ORDER BY MEM_EFF_DATE,AUTH_EFF_DATE)  as PrevAUTH_EFF_DATE,
   LAG(AUTH_EXP_DATE) OVER(PARTITION BY ID_NUM ORDER BY MEM_EFF_DATE,AUTH_EFF_DATE)  as PrevAUTH_EXP_DATE
 FROM ADMITS
),
Eligible as
(  
SELECT *,
    CASE 
     WHEN PrevExprDate IS NULL THEN NULL 
     WHEN PrevExprDate = '12/31/9999' THEN '12/31/9999'
     ELSE DateAdd(day, 1, PrevExprDate) END as StartNotEligible,
    CASE 
     WHEN MEM_EFF_DATE IS NULL THEN NULL 
     WHEN MEM_EFF_DATE = '12/31/9999' THEN '12/31/9999'
     ELSE DateAdd(day, -1, MEM_EFF_DATE) END as EndNotEligible
FROM Previous
)
Select ID_NUM, PrevAUTH_EFF_DATE, PrevAUTH_EXP_DATE, StartNotEligible,EndNotEligible 
FROM Eligible 
WHERE (StartNotEligible <= PrevAUTH_EXP_DATE      AND EndNotEligible >= PrevAUTH_EFF_DATE)

рабочий пример

Средний:

ID_NUM AUTH_EFF_DATE AUTH_EXP_DATE MEM_EFF_DATE MEM_EXP_DATE Превэкспрдате StartNotEligible EndNotEligible 118206307 2023-01-02 07.06.2023 2022-04-22 2023-09-30 нулевой нулевой 21 апреля 2022 г. 118206307 2023-08-30 2024-02-17 2022-04-22 2023-09-30 2023-09-30 01.10.2023 21 апреля 2022 г. 118206307 2023-01-02 07.06.2023 20.10.2023 9999-12-31 2023-09-30 01.10.2023 2023-10-19 118206307 2023-08-30 2024-02-17 20.10.2023 9999-12-31 9999-12-31 9999-12-31 2023-10-19

Финал:

ID_NUM ПредыдущаяAUTH_EFF_DATE ПредыдущаяAUTH_EXP_DATE StartNotEligible EndNotEligible 118206307 2023-08-30 2024-02-17 01.10.2023 2023-10-19

Первым шагом является разделение и дедупликация данных авторизации и соответствия требованиям. Предоставленные исходные опубликованные данные представляют собой перекрестное применение этих двух данных, возможно, из более раннего запроса.

Второй шаг — рассчитать исключительные даты окончания, добавив 1 день к включенным датам окончания, указанным в данных. Вычисления диапазона дат и перекрытия работают лучше, если сочетать включающие даты начала и исключительные даты окончания, которые образуют полуоткрытые интервалы. Комбинация NULLIF() и ISNULL() используется для сохранения особых значений конца времени 9999-12-31.

В приведенном ниже коде я использую термин ThruDate для обозначения включенной даты окончания (только для отображения) и ToDate для обозначения исключительной даты окончания (используется логикой).

Далее нам нужно определить все отдельные диапазоны дат для тестирования. Это можно сделать, выделив все отдельные даты начала и окончания, отсортировав их, а затем создав новые диапазоны дат для каждого интервала. Функция окна LAG() используется для объединения предыдущей даты с текущими данными для определения каждого диапазона.

Теперь мы можем проверить каждый интервал дат на предмет совпадения с диапазонами дат авторизации и правомочности. Стандартный тест на перекрытие диапазонов дат — Start1 < End2 AND Start2 < End1. Если наш интервал выборки перекрывает строку авторизации, но не перекрывает строку приемлемости, мы определили разрешенный, но недопустимый интервал, который следует включить в результаты.

Все расчеты производятся независимо для каждого значения идентификатора.

Итоговая логика будет примерно такой:

-- Date range operations work much better when using "exclusive" end dates,
-- so we will calculate and use exclusive end dates for all range and overlap
-- logic, and only use the inclusive end dates for display.
--
-- To see the intermediate results:
--    Change the select to SELECT * and
--    Uncomment the WHERE clause.
WITH AuthDates AS (
    SELECT DISTINCT -- Distinct is used here to dedup the original unnormalized data
        ID_NUM AS Id,
        AUTH_EFF_DATE AS AuthFromDate,
        AUTH_EXP_DATE AS AuthThruDate, -- Inclusive
        ISNULL(
            DATEADD(day, 1, NULLIF(AUTH_EXP_DATE, '9999-12-31')),
            '9999-12-31') AS AuthToDate -- Exclusive
    FROM ADMITS
),
EligibilityDates AS ( -- Distinct is used here to dedup the original unnormalized data
    SELECT DISTINCT
        ID_NUM AS Id,
        MEM_EFF_DATE AS EligFromDate,
        MEM_EFF_DATE AS EligThruDate, -- Inclusive
        ISNULL(
            DATEADD(day, 1, NULLIF(MEM_EXP_DATE, '9999-12-31')),
            '9999-12-31') AS EligToDate -- Exclusive
    FROM ADMITS
),
DistinctDates AS (
    SELECT DISTINCT U.Id, U.Date
    FROM (
        SELECT A.Id, V.Date
        FROM AuthDates A
        CROSS APPLY (VALUES (AuthFromDate), (AuthToDate)) V(Date)
        UNION ALL  -- (Yes, I could drop the ALL, but I prefer an explicit DISTINCT)
        SELECT E.Id, V.Date
        FROM EligibilityDates E
        CROSS APPLY (VALUES (EligFromDate), (EligToDate)) V(Date)
    ) U
),
DateRanges AS (
    SELECT
        Id,
        LAG(Date) OVER(PARTITION BY Id ORDER BY Date)  AS FromDate,
        ISNULL(
            DATEADD(day, -1, NULLIF(Date, '9999-12-31')),
            '9999-12-31') AS ThruDate,
        Date AS ToDate
    FROM DistinctDates
)
SELECT
    A.Id,
    A.AuthFromDate, A.AuthThruDate,
    D.FromDate AS IneligibleFromDate, D.ThruDate AS IneligibleThruDate
FROM DateRanges D
LEFT JOIN AuthDates A
    ON A.Id = D.Id
    AND A.AuthFromDate < D.ToDate
    AND A.AuthToDate > D.FromDate
LEFT JOIN EligibilityDates E
    ON E.Id = D.Id
    AND E.EligFromDate < D.ToDate
    AND E.EligToDate > D.FromDate
WHERE A.ID IS NOT NULL  -- Authorized
AND E.Id IS NULL        -- But not eligible
ORDER BY
    D.Id,
    D.FromDate

(Текущие условия WHERE можно устранить, изменив первое LEFT JOIN на INNER JOIN и изменив второе LEFT JOIN на подвыборку WHERE NOT EXISTS(...), но я оставил их как внешние соединения, чтобы можно было легко просмотреть промежуточные результаты.)

Промежуточные результаты (с использованием исключительных дат окончания):

Идентификатор С даты На сегодняшний день Аутфромдате Ауттодате Элигфромдате ЭлигТодате 118206307 нулевой 2022-04-22 нулевой нулевой нулевой нулевой 118206307 2022-04-22 2023-01-02 нулевой нулевой 2022-04-22 01.10.2023 118206307 2023-01-02 08.06.2023 2023-01-02 08.06.2023 2022-04-22 01.10.2023 118206307 08.06.2023 2023-08-30 нулевой нулевой 2022-04-22 01.10.2023 118206307 2023-08-30 01.10.2023 2023-08-30 18 февраля 2024 г. 2022-04-22 01.10.2023 118206307 01.10.2023 20.10.2023 2023-08-30 18 февраля 2024 г. нулевой нулевой 118206307 20.10.2023 18 февраля 2024 г. 2023-08-30 18 февраля 2024 г. 20.10.2023 9999-12-31 118206307 18 февраля 2024 г. 9999-12-31 нулевой нулевой 20.10.2023 9999-12-31

Окончательные результаты:

Идентификатор Аутфромдате Авторизация через дату НеправомерноFromDate Недопустимо через дату 118206307 2023-08-30 2024-02-17 01.10.2023 2023-10-19

См. эту db<>fiddle для демонстрации, которая включает дополнительные тестовые данные, охватывающие несколько перекрывающихся сценариев авторизации и соответствия критериям.

Другие вопросы по теме