Можно ли выполнить несколько обновлений с помощью одного оператора SQL UPDATE? - PullRequest
27 голосов
/ 05 января 2009

Допустим, у меня есть таблица tbl со столбцами id и title . Мне нужно изменить все значения заголовка столбца:

  1. от 'a-1' до 'a1',
  2. от 'a.1' до 'a1',
  3. от 'b-1' до 'b1',
  4. от 'b.1' до 'b1'.

Сейчас я выполняю два оператора UPDATE:

UPDATE tbl SET title='a1' WHERE title IN ('a-1', 'a.1')
UPDATE tbl SET title='b1' WHERE title IN ('b-1', 'b.1')

Это совсем не проблема, если таблица небольшая, а один оператор завершается менее чем за секунду, и для его выполнения требуется всего несколько операторов.

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

Итак, можно ли объединить обновления, чтобы таблица сканировалась только один раз? Или, возможно, есть лучший способ справиться с такой ситуацией.

РЕДАКТИРОВАТЬ: обратите внимание, что реальные данные, с которыми я работаю, и изменения в данных, которые я должен выполнить, на самом деле не так просты - строки длиннее и не следуют какой-либо схеме (это пользовательские данные, поэтому никакие предположения не могут быть сделаны - это может быть что угодно).

Ответы [ 5 ]

23 голосов
/ 05 января 2009

Вы можете использовать одно утверждение и несколько падежных операторов

update tbl
  set title = 
    case
      when title in ('a-1', 'a.1') then 'a1'
      when title in ('b-1', 'b.1') then 'b1'
      else title
    end

Конечно, это приведет к записи каждой записи, а с индексами это может быть проблемой, поэтому вы можете отфильтровать только те строки, которые хотите изменить:

update tbl
  set title = 
    case
      when title in ('a-1', 'a.1') then 'a1'
      when title in ('b-1', 'b.1') then 'b1'
      else title
    end
where
  title in ('a.1', 'b.1', 'a-1', 'b-1')

Это сократит количество записей в таблицу.

22 голосов
/ 05 января 2009

В более общем случае, когда может быть много сотен сопоставлений для каждого из новых значений, вы должны создать отдельную таблицу старых и новых значений, а затем использовать ее в операторе UPDATE. На одном диалекте SQL:

CREATE TEMP TABLE mapper (old_val CHAR(5) NOT NULL, new_val CHAR(5) NOT NULL);
...multiple inserts into mapper...
INSERT INTO mapper(old_val, new_val) VALUES('a.1', 'a1');
INSERT INTO mapper(old_val, new_val) VALUES('a-1', 'a1');
INSERT INTO mapper(old_val, new_val) VALUES('b.1', 'b1');
INSERT INTO mapper(old_val, new_val) VALUES('b-1', 'b1');
...etcetera...

UPDATE tbl
   SET title = (SELECT new_val FROM mapper WHERE old_val = tbl.title)
   WHERE title IN (SELECT old_val FROM mapper);

Оба выбранных утверждения имеют решающее значение. Первый - это коррелированный подзапрос (не обязательно быстрый, но более быстрый, чем большинство альтернатив, если таблица сопоставления имеет тысячи строк), который извлекает новое значение из таблицы сопоставления, которое соответствует старому значению. Второй гарантирует, что изменяются только те строки, которые имеют значение в таблице сопоставления; это очень важно, так как в противном случае заголовок будет иметь значение NULL для тех строк без записи сопоставления (и это были записи, которые были в порядке до того, как вы начали).

Для нескольких вариантов операции CASE в порядке. Но если у вас есть сотни, тысячи или миллионы отображений для выполнения, то вы, вероятно, превысите пределы длины оператора SQL в вашей СУБД.

9 голосов
/ 05 января 2009

Отработка ответа Джонатана.

UPDATE tbl
   SET title = new_val
FROM mapper
WHERE title IN (SELECT old_val FROM mapper)
     AND mapper.old_val = tbl.title;

Его первоначальная версия потребовала бы большого числа операций чтения в таблицу сопоставления.

3 голосов
/ 05 января 2009

Если преобразования так же просты, как ваши примеры, вы можете выполнить обновление с небольшим количеством манипуляций со строками:

UPDATE tbl 
SET title = left(title, 1) + right(title, 1) 
WHERE title IN ('a-1', 'a.1', 'b-1', 'b.1')

Будет ли что-то подобное работать для вас?

0 голосов
/ 05 января 2009

или

   Update Table set 
     title = Replace(Replace(title, '.', ''), '-', '')
   Where title Like '[ab][.-]1'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...