SQL - обновить с помощью оператора CASE, нужно ли повторять один и тот же CASE несколько раз?

Мой оператор 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 и 10 обновлений полей в предложениях WHEN/ELSE?

(Я использую базу данных Postgresql 8.2, но я считаю, что это стандартный SQL).

Большое спасибо, Риши

Ответ 1

Я считаю, что это стандартное SQL

Собственно, это не так. Стандартный SQL не имеет синтаксиса UPDATE..FROM. Скорее, вам нужно использовать скалярный подзапрос для каждого предложения SET плюс другой для EXISTS, поэтому синтаксис Standard еще более повторяется, например.

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-продукт по выбору поддерживает синтаксис Standard, но на самом деле не оптимизирует его, значит, "поддержка" стоит чего-то?

Если вы хотите использовать стандартный SQL (так как вы действительно должны ИМО:) и хотите более "компактный" синтаксис, посмотрите 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;

Ответ 2

Если вы хотите выполнить CASE на том же уровне запросов, вам нужно будет повторить CASE так же, как вы повторите вычисляемый столбец в разделе group by.

В вашем примере запроса вообще не отображается то, что вы хотите сделать, действительно ли вы обновляете все записи до одного и того же значения (фиксированный текст), а также столбцы все на запись. Если вы обновляетесь, чтобы сделать вопрос более актуальным, вероятно, лучший ответ.


Но пока для вашего конкретного запроса вы можете использовать что-то вроде этого
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

Ответ 3

Если вам нужно скопировать точный случай больше раз (более 2), вы можете использовать следующий запрос. Но вам действительно нужно скопировать случай, а не с test и test2 (это не совсем тот же случай). Очевидно, если вам нужно объединить текст, например test/test 2 или что-то другое, то вы можете сделать это в инструкции select. ex: surname = 'test' + st.result, поэтому есть некоторые возможности сделать некоторые "хаки".

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