2008-08-03 81 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.兩個查詢,我知道沒有更好的解決方案。

相關問題