【问题标题】:case statement in SQL, how to return multiple variables?SQL中的case语句,如何返回多个变量?
【发布时间】:2020-11-01 04:53:26
【问题描述】:

我想在我的 case 语句中返回多个值,例如:

SELECT
  CASE
    WHEN <condition 1> THEN <value1=a1, value2=b1>
    WHEN <condition 2> THEN <value1=a2, value2=b2>
    ELSE <value1=a3, value3=b3>
  END
FROM <table>

当然我可以多次写case条件,每次返回一个值。但是,因为我有很多条件需要适应,比如说 100。一次又一次地重复 case 条件是不好的。

我想问的另一个问题是,如果一个记录符合多个条件会发生什么?这是否意味着它将返回所有这些或仅返回最后一个?例如条件 1 可能成为条件 2 的子集。会发生什么?

【问题讨论】:

  • 您能说明一下您的情况吗?例如,100 个不同的案例条件是否会导致 value1 和 value2 中的每一个都有 100 个不同的适用值,或者 100 个不同的条件中的许多会产生相同的结果?条件是否都是不同字段的完全组合,还是单个字段的不同可能值?
  • 是的,100 种不同的案例条件导致每个 value1 和 value2 有 100 个不同的值。条件都是不同领域的组合.. 非常感谢
  • @yzhang : 结果固定了吗?这样,与条件 1 匹配的每一行将始终在 value1 和 value2 中具有完全相同的值
  • 是的,基本上,我有 100 条规则,我需要通过表格检查每条记录,以符合这些规则,如果符合,那么我将返回 3 个值......但是有些记录可能符合多个规则。理想情况下,所有结果都将返回.. 然后,我将根据其他条件选择为每条记录保留一组值
  • @yzhang - 对于CASE,只有第一个匹配项会返回值。如果您希望对每个输入行进行多个条件的数学运算,则需要单独进行每个检查,并将结果合并在一起。 @Yuck 和我都有可以满足你的答案。

标签: sql sql-server


【解决方案1】:

不幸的是,基本的方法是重复自己。

SELECT
  CASE WHEN <condition 1> THEN <a1> WHEN <condition 2> THEN <a2> ELSE <a3> END,
  CASE WHEN <condition 1> THEN <b1> WHEN <condition 2> THEN <b2> ELSE <b3> END
FROM 
  <table> 

幸运的是,大多数 RDBMS 都足够聪明,不必多次评估条件。这只是多余的输入。


在 MS SQL Server (2005+) 中,您可以使用 CROSS APPLY 作为替代方案。虽然我不知道它的性能如何......

SELECT
  *
FROM
  <table>
CROSS APPLY
  (
   SELECT a1, b1 WHERE <condition 1>
   UNION ALL
   SELECT a2, b2 WHERE <condition 2>
   UNION ALL
   SELECT a3, b3 WHERE <condition 3>
  )
  AS case_proxy

这里值得注意的缺点是没有 ELSE 等效项,并且由于所有条件可能都返回值,因此需要对它们进行框架,以便一次只有一个条件为真。 p>


编辑

如果 Yuck 的答案改为 UNION 而不是 JOIN 方法,它会变得与此非常相似。但是,主要区别在于,这只扫描输入数据集一次,而不是每个条件一次(在您的情况下为 100 次)。


编辑

我还注意到您可能表示 CASE 语句返回的值是固定的。匹配相同条件的所有记录在 value1 和 value2 中获得完全相同的值。可以这样形成……

WITH
  checked_data AS
(
  SELECT
    CASE WHEN <condition1> THEN 1
         WHEN <condition2> THEN 2
         WHEN <condition3> THEN 3
         ...
         ELSE                   100
    END AS condition_id,
    *
  FROM
    <table>
)
,
  results (condition_id, value1, value2) AS
(
   SELECT 1, a1, b1
   UNION ALL
   SELECT 2, a2, b2
   UNION ALL
   SELECT 3, a3, b3
   UNION ALL
   ...
   SELECT 100, a100, b100
)
SELECT
  *
FROM
  checked_data
INNER JOIN
  results
    ON results.condition_id = checked_data.condition_id

