【问题标题】:SQL, during query create new column based on data conditionsSQL,在查询期间根据数据条件创建新列
【发布时间】:2022-01-14 08:42:02
【问题描述】:

我已经设法拼凑出一个有效的 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

所有这些都是 YYYY-MM-DD 格式(我认为)。有没有办法,在查询期间,我可以在查询结束时添加一些代码,以便它将在输出中创建一个新列,这样如果 Date 介于 BaseStart 和 BaseEnd 之间,则新列中的值将是' Y',否则新列中的值将是'N'?在这种情况下,我使用 sqlite 进行开发/测试,但它最终必须在 mssqlserver 2019 中工作。谢谢。 J

【问题讨论】:

  • 参见:CASE 表达式和派生列。
  • 如果您显示您的初始尝试或初始查询,则调整解决方案以将您的逻辑合并到 SQL 中会更容易。通常,这可能是派生表或 CTE 术语的形式,但并不总是必要的。
  • 您的新 SQL 留下了另一个问题。 n.Date 与其他值一样是派生的。它们应该是直接可用/可比较的。可能还缺少其他东西。如果您可以将CREATE TABLE 语句添加到问题中,那可能会有所帮助。
  • 如果您让event_date 暴露于外部逻辑,您可以在转换之前比较原始值。有很多方法可以构建它。
  • 您应该能够进行单行调整。我已将该建议添加到答案中。

标签: sql date conditional-statements


【解决方案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 表达式添加到外部 SELECT 列表中。其他 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
;

The fiddle

类似这样的:

sqlite 的特定测试用例,作为起点。

  • cte - 提供几行代表您当前的查询结果
  • cte2 - 展示我们如何将日期 (conv_date) 转换为与基本值兼容
  • 最终查询表达式 - 显示如何使用从 cte2 派生的 conv_date 正确计算 BETWEEN。

注意,我尝试直接使用日期,这会产生错误的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(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,467,241,200,000。在我的 SQL 查询中,我使用 "date(value / 1000, 'unixepoch') 将它们转换为 YYYY-MM-DD,但是 BETWEEN 检查不起作用。我可以将它们转换为人类可见的东西,以便 BETWEEN 检查也将工作正常吗?
  • @JimMaas 在问题中添加某种可运行的测试用例会有所帮助,这样我们就可以看到实际的类型、数据和计算。通常,您需要使用一致且可排序的值。无论您使用所有转换的值还是未转换的值,都无关紧要。使用 BETWEEN 运算符时,所有操作数都需要保持一致。请注意,您没有在问题中显示 Date、BaseStart 或 BaseEnd 代表/包含什么的任何指示。如果没有测试用例,我们只能假设或猜测。
  • @JimMaas 请注意您的问题:所有这些都是 YYYY-MM-DD 格式 .. 这不是真的吗?即使正确,也要在测试用例中提供实际示例。请记住,您的测试用例可能非常琐碎,只有足够的细节来处理这个关于推导 BETWEEN 指示的特定问题。
  • 谢谢。乔恩。欣赏您的建议,不知道如何提供运行测试用例而不提供包括数据库在内的所有内容。建议?
  • @JimMaas 首先提供您正在使用的数据库。你在用sqlite吗?我将在我的答案中添加一种测试用例。您可能会发现它很有帮助 :) 解决方案已更新。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-04
  • 2020-10-02
  • 2020-07-29
  • 2022-11-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多