문제

데이터베이스 테이블이 있고 필드 중 하나(기본 키 아님)에 고유 인덱스가 있습니다.이제 이 열 아래의 값을 두 행으로 바꾸고 싶습니다.어떻게 이런 일이 가능할까요?내가 아는 두 가지 해킹은 다음과 같습니다.

  1. 두 행을 모두 삭제하고 다시 삽입하십시오.
  2. 다른 값으로 행을 업데이트하고 스왑 한 다음 실제 값으로 업데이트하십시오.

그러나 나는 이러한 방법이 문제에 대한 적절한 해결책이 아닌 것 같기 때문에 선택하고 싶지 않습니다.누구든지 나를 도와줄 수 있나요?

도움이 되었습니까?

해결책

해결책 2를 선택해야 한다고 생각합니다.내가 아는 SQL 변형에는 '스왑' 기능이 없습니다.

이 작업을 정기적으로 수행해야 하는 경우 소프트웨어의 다른 부분에서 이 데이터를 사용하는 방식에 따라 솔루션 1을 제안합니다.주의하지 않으면 잠금 문제가 발생할 수 있습니다.

그러나 간단히 말해서:귀하가 제공한 솔루션 외에는 다른 솔루션이 없습니다.

다른 팁

마법의 단어는 연기 가능 여기:

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

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가 최선의 선택이라고 생각합니다.

요청한 이후 고유 인덱스 값을 다른 값으로 업데이트하는 대안은 행의 다른 모든 값을 다른 행의 값으로 업데이트하는 것입니다.이렇게 하면 고유 인덱스 값만 그대로 두고 결국 원하는 데이터를 얻을 수 있다는 의미입니다.하지만 다른 테이블이 외래 키 관계에서 이 테이블을 참조하는 경우 DB의 모든 관계가 그대로 유지된다는 점에 주의하세요.

나는 같은 문제를 가지고있다.PostgreSQL에서 제가 제안한 접근 방식은 다음과 같습니다.내 경우, 내 고유 인덱스는 내 행에 대한 명시적인 사용자 순서를 정의하는 시퀀스 값입니다.사용자는 웹앱에서 행을 섞은 다음 변경 사항을 제출합니다.

"이전" 트리거를 추가할 계획입니다.해당 트리거에서는 고유 인덱스 값이 업데이트될 때마다 다른 행에 이미 새 값이 있는지 확인합니다.그렇다면 나는 그들에게 나의 이전 가치를 부여하고 효과적으로 그들의 가치를 훔칠 것입니다.

나는 PostgreSQL이 이전 트리거에서 이 셔플을 수행할 수 있기를 바라고 있습니다.

다시 글을 올려 마일리지를 알려드리겠습니다.

업데이트하려는 두 행의 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

Oracle의 경우 DEFERRED 옵션이 있지만 이를 제약 조건에 추가해야 합니다.

SET CONSTRAINT emp_no_fk_par DEFERRED; 

전체 세션 동안 연기할 수 있는 모든 제약 조건을 연기하려면 ALTER SESSION SET Constraints=DEFERRED 문을 사용할 수 있습니다.

원천

Oracle은 이 문제를 정확하게 해결하는 지연된 무결성 검사를 제공하지만 SQL Server나 MySQL에서는 사용할 수 없습니다.

SQL Server에서 MERGE 문은 일반적으로 UNIQUE KEY/INDEX를 중단하는 행을 업데이트할 수 있습니다.(궁금해서 테스트해봤습니다.)

그러나 필요한 행과 함께 MERGE를 제공하려면 임시 테이블/변수를 사용해야 합니다.

나는 보통 내 테이블의 어떤 인덱스도 가질 수 없는 값을 생각합니다.일반적으로 고유한 열 값의 경우 정말 쉽습니다.예를 들어 '위치' 열 값(여러 요소의 순서에 대한 정보)의 경우 0입니다.

그런 다음 값 A를 변수에 복사하고 값 B로 업데이트한 다음 변수에서 값 B를 설정할 수 있습니다.두 가지 쿼리가 있지만 더 나은 해결책은 없습니다.

1) 이름에 대한 ID를 전환합니다.

id    student 

1     Abbot   
2     Doris  
3     Emerson 
4     Green  
5     Jeames  

샘플 입력의 경우 출력은 다음과 같습니다.

아이디 학생

1     Doris   
2     Abbot   
3     Green   
4     Emerson 
5     Jeames  

"n개의 행이 있는 경우 어떻게 관리할 것인가......"

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top