Поменять местами уникальные значения индексированных столбцов в базе данных

StackOverflow https://stackoverflow.com/questions/644

  •  08-06-2019
  •  | 
  •  

Вопрос

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

  1. Удалите обе строки и вставьте их заново.
  2. Обновите строки с каким -то другим значением и обмена, а затем обновите до фактического значения.

Но я не хочу идти на это, поскольку они не кажутся подходящим решением проблемы.Может ли кто-нибудь мне помочь?

Это было полезно?

Решение

Я думаю, вам следует выбрать решение 2.Ни в одном известном мне варианте SQL нет функции «подкачки».

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

Но вкратце:другого решения, кроме предложенного вами, нет.

Другие советы

Волшебное слово ОТЛОЖЕННАЯ здесь:

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)

В дополнение к ответу Энди Ирвинга

Это сработало для меня (на 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))

Существует еще один подход, который работает с 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 за эту технику.- Отметка

Я также считаю, что №2 — лучший вариант, хотя я бы обязательно обернул его в транзакцию на случай, если что-то пойдет не так в середине обновления.

Альтернативой (раз уж вы спросили) обновлению значений уникального индекса другими значениями было бы обновление всех остальных значений в строках до значений другой строки.Это означает, что вы можете оставить значения уникального индекса в покое и в конечном итоге получить нужные данные.Однако будьте осторожны: если какая-то другая таблица ссылается на эту таблицу в отношении внешнего ключа, все связи в БД останутся нетронутыми.

У меня та же проблема.Вот мой предлагаемый подход в PostgreSQL.В моем случае мой уникальный индекс — это значение последовательности, определяющее явный пользовательский порядок в моих строках.Пользователь будет перетасовывать строки в веб-приложении, а затем отправлять изменения.

Я планирую добавить триггер «до».В этом триггере всякий раз, когда обновляется значение моего уникального индекса, я проверяю, содержит ли уже какая-либо другая строка мое новое значение.Если да, то я передам им свою старую ценность и фактически украду у них эту ценность.

Я надеюсь, что PostgreSQL позволит мне сделать это в триггере before.

Я отвечу и сообщу вам свой пробег.

Предполагая, что вы знаете ПК двух строк, которые хотите обновить...Это работает в 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

Для Oracle есть опция DEFERRED, но вам придется добавить ее в свое ограничение.

SET CONSTRAINT emp_no_fk_par DEFERRED; 

Чтобы отложить ВСЕ ограничения, которые можно отложить в течение всего сеанса, вы можете использовать оператор ALTER SESSION SET ограничения=DEFERRED.

Источник

Oracle имеет отложенную проверку целостности, которая решает именно эту проблему, но она недоступна ни в SQL Server, ни в MySQL.

В SQL Server инструкция MERGE может обновлять строки, которые обычно нарушают UNIQUE KEY/INDEX.(Просто проверил это, потому что мне было любопытно.)

Однако вам придется использовать временную таблицу/переменную для предоставления MERGE с необходимыми строками.

Обычно я думаю о значении, которое не может иметь ни один индекс в моей таблице.Обычно — для уникальных значений столбца — это действительно просто.Например, для значений столбца «позиция» (информация о порядке нескольких элементов) это 0.

Затем вы можете скопировать значение A в переменную, обновить его значением B, а затем установить значение B из вашей переменной.Два вопроса, лучшего решения я не знаю.

1) переключить идентификаторы для имени

id    student 

1     Abbot   
2     Doris  
3     Emerson 
4     Green  
5     Jeames  

Для образца входных данных выходные данные:

удостоверение личности студента

1     Doris   
2     Abbot   
3     Green   
4     Emerson 
5     Jeames  

"в случае n количества строк, как получится......"

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top