在数据库中交换唯一的索引列值
我有一个数据库表,其中一个字段(不是主键)上有一个唯一的索引。 现在我想交换这两列下的值。 这怎么可能呢? 我知道的两个黑客是:
- 删除两行并重新插入它们
- 用其他值更换行并交换,然后更新为实际值。
但我不想去这些,因为他们似乎不是解决问题的恰当方法。 任何人都可以帮我吗?
我认为你应该去解决scheme2.在我所知的任何SQL变体中没有“交换”function。
如果您需要定期执行此操作,则build议使用解决scheme1,具体取决于软件的其他部分如何使用此数据。 如果你不小心,你可以有locking问题。
但总而言之:除了你提供的解决scheme,没有别的解决scheme。
这里的魔术字是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)
继Andy Irving的回答
这对我来说(在SQL Server 2005上)在类似的情况下,我有一个组合键,我需要交换一个字段,这是唯一约束的一部分。
键:pID,LNUM rec1:10,0 rec2:10,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))
还有另一种与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)
感谢Rich H对此技术的支持。 – 马克
我也认为#2是最好的select,尽pipe我会确保在交易中包装它,以防中间更新中出现问题。
另一种(因为您问)更新具有不同值的唯一索引值将是将行中的所有其他值更新到另一行的值。 这样做意味着您可以单独保留唯一索引值,最终最终得到您想要的数据。 但是,要小心,以防其他表引用此表中的外键关系,DB中的所有关系保持不变。
我也有同样的问题。 这是我在PostgreSQL中提出的方法。 就我而言,我唯一的索引是一个序列值,在我的行上定义了一个明确的用户顺序。 用户将在networking应用程序中随机播放行,然后提交更改。
我打算添加一个“之前”的触发器。 在该触发器中,只要我的唯一索引值被更新,我会查看是否有其他行已经保存了我的新值。 如果是这样,我会给他们以前的价值,并有效地窃取他们的价值。
我希望PostgreSQL将允许我在之前的触发器中做这个洗牌。
我会回来,让你知道我的里程。
假设你知道你想更新的两行的PK …这在SQL Server中工作,不能说其他产品。 SQL(应该是)在语句级别是primefaces的:
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
Oracle已经推迟了完整性检查,它完全解决了这个问题,但是它在SQL Server或MySQL中都不可用。
在SQL Server中,MERGE语句可以更新通常会破坏UNIQUE KEY / INDEX的行。 (刚刚testing过,因为我好奇。)
但是,您必须使用临时表/variables来提供必要的MERGE行。
对于Oracle,有一个选项DEFERRED,但是您必须将其添加到您的约束。
SET CONSTRAINT emp_no_fk_par DEFERRED;
要推迟在整个会话期间可延期的所有约束,可以使用ALTER SESSION SET constraints = DEFERRED语句。
资源
我通常会想到一个值,绝对没有我的表中的索引可以有。 通常 – 对于独特的列值 – 这很容易。 例如,对于“位置”列的值(关于几个元素的顺序的信息),它是0。
然后,您可以将值A复制到一个variables,用值B更新它,然后从variables中设置值B. 两个查询,我知道没有更好的解决scheme。