【讨论】:

    【解决方案2】:

    CASE 语句只能返回一个值。

    您可以将其转换为子查询,然后将JOIN 转换为您正在处理的任何其他关系。例如(使用 SQL Server 2K5+ CTE):

    WITH C1 AS (
      SELECT a1 AS value1, b1 AS value2
      FROM table
      WHERE condition1
    ), C2 AS (
      SELECT a2 AS value1, b2 AS value2
      FROM table
      WHERE condition2
    ), C3 AS (
      SELECT a3 AS value1, b3 AS value2
      FROM table
      WHERE condition3
    )
    SELECT value1, value2
    FROM -- some table, joining C1, C2, C3 CTEs to get the cased values
    ;
    

    【讨论】:

    • 而不是加入(可能是外连接与合并语句),您可能会将这些联合在一起。假设对于任何给定的输入记录只有一个条件为真。即使这样,这也需要针对每个条件扫描一次源数据集,因此需要对数据进行 100 次扫描。
    • @Dems:是的,这也可以。 OP 问题中的ELSE 条件写为ELSE &lt;value1=a3, value3=b3&gt;,因此列并不完全相同。我在回答中“修正”了这个问题,但它可能不是错字。
    • 好地方,我不会假设错字,它可能是相关的,因此需要在 value2 中使用 NULL 以获得其他结果。
    【解决方案3】:

    CASE 根据定义只返回一个值。永远。

    它也(几乎总是)短路,这意味着如果满足您的第一个条件,则不会运行其他检查。

    【讨论】:

    • +1 有没有短路的情况?
    • @MarkBannister - 是的,但它们有问题(在 SQL Server 中)。基本上,我看到的唯一情况是您与变量的聚合(CASE WHEN MAX(@var)...)进行比较。它将评估它们,但仍然返回正确的值。
    【解决方案4】:

    在您的情况下,您将使用两个 case 语句,一个用于您想要返回的每个值。

    【讨论】:

      【解决方案5】:

      您可以在“case”表达式中返回 xml 数据类型中的多个值,然后提取它们,“else”块也可用

      SELECT 
      xmlcol.value('(value1)[1]', 'NVARCHAR(MAX)') AS value1,
      xmlcol.value('(value2)[1]', 'NVARCHAR(MAX)') AS value2
      FROM
      (SELECT CASE
      WHEN <condition 1> THEN
      CAST((SELECT a1 AS value1, b1 AS value2 FOR XML PATH('')) AS XML)
      WHEN <condition 2> THEN
      CAST((SELECT a2 AS value1, b2 AS value2 FOR XML PATH('')) AS XML)
      ELSE
      CAST((SELECT a3 AS value1, b3 AS value2 FOR XML PATH('')) AS XML)
      END AS xmlcol
      FROM <table>) AS tmp
      

      【讨论】:

      • 虽然代码对于有经验的人来说可能很容易理解,但初学者可能需要解释。请添加说明您的代码的作用以及它如何解决 OP 问题。
      【解决方案6】:

      或者你可以

      SELECT
        String_to_array(CASE
          WHEN <condition 1> THEN a1||','||b1
          WHEN <condition 2> THEN a2||','||b2
          ELSE a3||','||b3
        END, ',') K
      FROM <table>
      

      【讨论】:

      • SQL Server:THEN a1+','+b1.
      【解决方案7】:

      在 SQL CASE 子句中,应用第一个成功匹配的条件,并忽略任何后续匹配条件。

      【讨论】:

        【解决方案8】:

        您可以将子选择与 UNION 结合使用。 每当您可以为多个条件返回相同的字段时,请使用 OR 和括号,如下例所示:

        SELECT * FROM
          (SELECT val1, val2 FROM table1 WHERE (condition1 is true) 
                                            OR (condition2 is true))
        UNION
        SELECT * FROM
          (SELECT val5, val6 FROM table7 WHERE (condition9 is true) 
                                            OR (condition4 is true))
        

        【讨论】:

        • 对于其他 99 个条件呢? (这给出 2 个字段,但仅适用于 1 个条件)
        • 使用 UNION,每个条件一个子查询。
        • @Dems:你也可以在这里使用'OR',(我编辑我的例子以适应它)
        【解决方案9】:

        根据您的用例,您可以使用多个 select 语句的并集,而不是使用 case 语句,每个条件一个。

        当我发现这个问题时,我的目标是有条件地选择多个列。我不一定需要case语句,所以我就是这样做的。

        例如:

          SELECT
            a1,
            a2,
            a3,
            ...
          WHERE <condition 1>
            AND (<other conditions>)
          UNION
          SELECT
            b1,
            b2,
            b3,
            ...
          WHERE <condition 2>
            AND (<other conditions>)
          UNION
          SELECT
          ...
        -- and so on
        
        

        确保一次只有一个条件为真。

        我使用的是 Postgresql,如果 where 子句中的条件评估为 false(即只有一个 select 语句实际运行),查询计划器足够聪明,根本不运行 select 语句,所以这也是对我来说很高效。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2022-06-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-07-07
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多