Frage

Ich habe eine Datenbanktabelle und eines der Felder (nicht der Primärschlüssel) verfügt über einen eindeutigen Index.Jetzt möchte ich Werte unter dieser Spalte für zwei Zeilen austauschen.Wie könnte das geschehen?Zwei Hacks, die ich kenne, sind:

  1. Löschen Sie beide Zeilen und fügen Sie sie erneut ein.
  2. Aktualisieren Sie die Zeilen mit einem anderen Wert und tauschen Sie und aktualisieren Sie dann auf den tatsächlichen Wert.

Aber ich möchte mich nicht dafür entscheiden, da sie nicht die geeignete Lösung für das Problem zu sein scheinen.Könnte mir jemand helfen?

War es hilfreich?

Lösung

Ich denke, Sie sollten sich für Lösung 2 entscheiden.In keiner mir bekannten SQL-Variante gibt es eine „Swap“-Funktion.

Wenn Sie dies regelmäßig tun müssen, empfehle ich Lösung 1, je nachdem, wie andere Teile der Software diese Daten verwenden.Wenn Sie nicht aufpassen, kann es zu Sperrproblemen kommen.

Aber kurz gesagt:Es gibt keine andere Lösung als die, die Sie angegeben haben.

Andere Tipps

Das Zauberwort lautet AUFSCHIEBBAR Hier:

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;

ERGEBNIS:

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)

Weiter zu Andy Irvings Antwort

Dies funktionierte für mich (auf SQL Server 2005) in einer ähnlichen Situation, in der ich einen zusammengesetzten Schlüssel habe, und ich muss ein Feld tauschen, das Teil der eindeutigen Einschränkung ist.

Schlüssel:PID, lnum rec1:10, 0 rec2:10, 1 Rec3:10, 2

und ich muss LNUM austauschen, damit das Ergebnis so ist

Schlüssel:PID, lnum rec1:10, 1 Rec2:10, 2 rec3:100

das benötigte 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))

Es gibt einen anderen Ansatz, der mit SQL Server funktioniert:Verwenden Sie in Ihrer UPDATE-Anweisung einen temporären Tabellen-Join.

Das Problem wird durch zwei Zeilen mit demselben Wert verursacht gleichzeitig, aber wenn Sie beide Zeilen gleichzeitig aktualisieren (auf ihre neuen, eindeutigen Werte), liegt keine Einschränkungsverletzung vor.

Pseudocode:

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

Vielen Dank an Rich H für diese Technik.- Markieren

Ich denke auch, dass #2 die beste Wahl ist, obwohl ich es auf jeden Fall in eine Transaktion einschließen würde, falls während des Updates etwas schief geht.

Eine Alternative (wie Sie gefragt haben) zum Aktualisieren der eindeutigen Indexwerte mit anderen Werten wäre, alle anderen Werte in den Zeilen auf die Werte der anderen Zeile zu aktualisieren.Dies bedeutet, dass Sie die Werte des eindeutigen Index in Ruhe lassen können und am Ende die gewünschten Daten erhalten.Achten Sie jedoch darauf, dass alle Beziehungen in der Datenbank intakt bleiben, falls eine andere Tabelle in einer Fremdschlüsselbeziehung auf diese Tabelle verweist.

Ich habe das gleiche Problem.Hier ist mein vorgeschlagener Ansatz in PostgreSQL.In meinem Fall ist mein eindeutiger Index ein Sequenzwert, der eine explizite Benutzerreihenfolge für meine Zeilen definiert.Der Benutzer mischt die Zeilen in einer Web-App und übermittelt dann die Änderungen.

Ich plane, einen „Vorher“-Trigger hinzuzufügen.In diesem Trigger schaue ich bei jeder Aktualisierung meines eindeutigen Indexwerts nach, ob eine andere Zeile bereits meinen neuen Wert enthält.Wenn ja, gebe ich ihnen meinen alten Wert und stehle ihnen damit faktisch den Wert.

Ich hoffe, dass PostgreSQL es mir ermöglicht, diesen Shuffle im Before-Trigger durchzuführen.

Ich melde mich zurück und teile Ihnen meinen Kilometerstand mit.

Angenommen, Sie kennen den PK der beiden Zeilen, die Sie aktualisieren möchten ...Dies funktioniert in SQL Server, kann nicht für andere Produkte gelten.SQL ist (soll) auf Anweisungsebene atomar:

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;

Sie gehen also von:

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

Zu:

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

Für Oracle gibt es die Option DEFERRED, die Sie jedoch zu Ihrer Einschränkung hinzufügen müssen.

SET CONSTRAINT emp_no_fk_par DEFERRED; 

Um ALLE Einschränkungen, die während der gesamten Sitzung aufschiebbar sind, aufzuschieben, können Sie die Anweisung ALTER SESSION SET Constraints=DEFERRED verwenden.

Quelle

Oracle verfügt über eine verzögerte Integritätsprüfung, die genau dieses Problem löst, aber weder in SQL Server noch in MySQL verfügbar ist.

In SQL Server kann die MERGE-Anweisung Zeilen aktualisieren, die normalerweise einen UNIQUE KEY/INDEX zerstören würden.(Habe es gerade getestet, weil ich neugierig war.)

Sie müssten jedoch eine temporäre Tabelle/Variable verwenden, um MERGE mit den erforderlichen Zeilen bereitzustellen.

Normalerweise denke ich an einen Wert, den absolut kein Index in meiner Tabelle haben könnte.Normalerweise ist das bei eindeutigen Spaltenwerten ganz einfach.Für Werte der Spalte „Position“ (Informationen über die Reihenfolge mehrerer Elemente) ist es beispielsweise 0.

Anschließend können Sie den Wert A in eine Variable kopieren, ihn mit dem Wert B aktualisieren und dann den Wert B aus Ihrer Variablen festlegen.Zwei Fragen, ich kenne jedoch keine bessere Lösung.

1) Tauschen Sie die IDs gegen den Namen aus

id    student 

1     Abbot   
2     Doris  
3     Emerson 
4     Green  
5     Jeames  

Für die Beispieleingabe lautet die Ausgabe:

Ausweis Student

1     Doris   
2     Abbot   
3     Green   
4     Emerson 
5     Jeames  

„Wie geht das im Fall von n Zeilen?“

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top