Swap unieke geïndexeerde kolom waarden in de database

stemmen
47

Ik heb een database tabel en één van de velden (niet de primaire sleutel) heeft een unieke index op. Nu wil ik de waarden wisselen in deze kolom voor twee rijen. Hoe kon dit gebeuren? Twee hacks die ik ken zijn:

  1. Verwijder zowel rijen en plaats ze
  2. Werk rijen met een andere waarde en swap en vervolgens updaten naar actuele waarde.

Maar ik wil niet te gaan voor deze, omdat zij niet schijnen om de juiste oplossing voor het probleem te zijn. Kan iemand me helpen?

De vraag is gesteld op 03/08/2008 om 10:55
bron van user
In andere talen...                            


12 antwoorden

stemmen
8

Ik denk dat je moet gaan voor oplossing 2. Er is geen functie 'swap' in een SQL-variant ik ken.

Als u dit regelmatig doet, stel ik voor oplossing 1, afhankelijk van hoe andere delen van de software worden met behulp van deze gegevens. U kunt vergrendelen problemen hebben als je niet oppast.

Maar in het kort: er is geen andere oplossing dan die u heeft opgegeven.

antwoordde op 03/08/2008 om 13:26
bron van user

stemmen
2

Ik denk ook dat # 2 is de beste gok, maar ik zou er zeker van zijn om het te verpakken in een transactie in het geval er iets misgaat mid-update.

Een alternatief (omdat je gevraagd) aan het bijwerken van de unieke index waarden met verschillende waarden zou zijn om alle van de andere waarden bij te werken in de rijen met die van de andere rij. Door dit te doen betekent dat u de unieke index waarden alleen kon laten, en op het einde, eindig je met de gegevens die u wilt. Wees wel voorzichtig, in het geval een aantal andere tafel verwijzingen deze tabel in een buitenlandse Key relatie, dat alle relaties in de DB intact blijven.

antwoordde op 03/08/2008 om 14:22
bron van user

stemmen
2

Ervan uitgaande dat je weet dat de PK van de twee rijen die u wilt bijwerken ... Dit werkt in SQL Server, kunnen niet voor andere producten te spreken. SQL is (zou moeten zijn) atomaire op de verklaring niveau:

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;

dus je zal gaan van:

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

naar:

cola    colb
------------
1       a
2       b
antwoordde op 16/09/2008 om 15:57
bron van user

stemmen
5

Aanvulling op het antwoord Andy Irving's

Dit werkte voor mij (op SQL Server 2005) in een soortgelijke situatie waar ik een samengestelde sleutel en ik moet een gebied dat deel uitmaakt van de unieke beperking wisselen.

key: PID LNUM REC1: 10, 0 rec2: 10, 1 REC3: 10, 2

en ik moet LNUM te verwisselen, zodat het resultaat

key: PID LNUM REC1: 10, 1 rec2: 10, 2 REC3: 10, 0

de SQL nodig:

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))
antwoordde op 10/12/2008 om 13:19
bron van user

stemmen
2

Ik heb hetzelfde probleem. Hier is mijn voorgestelde aanpak in PostgreSQL. In mijn geval, mijn unieke index is een volgnummer, welke een expliciete user-order op mijn rijen. De gebruiker zal toeren langs schuifelen in een web-app, dan is de wijzigingen te verzenden.

Ik ben van plan om een ​​toe te voegen "voor" trigger. In dat trigger, wanneer mijn unieke indexwaarde wordt bijgewerkt, zal ik kijken om te zien of een andere rij al mijn nieuwe waarde houdt. Als dat zo is, zal ik hen mijn oude waarde te geven, en effectief te stelen de waarde van hen af.

Ik hoop dat PostgreSQL me zal toestaan ​​om deze shuffle doen in de voor trigger.

Ik kom na achterover en laat je weet dat mijn kilometers.

antwoordde op 24/06/2009 om 04:58
bron van user

stemmen
1

Oracle heeft uitgesteld integriteit controle en dat is precies dit oplost, maar het is niet beschikbaar in elk SQL Server of MySQL.

antwoordde op 19/03/2010 om 20:29
bron van user

stemmen
5

Er is een andere aanpak die werkt met SQL Server: gebruik maken van een tijdelijke tabel mee te doen met het in uw verklaring van de UPDATE.

Het probleem wordt veroorzaakt door het hebben van twee rijen met dezelfde waarde op hetzelfde moment , maar als je beide rijen tegelijk te werken (naar hun nieuwe, unieke waarden), is er geen schending van de beperking.

Pseudo-code:

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

Met dank aan Rich H voor deze techniek. - Mark

antwoordde op 04/04/2012 om 20:40
bron van user

stemmen
1

In SQL Server, kan het samenvoegen verklaring rijen die normaal een unieke sleutel / INDEX zou breken bij te werken. (Dit beeld Enkel getest omdat ik nieuwsgierig was.)

Echter, zou je moet een tijdelijke tabel / variabele gebruiken om te fuseren leveren w / de benodigde rijen.

antwoordde op 20/04/2012 om 20:12
bron van user

stemmen
21

Het toverwoord is deferrable hier:

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;

RESULTAAT:

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)
antwoordde op 15/09/2012 om 13:38
bron van user

stemmen
1

Voor Oracle is er een optie, UITGESTELDE, maar je moet het toe te voegen aan uw beperking.

SET CONSTRAINT emp_no_fk_par DEFERRED; 

Om alle beperkingen die deferrable zijn gedurende de hele sessie uit te stellen, kunt u de ALTER SESSION SET beperkingen = UITGESTELD statement te gebruiken.

Bron

antwoordde op 27/04/2016 om 14:15
bron van user

stemmen
0

Ik denk dat meestal met een waarde van dat er absoluut geen index in mijn tafel kon hebben. Meestal - voor de unieke waarden van de kolom - het is heel eenvoudig. Bijvoorbeeld voor waarden van kolom 'positie' (informaties orde van enkele elementen) is de 0.

Dan kunt u de waarde A kopiëren naar een variabele, bijwerken met de waarde B en stel dan de waarde B van de variabele. Twee vragen, ik ken geen betere oplossing wel.

antwoordde op 18/03/2017 om 18:00
bron van user

stemmen
1

1) zet de id's naam

id    student 

1     Abbot   
2     Doris  
3     Emerson 
4     Green  
5     Jeames  

Voor het monster input de output is:

id student

1     Doris   
2     Abbot   
3     Green   
4     Emerson 
5     Jeames  

"In het geval n aantal rijen hoe zal beheren ......"

antwoordde op 06/11/2018 om 08:56
bron van user

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