Pergunta

Eu tenho uma tabela de banco de dados e um dos campos (não a chave primária) possui um índice exclusivo.Agora quero trocar os valores desta coluna por duas linhas.Como isso poderia ser feito?Dois hacks que conheço são:

  1. Exclua ambas as linhas e insira-as novamente.
  2. Atualize linhas com algum outro valor e troca e, em seguida, atualize para o valor real.

Mas não quero optar por isso, pois não parecem ser a solução adequada para o problema.Alguém poderia me ajudar?

Foi útil?

Solução

Eu acho que você deveria ir para a solução 2.Não há função 'swap' em nenhuma variante SQL que eu conheça.

Se você precisar fazer isso regularmente, sugiro a solução 1, dependendo de como outras partes do software estão usando esses dados.Você pode ter problemas de bloqueio se não tomar cuidado.

Mas resumindo:não há outra solução além das que você forneceu.

Outras dicas

A palavra mágica é DIFERÍVEL aqui:

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;

RESULTADO:

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)

Seguindo a resposta de Andy Irving

Isso funcionou para mim (no SQL Server 2005) em uma situação semelhante em que tenho uma chave composta e preciso trocar um campo que faz parte da restrição única.

chave:pid, lnum rec1:10, 0 Rec2:10, 1 Rec3:10, 2

e preciso trocar o LNUM para que o resultado seja

chave:pid, lnum rec1:10, 1 Rec2:10, 2 Rec3:10, 0

o SQL necessário:

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

Existe outra abordagem que funciona com o SQL Server:use uma tabela temporária associada a ela em sua instrução UPDATE.

O problema é causado por ter duas linhas com o mesmo valor ao mesmo tempo, mas se você atualizar ambas as linhas de uma vez (para seus valores novos e exclusivos), não haverá violação de restrição.

Pseudo-código:

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

Obrigado a Rich H por esta técnica.- Marca

Eu também acho que o número 2 é a melhor aposta, embora eu tenha certeza de envolvê-lo em uma transação caso algo dê errado no meio da atualização.

Uma alternativa (desde que você pediu) para atualizar os valores do Índice Único com valores diferentes seria atualizar todos os outros valores nas linhas para os da outra linha.Fazer isso significa que você pode deixar os valores do Índice Único de lado e, no final, obter os dados desejados.Porém, tenha cuidado, caso alguma outra tabela faça referência a esta tabela em um relacionamento de chave estrangeira, para que todos os relacionamentos no banco de dados permaneçam intactos.

Eu tenho o mesmo problema.Aqui está minha abordagem proposta no PostgreSQL.No meu caso, meu índice exclusivo é um valor de sequência, definindo uma ordem de usuário explícita em minhas linhas.O usuário embaralhará as linhas em um aplicativo da web e enviará as alterações.

Estou planejando adicionar um gatilho "antes".Nesse gatilho, sempre que meu valor de índice exclusivo for atualizado, verificarei se alguma outra linha já contém meu novo valor.Nesse caso, darei a eles meu antigo valor e efetivamente roubarei o valor deles.

Espero que o PostgreSQL me permita fazer esse embaralhamento no gatilho anterior.

Vou postar de volta e informar minha quilometragem.

Supondo que você conheça o PK das duas linhas que deseja atualizar...Isso funciona no SQL Server, não posso falar por outros produtos.SQL é (supostamente) atômico no nível da instrução:

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;

então você irá de:

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

para:

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

Para Oracle existe uma opção, DEFERRED, mas você tem que adicioná-la à sua restrição.

SET CONSTRAINT emp_no_fk_par DEFERRED; 

Para adiar TODAS as restrições que podem ser adiadas durante toda a sessão, você pode usar a instrução ALTER SESSION SET constraints=DEFERRED.

Fonte

A Oracle adiou a verificação de integridade que resolve exatamente isso, mas não está disponível no SQL Server ou no MySQL.

No SQL Server, a instrução MERGE pode atualizar linhas que normalmente quebrariam uma UNIQUE KEY/INDEX.(Só testei isso porque estava curioso.)

No entanto, você teria que usar uma tabela/variável temporária para fornecer MERGE com as linhas necessárias.

Normalmente penso em um valor que absolutamente nenhum índice da minha tabela poderia ter.Normalmente - para valores de coluna exclusivos - é muito fácil.Por exemplo, para valores da coluna 'posição' (informações sobre a ordem de vários elementos) é 0.

Então você pode copiar o valor A para uma variável, atualizá-lo com o valor B e então definir o valor B da sua variável.Duas perguntas, mas não conheço solução melhor.

1) troque os ids por nome

id    student 

1     Abbot   
2     Doris  
3     Emerson 
4     Green  
5     Jeames  

Para a entrada de amostra, a saída é:

estudante de identificação

1     Doris   
2     Abbot   
3     Green   
4     Emerson 
5     Jeames  

"no caso de n número de linhas como será gerenciado...."

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top