Запрос для активных записей между диапазоном дат или самым последним перед диапазоном дат

#sql #sql-server

#sql #sql-сервер

Вопрос:

Мне нужно найти активные записи, которые находятся между диапазоном параметров даты из таблицы, содержащей приложения. Сначала я ищу запись между диапазоном дат в таблице с именем ‘app_notes’ и проверяю, связана ли она с приложением. Если в диапазоне дат нет записи app_note, я должен просмотреть самую последнюю заметку приложения из диапазона дат. Если в этом примечании к приложению указан статус активный, я выбираю его.

Таблица app_indiv соединяет пользователя с приложением. Для каждого отдельного пользователя может быть несколько записей app_indiv и несколько заметок app_notes для каждого app_indiv. Вот что у меня есть на данный момент:

 SELECT DISTINCT individual.indiv_id
    FROM individual INNER JOIN 
    app_indiv ON app_indiv.indiv_id = individual.indiv_id INNER JOIN
    app_note ON app_indiv.app_indiv_id = app_note.app_indiv_id
    WHERE (app_note.when_mod BETWEEN @date_from AND @date_to)
    /* OR most recent app_note indicates active */
  

Как я могу получить самую последнюю запись app_note, если в диапазоне дат ее нет? Поскольку возможно несколько записей app_note, я не знаю, как заставить его извлекать только самые последние.

Комментарии:

1. Это довольно странное требование. Я бы сказал, просто возьмите максимальную дату и найдите самую последнюю активную запись до этого.

2. Не могли бы вы опубликовать схему таблицы для таблицы app_note и сообщить мне, есть ли app_note_id (или эквивалент), который будет соответствовать порядку самого последнего app_note? Кроме того, пожалуйста, дайте мне знать, означает ли требование «самый последний» диапазон до даты или максимально доступный

Ответ №1:

 SELECT * 
FROM individual i
INNER JOIN app_indiv ai
    ON ai.indiv_id = i.indiv_id
OUTER APPLY
(
    SELECT TOP 1 * FROM app_note an
    WHERE an.app_indiv_id = ai.app_indiv_id
        AND an.when_mod < @date_to
    ORDER BY an.when_mod DESC
) d
WHERE d.status = 'active'
  

Найдите последнюю заметку меньше даты окончания, проверьте, активна ли она, и если да, покажите отдельную запись.

Ответ №2:

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

 SELECT DISTINCT individual.indiv_id
FROM individual INNER JOIN 
app_indiv ON app_indiv.indiv_id = individual.indiv_id INNER JOIN
app_note ON app_indiv.app_indiv_id = app_note.app_indiv_id
WHERE (CASE WHEN app_note.when_mod BETWEEN @date_from AND @date_to 
                THEN (SELECT appnote.when_mod from individual where appnote.when_mod BETWEEN @date_from AND @date_to)
            WHEN app_note.when_mod NOT BETWEEN @date_from and @date_to 
                THEN (SELECT appnote.when_mod from individual appnote.when_mod LIMIT 1))
  

Запрос может быть неправильным. Возможно, потребуется включить переключатель в первую часть запроса SELECT.

Ответ №3:

Мне кажется, что вас действительно волнует только дата окончания вашего диапазона дат, поскольку вы хотите иметь возможность заглянуть дальше назад, если в этом диапазоне дат ничего нет. Я бы использовал CTEи функцию ROW_NUMBER(). CTE — это просто более чистый способ написания подзапроса (хотя в этом случае CTE может сделать намного больше). Функция Row_Number нумерует строки на основе order by инструкции. partition by сбрасывает нумерацию на единицу каждый раз, когда вы вводите новое значение в этом столбце.

 with AppNoteCTE as
(select
<not sure what columns you need here>
app_indiv_id,
ROW_NUMBER() OVER (PARTITION BY APP_INDIV_ID ORDER BY WHEN_MOD DESC) RN
FROM
APP_INDIV
WHERE
WHEN_MOD <= @endDate)

SELECT DISTINCT individual.indiv_id
    FROM individual INNER JOIN 
    app_indiv ON app_indiv.indiv_id = individual.indiv_id INNER JOIN
    AppNoteCTE ON app_indiv.app_indiv_id = AppNoteCTE .app_indiv_id
    and AppNoteCTE.RN = 1