SQL - 使用 CASE 语句更新,是不是需要多次重复相同的 CASE?

Posted

技术标签:

【中文标题】SQL - 使用 CASE 语句更新,是不是需要多次重复相同的 CASE?【英文标题】:SQL - Update with a CASE statement, do I need to repeat the same CASE multiple times?SQL - 使用 CASE 语句更新,是否需要多次重复相同的 CASE? 【发布时间】:2011-04-05 08:03:02 【问题描述】:

我的 UPDATE 语句大致如下:

UPDATE  customer
SET  forenames=ot.forenames,
     surname =

CASE WHEN ot.safeplace IS NULL
THEN 'test SAFEPLACE IS NULL'
ELSE 'test Safeplace IS NOT NULL'
END,

     middlename =

CASE WHEN ot.safeplace IS NULL
THEN 'test2 SAFEPLACE IS NULL'
ELSE 'test2 Safeplace IS NOT NULL'
END,

FROM    order_transaction ot

WHERE   customer.custid = ot.custid
AND ot.trans_orderid = 5678
AND customer.custid = 1234

以上工作。它基本上检查另一个表中的字段是否为 NULL,然后相应地更新客户的“姓氏”和“中间名”。正如您在上面看到的,我重复了两次相同的 CASE 语句。我的问题是 - 有没有办法只指定一次 CASE 语句?

关键是,如果我说要根据某个条件更新 10 个字段,是否需要包含 10 个类似的 CASE 条件?或者可以将 SQL 改进为只有一个 CASE,并在 WHEN / ELSE 子句中更新 10 个字段?

(我使用的是 Postgresql 8.2 数据库,但我相信以上是标准 SQL)。

非常感谢, 里士

【问题讨论】:

【参考方案1】:

我相信以上是标准 SQL

其实不然。标准 SQL 没有 UPDATE..FROM 语法。相反,您需要为每个 SET 子句使用一个标量子查询,并为 EXISTS 使用另一个子查询,因此标准语法更加重复,例如

UPDATE customer
   SET forenames = (
                    SELECT ot.forenames
                      FROM order_transaction AS ot
                     WHERE customer.custid = ot.custid
                           AND ot.trans_orderid = 5678
                   ),
       surname = (
                  SELECT CASE 
                            WHEN ot.safeplace IS NULL 
                               THEN 'test SAFEPLACE IS NULL'
                            ELSE 'test Safeplace IS NOT NULL'
                         END
                    FROM order_transaction AS ot
                   WHERE customer.custid = ot.custid
                         AND ot.trans_orderid = 5678
                 ),
       middlename = (
                     SELECT CASE 
                               WHEN ot.safeplace IS NULL 
                                  THEN 'test SAFEPLACE IS NULL'
                               ELSE 'test Safeplace IS NOT NULL'
                            END
                       FROM order_transaction AS ot
                      WHERE customer.custid = ot.custid
                            AND ot.trans_orderid = 5678
                    )
 WHERE customer.custid = 1234
       AND EXISTS (
                   SELECT * 
                     FROM order_transaction AS ot
                    WHERE customer.custid = ot.custid
                          AND ot.trans_orderid = 5678
                  );

虽然语法看起来很重复,但好的优化器应该能够识别重复并相应地进行优化。您的 SQL 产品的当前版本在实践中是否真的能很好地优化这一点当然是另一回事。但是请考虑一下:如果您选择的 SQL 产品支持标准语法但实际上并未正确优化它,那么“支持”是否值得?

如果您正在寻找使用标准 SQL(因为您确实应该使用 IMO :) 并且想要更“紧凑”的语法,请查看 MERGE 或 MERGE (SQL) 例如可能看起来更像这样:

MERGE INTO customer
   USING (
          SELECT ot.custid, ot.forenames, 
                 CASE 
                     WHEN ot.safeplace IS NULL 
                        THEN 'test SAFEPLACE IS NULL'
                     ELSE 'test Safeplace IS NOT NULL'
                  END
             FROM order_transaction AS ot
            WHERE ot.trans_orderid = 5678   
         ) AS source (custid, forenames, safeplace_narrative)
   ON customer.custid = source.custid
      AND customer.custid = 1234
WHEN MATCHED THEN
   UPDATE 
      SET forenames = source.forenames, 
          surname = source.safeplace_narrative, 
          middlename = source.safeplace_narrative;

【讨论】:

@Richard aka cyberkiwi:更新答案以添加一个 MERGE 示例,以显示它在保留标准 SQL 的同时更紧凑。【参考方案2】:

如果您想在同一查询级别上执行 CASE,则需要重复 CASE,就像在 group by 子句中重复计算列一样。

您的示例查询根本没有显示您想要做什么,您是否真的将 all 记录更新为相同的值(固定文本),以及 all每条记录的列。如果您更新以使问题更相关,可能会有更好的答案。


但是现在,对于您的特定查询,您可以使用类似这样的内容
UPDATE  customer
SET  forenames=ot.forenames,
     surname = fixedText,
     middlename = fixedText    
FROM (select o.*, CASE
      WHEN safeplace IS NULL
      THEN 'test2 SAFEPLACE IS NULL'
      ELSE 'test2 Safeplace IS NOT NULL'
      END fixedText
      from order_transaction o) ot
WHERE   customer.custid = ot.custid
AND ot.trans_orderid = 5678
AND customer.custid = 1234

【讨论】:

不是最好的解决方案理查德。当您的选择首先扫描表并添加结果,然后过滤它。 @Dumitrescu Bogdan / 我不认为它会那样做。因为 PostgreSQL 将展开查询以将过滤器应用到子查询中,但仍保留使用已定义(计算)列的能力 OP 的语法不是标准 SQL(另请注意,SQL 标准是国际性的,并且 'ANSI' 中的 'A' 代表 'American' 所以“符合 ISO 的 SQL”在这里更合适:) 这肯定是它应该处理它的合乎逻辑的方式..虽然我从不依赖它。不幸的是,我没有可以实际看到这种行为的 psql 服务器。 @Dumitrescu Bogdan / 我愿意,我已经测试过了。 Explain 清楚地表明它可以正常工作。【参考方案3】:

如果您需要多次复制确切的大小写(远不止 2 次),您可以使用下一个查询。但是您必须确实需要复制案例,而不是使用 test 和 test2 (这不是完全相同的案例)。显然,如果您需要将诸如 test / test 2 之类的文本连接到结果中,那么您可以在 select 语句中执行此操作。例如:surname = 'test '+st.result 所以有一些可能性可以做一些'hacks'。

UPDATE  customer
SET  forenames=ot.forenames,
     surname = st.result,
     middlename = st.result

FROM    order_transaction ot
JOIN (select 1 as ID,'test SAFEPLACE IS NULL' as result 
      union
      select 2,'test SAFEPLACE IS NULL') st on case when ot.safeplace is null then 1 else 2 end = st.id

WHERE   customer.custid = ot.custid
AND ot.trans_orderid = 5678
AND customer.custid = 1234

【讨论】:

以上是关于SQL - 使用 CASE 语句更新,是不是需要多次重复相同的 CASE?的主要内容,如果未能解决你的问题,请参考以下文章

检查 SQL CASE 语句中是不是存在

T-SQL:在 UPDATE 语句中使用 CASE 根据条件更新某些列

SQL语句,使用case when 实现批量更新数据

SQL语句,使用case when 实现批量更新数据

Sql server 未在查询中使用嵌套 case 语句更新记录

Oracle SQL - 使用 Case 语句缺少关键字错误的更新查询