2008-08-03 79 views
47

我有一个数据库表,其中一个字段(不是主键)上有一个唯一索引。现在我想将这个列下的值交换两行。这怎么能做到?两名黑客我知道有:在数据库中交换唯一索引列值

  1. 删除行和其他一些价值 和交换重新插入它们
  2. 更新行,然后更新到实际值。

但我不想去这些,因为他们似乎不是解决问题的恰当方法。 任何人都可以帮我吗?

+0

重命名列? – MatBailie 2016-04-18 07:19:53

回答

8

我认为你应该去找解决方案2.在我知道的任何SQL变体中,没有“swap”函数。

如果你需要定期做这个,我建议解决方案1,这取决于软件的其他部分如何使用这些数据。如果你不小心,你可能会遇到锁定问题。

但总而言之:除了您提供的解决方案外,没有其他解决方案。

2

我也认为#2是最好的选择,尽管我会确保将它包装在交易中,以防中间更新出现问题。

另一种方法(因为您要求)使用不同的值更新唯一索引值将会将行中的所有其他值更新为另一行的值。这样做意味着您可以单独保留唯一索引值,并最终获得所需的数据。但要小心,以防其他表在DB中引用此表时,DB中的所有关系都保持不变。

2

假设您知道要更新的两行的PK ...这在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 
+0

这在MySQL中并不适用于我。 – 2008-11-05 10:14:54

5

继安迪·欧文的回答

这对我有用(在SQL Server 2005上)在类似的情况下 我有一个组合键和I n交换一个属于唯一约束一部分的字段。

键: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)) 
+0

如果这样做,那很好,它可以在一个单一的交易 – codeulike 2009-09-23 20:06:06

2

我有同样的问题。这是我在PostgreSQL中提出的方法。在我的情况下,我的唯一索引是一个序列值,在我的行上定义了明确的用户顺序。用户将在网络应用程序中随机播放行,然后提交更改。

我打算添加一个“before”触发器。在该触发器中,只要我的唯一索引值更新,我会查看是否有其他行已经保存了我的新值。如果是这样,我会给他们我以前的价值,并有效地窃取他们的价值。

我希望PostgreSQL将允许我在前触发器中做这个洗牌。

我会回复并让您知道我的里程。

1

Oracle具有延迟完整性检查,它完全解决了这个问题,但它在SQL Server或MySQL中都不可用。

3

还有一种与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) 

由于富H进行此技术。 - Mark

+1

这可能会有点老,但我想为我的Silverlight应用做一个'重新排序'页面,因为客户希望按照他们的报告排序一个特定的顺序 - 我添加了一个排序列,但由于它是一个独特的密钥,我无法更新它。我结束了使用表变量,但原则是一样的(我不喜欢临时表很多老实说!)。感谢您的想法:) – Charleh 2012-06-29 00:18:11

1

在SQL Server中,MERGE语句可以更新通常会破坏UNIQUE KEY/INDEX的行。 (刚刚测试过,因为我很好奇。)

但是,您必须使用临时表/变量来为MERGE提供必要的行。

20

魔语是DEFERRABLE这里:

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

这是否在MySQL的工作? – 2012-12-27 14:35:12

1

对于Oracle有一个选项,推迟,但你必须将它添加到你的约束。

SET CONSTRAINT emp_no_fk_par DEFERRED; 

要推迟整个会话期间延迟的所有约束,你可以使用ALTER SESSION SET约束=递延声明。

Source

0

我通常会认为绝对在我的表没有索引可以有一个价值。通常 - 对于独特的列值 - 这非常简单。例如,对于列“位置”(有关多个元素的顺序的信息)的值,它为0.

然后,您可以将值A复制到变量中,使用值B更新它,然后从变量中设置值B.两个查询,我知道没有更好的解决方案。

相关问题