SQL, во время запроса создайте новый столбец на основе условий данных

#sql #date #conditional-statements

#sql #Дата #условные операторы

Вопрос:

Мне удалось собрать SQL-запрос, который работает, используя комбинацию объединений и объединений таблиц, которая дает мне промежуточные результаты, которые мне нужны.

 SELECT  n.study_id AS StudyId,
        n.practice_id AS PracticeId,
    n.FluVaxCode,
    n.Date,
    date(p.BaseStart / 1000, 'unixepoch') AS BaseStart, 
    date(p.BaseEnd / 1000, 'unixepoch') AS BaseEnd,
    date(p.OutcomeStart / 1000, 'unixepoch') AS OutcomeStart, 
    date(p.OutcomeEnd / 1000, 'unixepoch') AS OutcomeEnd,
    CASE WHEN Date BETWEEN BaseStart AND BaseEnd THEN 'Y' ELSE 'N' END AS BaseVax,
    CASE WHEN Date BETWEEN OutcomeStart AND OutcomeEnd THEN 'Y' ELSE 'N' END AS OutcomeVax
FROM toypractice p INNER JOIN
     (SELECT  t.study_id, t.practice_id,
     date(t.event_date / 1000, 'unixepoch') AS Date, 
     t.code_id AS FluVaxCode
     FROM toytherapy t
     WHERE t.code_id IN ('dher.', 'a6b1.', 'bk31.')
     UNION 
     SELECT  c.study_id, c.practice_id, 
     DATE(c.event_date / 1000, 'unixepoch') AS Date, 
     c.code_id AS FluVaxCode
     FROM toyclinical c
     WHERE c.code_id IN ('1383.', '229..', 'X77RW')
     ORDER BY FluVaxCode DESC
     ) n 
     ON p.practice_id = n.practice_id;
 

В итоге я получаю несколько столбцов, включая три как:

 Date, BaseStart, BaseEnd
 

все они в формате ГГГГ-ММ-ДД (я думаю). Есть ли способ, во время запроса я могу добавить некоторый код в конец запроса, чтобы он создавал новый столбец в выходных данных, так что, если дата попадает между BaseStart и BaseEnd, значение в новом столбце будет равно ‘Y’, в противном случае значение в новом столбце будет равно’N’? В этом случае я использую sqlite для разработки / тестирования, но в конечном итоге ему придется работать в mssqlserver 2019. Спасибо. J

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

1. См.: Выражения CASE и производные столбцы.

2. Если вы покажете свою первоначальную попытку или начальный запрос, будет легче настроить решение для включения вашей логики в SQL. Обычно это может быть в форме производной таблицы или термина CTE, но это не всегда необходимо.

3. Ваш новый SQL оставляет еще один вопрос. n.Date выводится так же, как и другие значения. Они должны быть непосредственно пригодны для использования / сопоставимы. Возможно, чего-то еще не хватает. Если вы можете добавить CREATE TABLE инструкции к вопросу, это может помочь.

4. Если бы вы продолжали event_date использовать внешнюю логику, вы могли бы сравнить необработанные значения перед преобразованием. Есть много способов структурировать это.

5. Вы должны иметь возможность внести однострочную корректировку. Я добавил это предложение к ответу.

Ответ №1:

Попробуйте добавить следующее в свой текущий список выбора:

     , CASE WHEN n.Date BETWEEN date(p.BaseStart / 1000, 'unixepoch') AND date(p.BaseEnd / 1000, 'unixepoch') THEN 'a' ELSE 'b' END AS is_between
 

Это можно сделать по-разному. Но вышеупомянутое должно работать.

Вот еще одна настройка, которая должна сработать. Я добавил исходную event_date к результату n производной таблицы, а также добавил соответствующее CASE выражение во внешний список ВЫБОРА. Другие выражения CASE, вероятно, необходимо настроить аналогичным образом.

 SELECT n.study_id AS StudyId
     , n.practice_id AS PracticeId
     , n.FluVaxCode
     , n.Date
     , date(p.BaseStart / 1000, 'unixepoch') AS BaseStart
     , date(p.BaseEnd / 1000, 'unixepoch') AS BaseEnd
     , date(p.OutcomeStart / 1000, 'unixepoch') AS OutcomeStart
     , date(p.OutcomeEnd / 1000, 'unixepoch') AS OutcomeEnd
     , CASE WHEN Date BETWEEN BaseStart AND BaseEnd THEN 'Y' ELSE 'N' END AS BaseVax
     , CASE WHEN Date BETWEEN OutcomeStart AND OutcomeEnd THEN 'Y' ELSE 'N' END AS OutcomeVax
     , CASE WHEN n.event_date BETWEEN p.BaseStart AND p.BaseEnd THEN 'a' ELSE 'b' END AS is_between
  FROM toypractice p
  JOIN (
         SELECT t.study_id, t.practice_id
              , t.event_date
              , date(t.event_date / 1000, 'unixepoch') AS Date
              , t.code_id AS FluVaxCode
           FROM toytherapy t
          WHERE t.code_id IN ('dher.', 'a6b1.', 'bk31.')
          UNION 
         SELECT c.study_id, c.practice_id
              , c.event_date
              , DATE(c.event_date / 1000, 'unixepoch') AS Date
              , c.code_id AS FluVaxCode
           FROM toyclinical c
          WHERE c.code_id IN ('1383.', '229..', 'X77RW')
          ORDER BY FluVaxCode DESC
       ) n 
    ON p.practice_id = n.practice_id
