我有一个数据库表,其中一个字段(不是主键)有一个唯一的索引.现在我想将此列下的值换成两行.怎么可以这样做?我知道的两个黑客是:
但我不想这样做,因为它们似乎不是解决问题的合适方法.任何人都可以帮我吗?
wil*_*ser 28
这里神奇的词是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;
Run Code Online (Sandbox Code Playgroud)
结果:
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)
Run Code Online (Sandbox Code Playgroud)
Daa*_*aan 13
我认为你应该寻求解决方案2.在我所知道的任何SQL变体中都没有'swap'功能.
如果您需要定期执行此操作,我建议使用解决方案1,具体取决于软件的其他部分如何使用此数据.如果你不小心,你可能会遇到锁定问题.
但简而言之:除了你提供的解决方案之外没有其他解决方案.
小智 6
继Andy Irving的回答
这对我(在SQL Server 2005上)在类似的情况下工作,我有一个复合键,我需要交换一个属于唯一约束的字段.
key:pID,LNUM rec1:10,0 rec2:10,1 rec3:10,2
我需要交换LNUM以便得到结果
key: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))
Run Code Online (Sandbox Code Playgroud)
假设您知道要更新的两行的 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;
Run Code Online (Sandbox Code Playgroud)
所以你将从:
cola colb
------------
1 b
2 a
Run Code Online (Sandbox Code Playgroud)
到:
cola colb
------------
1 a
2 b
Run Code Online (Sandbox Code Playgroud)
小智 5
还有另一种适用于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)
Run Code Online (Sandbox Code Playgroud)
感谢Rich H的这项技术. - 马克
| 归档时间: |
|
| 查看次数: |
12480 次 |
| 最近记录: |