SQL: оператор обновления с динамическим присвоением значения столбца - PullRequest
3 голосов
/ 17 февраля 2009

Представьте себе следующий SQL-запрос:

UPDATE MYTABLE
SET COL2 = (SELECT COL2 + 1 FROM (SELECT MAX(COL2) FROM MYTABLE) AS X)
WHERE ID IN (1,2,3,4,5)

Предположим, что перед выполнением обновления MAX (COL2) равно 1.

Я предполагаю, что для обновления, где ID = 1, COL2 обновляется до 'max (COL2) + 1' (то есть 2), а для последующих обновлений 'MAX (COL2) + 1' переоценивается, поэтому что для ID = 2, COL2 = 3 и ID = 3, COL2 = 4 и т. д ...

На самом деле происходит то, что для всех строк (ID = 1,2,3,4,5) значение COL2 равно 2.

Существует ли разумный способ повторной оценки значения MAX (COL2) +1 при каждом обновлении? Я понимаю, что при этом могут возникнуть проблемы с производительностью, но тем не менее мне любопытно! Есть ли лучшая альтернатива (не включающая несколько операторов обновления)?

Кстати: если вас интересует синтаксис, использованный для вышеуказанного запроса (вложенная внутренняя таблица), см. Здесь: SQL: Использование целевой таблицы в операторе UPDATE во вложенном предложении FROM

Ответы [ 5 ]

11 голосов
/ 17 февраля 2009
UPDATE mytable, (
  SELECT @loop := MAX(col1)
  FROM
    mytable
  ) o
SET col1 = (@loop := @loop + 1)

То, с чем вы здесь столкнулись, называется query stability.

Ни один запрос не может видеть сделанные изменения или следующий запрос:

UPDATE mytable
SET col1 = col2 + 1
WHERE col1 > col2 

никогда не закончится.

2 голосов
/ 17 февраля 2009

Вот как я бы это сделал:

SELECT MAX(col2) FROM mytable INTO @max;

UPDATE mytable
SET col2 = @max:=@max+1
WHERE id IN (1,2,3,4,5)
ORDER BY id;

Я проверил это на MySQL 5.1.30, и оно работает.

Сначала я установил переменную @max только потому, что считаю, что трюк @ Quassnoi делать это в декартовом произведении ненужен и менее читабелен.

Обратите внимание, что MySQL поддерживает ORDER BY в операторе UPDATE (это не стандартный SQL), и вы должны сделать это, чтобы значения обновлялись в нужном вам порядке. В противном случае MySQL не гарантирует определенного порядка.

0 голосов
/ 17 февраля 2009
declare @loop int
select @loop = 1

UPDATE MYTABLE
SET COL1 = @loop,
    @loop = @loop+1
WHERE ID IN (1,2,3,4,5)
0 голосов
/ 17 февраля 2009

Есть ли умный способ сделать так, чтобы значение MAX (COL1) +1 "переоценивалось" при каждой вставке?

Нет, если вы не обновите строки одну за другой: (* ​​1007 *

Это будет работать в MSSQL, не знаю о MySQL, но может дать вам представление:

DECLARE @Counter int

SELECT @Counter = MAX(COL2) FROM MYTABLE

UPDATE MYTABLE
SET @Counter = @Counter + 1,
    COL1 = @Counter
WHERE ID IN (1,2,3,4,5)

РЕДАКТИРОВАТЬ: Я думаю, что оператор SET может быть упрощен в MSSQL до:

SET @Counter = COL1 = @Counter + 1
0 голосов
/ 17 февраля 2009

Следует переоценивать его при каждой вставке; предположительно максимум COL2 не меняется. Вы уверены, что не хотите выбирать MAX (COL 1 )? Читая ваш вопрос еще раз, вы перепутали условия вставки и обновления (что теперь исправлено), и меня бросили за петлю.


Вы пытаетесь обновить много записей одновременно, но при этом действует так, как если бы они обновляли каждую запись по отдельности. Это не сработает так, как вы собираетесь.

Вы можете создать хранимую процедуру для циклического прохождения каждой записи WHERE ID IN (1,2,3,4,5) и обновления по отдельности. Или, поскольку вы делаете только 5 строк, почему бы просто не скопировать и вставить инструкцию пять раз и изменить предложение WHERE на WHERE ID = 1 (и 2, 3, 4, 5 на последующих строках).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...