;
 

Скрипка

Что-то вроде этого:

Конкретный тестовый пример для sqlite в качестве отправной точки.

  • cte — предоставляет пару строк, представляющих ваш текущий результат запроса
  • cte2 — показывает, как мы можем преобразовать дату (conv_date), чтобы она была совместима с базовыми значениями
  • Окончательное выражение запроса — показывает, как использовать производную conv_date из cte2 для правильного вычисления между ними.

Обратите внимание, я оставил попытку использовать date напрямую, что приводит к неправильному is_between результату. is_between2 показывает правильный результат.

 WITH cte (date, BaseStart, BaseEnd) AS (
         SELECT 1467241200000, '2016-06-20', '2016-06-30' UNION
         SELECT 1467241200000, '2017-06-20', '2017-06-30'
     )
SELECT date(date / 1000, 'unixepoch'), BaseStart, BaseEnd
     , CASE WHEN Date BETWEEN BaseStart AND BaseEnd THEN 'a' ELSE 'b' END AS is_between
     , CASE WHEN date(date / 1000, 'unixepoch') BETWEEN BaseStart AND BaseEnd THEN 'a' ELSE 'b' END AS is_between2
  FROM cte
;

and also:

WITH cte (date, BaseStart, BaseEnd) AS (
         SELECT 1467241200000, '2016-06-20', '2016-06-30' UNION
         SELECT 1467241200000, '2017-06-20', '2017-06-30'
     )
   , cte2 AS (
         SELECT *
              , date(date / 1000, 'unixepoch') AS conv_date
           FROM cte
     )
SELECT date(date / 1000, 'unixepoch'), BaseStart, BaseEnd
     , CASE WHEN Date      BETWEEN BaseStart AND BaseEnd THEN 'a' ELSE 'b' END AS is_between
     , CASE WHEN conv_date BETWEEN BaseStart AND BaseEnd THEN 'a' ELSE 'b' END AS is_between2
  FROM cte2
;
 

Результат:

дата (date / 1000, ‘unixepoch’) BaseStart BaseEnd is_between is_between2
2016-06-29 2016-06-20 2016-06-30 b a
2016-06-29 2017-06-20 2017-06-30 b b

Общий ответ:

 SELECT Date, BaseStart, BaseEnd
     , CASE WHEN Date BETWEEN BaseStart AND BaseEnd THEN 'a' ELSE 'b' END AS is_between
  FROM ( your SQL ) AS derived_table
;

or

WITH cte AS (
       your sql
     )
SELECT Date, BaseStart, BaseEnd
     , CASE WHEN Date BETWEEN BaseStart AND BaseEnd THEN 'a' ELSE 'b' END AS is_between
  FROM cte
;

 

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

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

1. Спасибо. Джон. Теперь у меня есть еще одна небольшая проблема. В предоставленной мне базе данных даты хранятся в виде длинных цифр, таких как 1,467,241,200,000. В моем SQL-запросе я преобразовал их в ГГГГ-ММ-ДД, используя «дата (значение / 1000, ‘unixepoch’), но тогда проверка МЕЖДУ не работает. Могу ли я преобразовать их во что-то по-человечески видимое, чтобы проверка МЕЖДУ ними также работала должным образом?

2. @JimMaas Помогло бы добавить к вопросу какой-нибудь доступный для выполнения тестовый пример, чтобы мы могли видеть фактические типы, данные и вычисления. Как правило, вы захотите использовать значения, которые являются согласованными и упорядочиваемыми. Используете ли вы все преобразованные значения или неконвертированные значения, на самом деле не имеет значения. При использовании оператора BETWEEN все операнды должны быть согласованными. Обратите внимание, вы не указали, какую дату, BaseStart или BaseEnd представляют / содержат в вопросе. Без тестового примера нам остается предполагать или угадывать.

3. @JimMaas Обратите внимание, что в вашем вопросе у вас есть: все они в формате ГГГГ-ММ-ДД .. Разве это не так? Даже если true, приведите реальные примеры в тестовом примере. Помните, что ваш тестовый пример может быть очень тривиальным, с достаточной детализацией для решения этого конкретного вопроса о получении указания BETWEEN .

4. Спасибо. Джон. Ценю то, что вы предлагаете, не знаю, как предоставить работающий тестовый пример, не предоставляя все, включая базу данных. Предложения?

5. @JimMaas Начните с предоставления базы данных, которую вы используете. Используете ли вы sqlite? Я добавлю к своему ответу своего рода тестовый пример. Это может оказаться полезным 🙂 Решение обновлено.