У меня есть таблица базы данных, и одно из полей (не первичный ключ) имеет уникальный индекс. Теперь я хочу поменять местами значения в этом столбце для двух строк. Как это можно было сделать? Я знаю два хака:
Но я не хочу прибегать к ним, поскольку они не кажутся подходящим решением проблемы. Может ли кто-нибудь мне помочь?


Я думаю, вам следует выбрать решение 2. Ни в одном из известных мне вариантов SQL нет функции «подкачки».
Если вам нужно делать это регулярно, я предлагаю решение 1, в зависимости от того, как другие части программного обеспечения используют эти данные. Если вы не будете осторожны, у вас могут возникнуть проблемы с блокировкой.
Но вкратце: нет другого решения, кроме тех, которые вы предоставили.
Я также считаю, что №2 - лучший выбор, хотя я обязательно включу его в транзакцию, если что-то пойдет не так в середине обновления.
Альтернативой (поскольку вы просили) обновить значения уникального индекса с другими значениями, было бы обновление всех других значений в строках до значений другой строки. Это означает, что вы можете оставить значения уникального индекса в покое, и, в конце концов, вы получите нужные данные. Однако будьте осторожны, если какая-то другая таблица ссылается на эту таблицу в отношении внешнего ключа, чтобы все отношения в БД остались нетронутыми.
Предполагая, что вы знаете PK двух строк, которые хотите обновить ... Это работает в SQL Server, не может говорить о других продуктах. SQL является (предполагается) атомарным на уровне операторов:
CREATE TABLE testing
(
cola int NOT NULL,
colb CHAR(1) NOT NULL
);
CREATE UNIQUE INDEX UIX_testing_a ON testing(colb);
INSERT INTO testing VALUES (1, 'b');
INSERT INTO testing VALUES (2, 'a');
SELECT * FROM testing;
UPDATE testing
SET colb = CASE cola WHEN 1 THEN 'a'
WHEN 2 THEN 'b'
END
WHERE cola IN (1,2);
SELECT * FROM testing;
так что вы пойдете с:
cola colb
------------
1 b
2 a
к:
cola colb
------------
1 a
2 b
В дополнение к ответу Энди Ирвинга
это сработало для меня (на SQL Server 2005) в аналогичной ситуации где у меня есть составной ключ, и мне нужно поменять местами поле, которое является частью уникального ограничения.
ключ: pID, LNUM rec1: 10, 0 rec2: 10, 1 rec3: 10, 2
и мне нужно поменять местами LNUM, чтобы результат был
ключ: pID, LNUM rec1: 10, 1 rec2: 10, 2 rec3: 10, 0
требуемый SQL:
UPDATE DOCDATA
SET LNUM = CASE LNUM
WHEN 0 THEN 1
WHEN 1 THEN 2
WHEN 2 THEN 0
END
WHERE (pID = 10)
AND (LNUM IN (0, 1, 2))
У меня та же проблема. Вот предлагаемый мной подход в PostgreSQL. В моем случае мой уникальный индекс - это значение последовательности, определяющее явный пользовательский порядок в моих строках. Пользователь перетасует строки в веб-приложении, а затем отправит изменения.
Я планирую добавить триггер «до». В этом триггере всякий раз, когда обновляется мое уникальное значение индекса, я проверяю, содержит ли уже какая-либо другая строка мое новое значение. Если так, я отдам им свою старую ценность и фактически украду у них ценность.
Я надеюсь, что PostgreSQL позволит мне сделать это в случайном порядке в триггере before.
Я отправлю ответ и сообщу свой пробег.
Oracle имеет отложенную проверку целостности, которая решает именно эту проблему, но она недоступна ни в SQL Server, ни в MySQL.
Есть еще один подход, который работает с SQL Server: используйте временную таблицу, присоединенную к ней в своем операторе UPDATE.
Проблема вызвана наличием двух строк с одинаковым значением в то же время, но если вы обновляете обе строки одновременно (до их новых уникальных значений), нарушения ограничений не возникает.
Псевдокод:
-- setup initial data values:
insert into data_table(id, name) values(1, 'A')
insert into data_table(id, name) values(2, 'B')
-- create temp table that matches live table
select top 0 * into #tmp_data_table from data_table
-- insert records to be swapped
insert into #tmp_data_table(id, name) values(1, 'B')
insert into #tmp_data_table(id, name) values(2, 'A')
-- update both rows at once! No index violations!
update data_table set name = #tmp_data_table.name
from data_table join #tmp_data_table on (data_table.id = #tmp_data_table.id)
Спасибо Rich H за эту технику. - Отметка
Возможно, это немного устарело, но я пытался сделать страницу переупорядочения для своего приложения silverlight, поскольку клиент хотел отсортировать свои отчеты в определенном порядке - я добавил столбец сортировки, но, поскольку это уникальный ключ, я был возникли проблемы с обновлением. В итоге я использовал табличную переменную, но принцип тот же (если честно, мне не очень нравятся временные таблицы!). Спасибо за идею :)
В SQL Server оператор MERGE может обновлять строки, которые обычно нарушают UNIQUE KEY / INDEX. (Просто проверил это, потому что мне было любопытно.)
Однако вам придется использовать временную таблицу / переменную для предоставления MERGE с необходимыми строками.
У MERGE есть другие .. проблемы .. и то же самое можно сделать с помощью цикла DELETE / UPDATE / INSERT; если UPDATE выполняется как один оператор (см. UPDATE..JOIN), то он не нарушает никаких ограничений PK / AK / UX, поскольку ограничения применяются только к конечному результату оператора. Так можно ли использовать MERGE на месте UPDATE..JOIN для решения этой проблемы? Конечно .. потому что это то, что внутренне.
Волшебное слово здесь ОТЛОЖЕННЫЙ:
DROP TABLE ztable CASCADE;
CREATE TABLE ztable
( id integer NOT NULL PRIMARY KEY
, payload varchar
);
INSERT INTO ztable(id,payload) VALUES (1,'one' ), (2,'two' ), (3,'three' );
SELECT * FROM ztable;
-- This works, because there is no constraint
UPDATE ztable t1
SET payload=t2.payload
FROM ztable t2
WHERE t1.id IN (2,3)
AND t2.id IN (2,3)
AND t1.id <> t2.id
;
SELECT * FROM ztable;
ALTER TABLE ztable ADD CONSTRAINT OMG_WTF UNIQUE (payload)
DEFERRABLE INITIALLY DEFERRED
;
-- This should also work, because the constraint
-- is deferred until "commit time"
UPDATE ztable t1
SET payload=t2.payload
FROM ztable t2
WHERE t1.id IN (2,3)
AND t2.id IN (2,3)
AND t1.id <> t2.id
;
SELECT * FROM ztable;
РЕЗУЛЬТАТ:
DROP TABLE
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "ztable_pkey" for table "ztable"
CREATE TABLE
INSERT 0 3
id | payload
----+---------
1 | one
2 | two
3 | three
(3 rows)
UPDATE 2
id | payload
----+---------
1 | one
2 | three
3 | two
(3 rows)
NOTICE: ALTER TABLE / ADD UNIQUE will create implicit index "omg_wtf" for table "ztable"
ALTER TABLE
UPDATE 2
id | payload
----+---------
1 | one
2 | two
3 | three
(3 rows)
@MarcoDemaio Я не знаю. Боюсь, что нет: поскольку mysql не допускает обновлений из самоподключений, я бы предположил, что он выполняет грязное чтение. Но вы могли бы попробовать ...
Это не поддерживается в MS SQL Server.
Может быть, Microsoft это не поддерживает. (sql-server не входит в число тегов) Поскольку вы за него заплатили, может быть, вы могли бы попросить об этом?
Для Oracle есть опция DEFERRED, но вы должны добавить ее к своему ограничению.
SET CONSTRAINT emp_no_fk_par DEFERRED;
Чтобы отложить ВСЕ ограничения, которые можно отложить в течение всего сеанса, вы можете использовать оператор ALTER SESSION SET constraints = DEFERRED.
Обычно я думаю о значении, которое не может иметь ни один индекс в моей таблице. Обычно - для уникальных значений столбца - это действительно просто. Например, для значений столбца position (информация о порядке нескольких элементов) это 0.
Затем вы можете скопировать значение A в переменную, обновить его значением B, а затем установить значение B из своей переменной. Два вопроса, лучшего решения я не знаю.
1) переключите идентификаторы для имени
id student
1 Abbot
2 Doris
3 Emerson
4 Green
5 Jeames
Для примера ввода вывод:
id студент
1 Doris
2 Abbot
3 Green
4 Emerson
5 Jeames
"в случае n числа строк, как будет справляться ......"
Если это сработает, это здорово, потому что это можно сделать в рамках одной транзакции.