Zamień unikalne wartości kolumn indeksowanych w bazie

głosy
47

Mam tabeli bazy danych i jedno z pól (nie klucz podstawowy) jest o unikalny indeks na nim. Teraz chcę, aby zamienić wartości w tej kolumnie dla dwóch rzędach. Jak można to zrobić? Dwa hacki znam to:

  1. Usuń zarówno wiersze i je ponownie wstawić
  2. Zaktualizować wiersze z inną wartością i zamiany, a następnie zaktualizować do wartości rzeczywistej.

Ale ja nie chcę iść do nich, ponieważ nie wydaje się być odpowiednim rozwiązaniem problemu. Czy ktoś może mi pomóc?

Utwórz 03/08/2008 o 10:55
źródło użytkownik
W innych językach...                            


12 odpowiedzi

głosy
21

Magiczne słowo jest odroczeniu tutaj:

DROP TABLE ztable CASCADE;
CREATE TABLE ztable
    ( id integer NOT NULL PRIMARY KEY
    , payload varchar
    );
INSERT IGNORE  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;

WYNIK:

DROP TABLE
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "ztable_pkey" for table "ztable"
CREATE TABLE
INSERT IGNORE  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)
Odpowiedział 15/09/2012 o 13:38
źródło użytkownik

głosy
8

Myślę, że należy udać się do roztworu 2. Nie ma funkcji „Swap” w dowolnym wariancie SQL wiem.

Jeśli trzeba to robić regularnie, proponuję rozwiązanie 1, w zależności od tego, jak inne części oprogramowania używasz tych danych. Możesz mieć problemy blokujące, jeśli nie jesteś ostrożny.

Ale w skrócie: nie ma innego rozwiązania niż te, które podałeś.

Odpowiedział 03/08/2008 o 13:26
źródło użytkownik

głosy
5

Nie ma innego podejścia, które współpracuje z SQL Server: użyć tabeli temp przystąpić do niego w instrukcji UPDATE.

Problem spowodowany jest posiadanie dwóch wierszy o tej samej wartości w tym samym czasie , ale jeśli aktualizować zarówno wiersze na raz (do nowych, unikalnych wartości), nie ma naruszenia ograniczenia.

Pseudo kod:

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

Dzięki Rich H do tej techniki. - Znak

Odpowiedział 04/04/2012 o 20:40
źródło użytkownik

głosy
5

W nawiązaniu do odpowiedzi Andy Irvinga

ten pracował dla mnie (na SQL Server 2005) w podobnej sytuacji gdzie mam klucza kompozytowej i muszę zamienić pole, które jest częścią unikalnego ograniczeń.

klucz: PID, LNUM REC1: 10, 0 rEC2: 10, 1 rEC3: 10, 2

i muszę zamienić LNUM tak, że wynik jest

klucz: PID, LNUM REC1: 10, 1 rEC2: 10, 2 rEC3: 10, 0

SQL potrzebne:

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))
Odpowiedział 10/12/2008 o 13:19
źródło użytkownik

głosy
2

Mam ten sam problem. Oto mój proponowane podejście w PostgreSQL. W moim przypadku, mój unikalny wskaźnik jest wartością sekwencja, definiując wyraźny obsługi zamówienie na moich wierszy. Użytkownik będzie przetasować rzędy dokoła w internetowej aplikacji, a następnie przesłać zmiany.

Mam zamiar dodać „przed” spuście. W tym spuście, gdy moja wyjątkowa wartość indeksu jest aktualizowana, ja patrzę, czy jakikolwiek inny rząd posiada już moją nową wartość. Jeśli tak, to dam im moją starą wartość i skutecznie ukraść wartości poza nimi.

Mam nadzieję, że PostgreSQL pozwoli mi to zrobić przed shuffle na spuście.

wyślę z powrotem i niech wiesz, mój przebieg.

Odpowiedział 24/06/2009 o 04:58
źródło użytkownik

głosy
2

Zakładając, że wiesz PK z dwóch wierszy, które chcesz zaktualizować ... To działa w SQL Server, nie można mówić o innych produktach. SQL jest (powinien być) atomowy na poziomie instrukcji:

CREATE TABLE testing
(
    cola int NOT NULL,
    colb CHAR(1) NOT NULL
);

CREATE UNIQUE INDEX UIX_testing_a ON testing(colb);

INSERT IGNORE  INTO testing VALUES (1, 'b');
INSERT IGNORE  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;

tak pójdziesz z:

cola    colb
------------
1       b
2       a

do:

cola    colb
------------
1       a
2       b
Odpowiedział 16/09/2008 o 15:57
źródło użytkownik

głosy
2

Myślę też, że 2 jest najlepiej, choć byłbym pewien, zawinąć go w transakcji w przypadku coś pójdzie nie tak w połowie aktualizacji.

Alternatywą (skoro pytasz) aktualizacją wartości unikatowy indeks o różnych wartościach byłoby zaktualizować wszystkie inne wartości w wierszach do tych drugiego rzędu. Rozwiązanie to oznacza, że ​​można zostawić wartości indeksu unikalnego sam, a w końcu skończyć z danymi, które chcesz. Bądź jednak ostrożny, w przypadku niektórych innych odniesień stół to stół w obcym relacji Key, że wszystkie relacje w DB pozostają nienaruszone.

Odpowiedział 03/08/2008 o 14:22
źródło użytkownik

głosy
1

1) przełączyć identyfikatory dla nazwy

id    student 

1     Abbot   
2     Doris  
3     Emerson 
4     Green  
5     Jeames  

Dla wejścia próbki, wyjście jest:

Student id

1     Doris   
2     Abbot   
3     Green   
4     Emerson 
5     Jeames  

„W przypadku liczby n wierszy jak uda ......”

Odpowiedział 06/11/2018 o 08:56
źródło użytkownik

głosy
1

Oracle nie jest opcją, odroczona, ale trzeba go dodać do przymusu.

SET CONSTRAINT emp_no_fk_par DEFERRED; 

Odroczyć wszystkie ograniczenia, które są odroczeniu podczas całej sesji, można użyć ALTER SESSION SET ograniczeń = DEFERRED oświadczenie.

Źródło

Odpowiedział 27/04/2016 o 14:15
źródło użytkownik

głosy
1

W SQL Server, oświadczenie MERGE mogą aktualizować wiersze, które normalnie rozkładają unikalny klucz / index. (Tylko przetestowane to, bo byłem ciekaw.)

Jednakże, trzeba by użyć tabeli / zmienna temp dostarczyć MERGE w / koniecznych wierszy.

Odpowiedział 20/04/2012 o 20:12
źródło użytkownik

głosy
1

Oracle wstrzymuje sprawdzanie integralności, które rozwiązuje dokładnie to, ale to nie jest dostępne w wersji SQL Server lub MySQL.

Odpowiedział 19/03/2010 o 20:29
źródło użytkownik

głosy
0

Zwykle myślę o wartości, która absolutnie nie ma indeksu w moim stole może mieć. Zwykle - dla unikatowych wartości kolumny - to naprawdę proste. Na przykład, dla wartości kolumnie „Pozycja” (informacja o rzędu kilku elementów) jest 0.

Następnie można skopiować wartość A do zmiennej, należy zaktualizować go z wartości B, a następnie ustawić wartość B od zmiennej. Dwa pytania, nie znam lepszego rozwiązania choć.

Odpowiedział 18/03/2017 o 18:00
źródło użytkownik

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more