Можно ли выполнить несколько обновлений с помощью одного оператора UPDATE SQL?

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

  1. от «а-1» до «а1»,
  2. от «а.1» до «а1»,
  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 секунд), и мне нужно выполнить огромное количество обновлений.

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

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


person Paulius    schedule 05.01.2009    source источник
comment
Итак, исходя из вашего комментария EDIT, сами строки могут быть разными, но обновление, которое вы пытаетесь выполнить, следует шаблону? Если так, то, что это? Если ничего не имеет шаблона, то решения нет, вам нужно кодировать каждое идиосинкразическое обновление отдельно.   -  person Charles Bretana    schedule 05.01.2009
comment
У меня есть список правильных значений, и у меня есть четко определенный список неправильных значений (и какое неправильное значение нужно изменить на какое правильное значение). Так что да, у обновлений есть закономерность. Короче говоря - каждое обновление изменяет ОДНО значение, но только если старое значение находится В указанном списке значений.   -  person Paulius    schedule 06.01.2009


Ответы (5)


В более общем случае, когда для каждого из новых значений может быть много сотен отображений, вы должны создать отдельную таблицу со старыми и новыми значениями, а затем использовать ее в операторе 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);

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

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

person Jonathan Leffler    schedule 05.01.2009
comment
Это ОЧЕНЬ интересно. Я никогда даже не думал об этом. Вставки в картограф по-прежнему будут быстрыми, а обновление будет сканировать мою таблицу только один раз, и мне не нужно создавать огромные запросы. - person Paulius; 05.01.2009
comment
но лучше использовать соединение, чем коррелированный подзапрос по соображениям производительности. - person HLGEM; 05.01.2009
comment
@HLGEM: да, если ваша СУБД поддерживает нотацию. Не могли бы вы предложить рабочий синтаксис для какой-нибудь известной вам СУБД? Если да, отредактируйте мой ответ - я считаю, что у вас достаточно представителей, чтобы сделать это. Или дайте мне знать по электронной почте - см. страницу моего профиля. - person Jonathan Leffler; 05.01.2009

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

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')

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

person casperOne    schedule 05.01.2009

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

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

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

person mrdenny    schedule 05.01.2009
comment
@MrDenny: могу ли я скопировать ваш материал в свой ответ - конечно, с указанием авторства? - person Jonathan Leffler; 05.01.2009
comment
Я использовал его запрос, и он сработал как шарм! Я был на самом деле удивлен - это было действительно быстрее, чем я думал. Очень хорошо. - person Paulius; 19.01.2009

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

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

Что-то подобное сработает для вас?

person Matt Hamilton    schedule 05.01.2009
comment
Нет, к сожалению, реальные данные I, с которыми я имею дело, не так просты, как в моем примере. Это не сработает для меня. Впрочем, спасибо. - person Paulius; 05.01.2009
comment
Похоже, что использование casperOne выражения CASE WHEN - это то, что нужно. - person Matt Hamilton; 05.01.2009

person    schedule
comment
Как я уже упоминал в комментариях к ответу Мэтта, данные в реальной базе данных не так просты. - person Paulius; 05.01.2009
comment
Рискуя показаться очевидным, но тогда ответ будет менее простым. В чем настоящая проблема? - person Charles Bretana; 05.01.2009
comment
Возможно, стоило в примере сделать названия другими, чтобы они не были такими простыми. Настоящая проблема заключается в том, что настоящие заголовки представляют собой строки, которые не следуют никакому шаблону — на самом деле это заголовки, созданные пользователями, поэтому я не могу делать никаких предположений о них. Я также отредактировал свой вопрос. - person Paulius; 05.01.2009