SQL Server는 커밋하기 전에 트랜잭션 내부의 트랜잭션 내 DDL을 트랜잭션에 허용 (표시)합니까?


9

PostgreSQL에서 테스트 데이터가있는 테이블을 만든 다음 트랜잭션 에서 테이블을 다른 유형의 새 열로 마이그레이션하여 테이블을 다시 작성합니다 COMMIT.

CREATE TABLE foo ( a int );
INSERT INTO foo VALUES (1),(2),(3);

그 다음에

BEGIN;
  ALTER TABLE foo ADD COLUMN b varchar;
  UPDATE foo SET b = CAST(a AS varchar);
  ALTER TABLE foo DROP COLUMN a;
COMMIT;

그러나 Microsoft의 SQL Server에서 동일한 내용이 오류를 생성하는 것으로 보입니다. 이 작업을 비교 DB 바이올린 1, ADD(열) 명령은 외부 거래입니다

-- txn1
BEGIN TRANSACTION;
  ALTER TABLE foo ADD b varchar;
COMMIT;

-- txn2
BEGIN TRANSACTION;
  UPDATE foo SET b = CAST( a AS varchar );
  ALTER TABLE foo DROP COLUMN a;
COMMIT;

작동하지 않는 이 db 바이올린 에

-- txn1
BEGIN TRANSACTION;
  ALTER TABLE foo ADD b varchar;
  UPDATE foo SET b = CAST( a AS varchar );
  ALTER TABLE foo DROP COLUMN a;
COMMIT;

그러나 대신 오류

Msg 207 Level 16 State 1 Line 2
Invalid column name 'b'.

어쨌든 DDL과 관련 하여이 트랜잭션을 가시화하여 PostgreSQL과 같이 작동합니까?

답변:


17

일반적으로 말해서 SQL Server는 실행 전에 현재 범위에서 전체 배치 를 컴파일 하므로 참조 된 엔터티가 있어야합니다 (나중에 문 수준 재 컴파일이 발생할 수도 있음). 주요 예외는 지연된 이름 확인 이지만 열이 아닌 테이블에 적용됩니다.

지연된 이름 확인은 존재하지 않는 테이블 개체를 참조 할 때만 사용할 수 있습니다. 저장 프로 시저가 작성 될 때 다른 모든 오브젝트가 존재해야합니다. 예를 들어, 저장 프로 시저에서 기존 테이블을 참조 할 때 해당 테이블에 존재하지 않는 열을 나열 할 수 없습니다.

일반적인 해결 방법에는 동적 코드 (Joe의 답변 에서처럼 ) 또는 DML과 DDL을 별도의 배치로 분리하는 것이 포함됩니다.

이 특정한 경우에는 다음과 같이 쓸 수도 있습니다.

BEGIN TRANSACTION;

    ALTER TABLE dbo.foo
        ALTER COLUMN a varchar(11) NOT NULL
        WITH (ONLINE = ON);

    EXECUTE sys.sp_rename
        @objname = N'dbo.foo.a',
        @newname = N'b',
        @objtype = 'COLUMN';

COMMIT TRANSACTION;

여전히 b동일한 배치 및 범위에서 이름이 바뀐 열에 액세스 할 수 없지만 작업이 완료됩니다.

SQL Server와 관련하여 트랜잭션에서 DDL과 DML을 혼합하는 것은 좋은 생각이 아니라고 생각하는 학교가 있습니다. 과거에이 작업을 수행하면 잘못된 로깅이 발생하고 데이터베이스를 복구 할 수없는 버그가있었습니다. 그럼에도 불구하고 사람들은 특히 임시 테이블을 사용합니다. 따라하기 어려운 코드가 생길 수 있습니다.


12

찾고 계십니까?

BEGIN TRANSACTION;
  ALTER TABLE foo ADD b varchar;
  EXEC sp_executesql N'UPDATE foo SET b = CAST( a AS varchar )';
  ALTER TABLE foo DROP COLUMN a;
COMMIT;

2

Paul White의 답변에 대한 "일반적으로 아니오"라는 진술에 따르면, 다음은 질문에 대한 직접적인 답변을 제공하지만 그러한 프로세스의 체계적 한계를 보여주고 쉽게 관리하고 노출시키지 않는 방법에서 멀어지게합니다. 위험.

수 DDL 당신이 DML을하고 있습니다 같은 시간을 변경하고 싶지 않아 여러 번 언급된다. 좋은 프로그래밍은 이러한 기능을 분리하여 지원 가능성을 유지하고 스파게티 스트링 변경을 피합니다.

Paul이 간결하게 지적했듯이 SQL Server는 일괄 적으로 작동 합니다 .

이제, 이것이 효과가 의심되는 사람들에게는 아마도 인스턴스에는 없지만 2017과 같은 일부 버전은 실제로 작동 할 수 있습니다! 증거는 다음과 같습니다. 여기에 이미지 설명을 입력하십시오

[테스트 코드-여러 버전의 SQL Server에서 작동하지 않을 수 있음]

USE master
GO
CREATE TABLE foo (a VARCHAR(11) )
GO
BEGIN TRANSACTION;
    INSERT INTO dbo.foo (a)
    VALUES ('entry')
/*****
[2] Check Values
*****/
    SELECT a FROM dbo.foo
/*****
[3] Add Column
*****/
    ALTER TABLE dbo.foo
        ADD b VARCHAR(11)
/*****
[3] Insert value into this new column in the same batch
-- Again, this is just an example. Please do not do this in production
*****/
    IF EXISTS (SELECT * FROM sys.columns WHERE object_ID('foo') = object_id
            AND name = 'b')
        INSERT INTO dbo.foo (b)
        VALUES ('d')
COMMIT TRANSACTION;
/*****
[4] SELECT outside transaction
-- this will fail
*****/
    --IF EXISTS (SELECT * FROM sys.columns WHERE object_ID('foo') = object_id
    --      AND name = 'b')
    --  SELECT b FROM dbo.foo
-- this will work...but a SELECT * ???
IF EXISTS (SELECT * FROM sys.columns WHERE object_ID('foo') = object_id
            AND name = 'b')
        SELECT * FROM dbo.foo

DROP TABLE dbo.foo

[결론]

예, @AndriyM 과 같은 특정 버전 또는 SQL Server 패치에 대해 동일한 배치에서 DDL 및 DML을 수행 할 수 있습니다 .SQL 2017의 dbfiddle은 지적했지만 모든 DML이 지원되는 것은 아니며 이것이 항상 그런 것은 아닙니다. 제대로 작동하면 SQL Server 버전에 수차가 생겨 패치 나 새 버전으로 마이그레이션 할 때 심각한 문제가 발생할 수 있습니다.

  • 또한 일반적으로 디자인은 변경을 예상해야합니다. 열 수정 / 추가에 대한 우려가 테이블에있을 수 있다는 것을 이해하지만이 문제를 일괄 적으로 적절하게 디자인 할 수 있습니다.

[추가 신용]

EXISTS 문에 관해서는 Paul이 언급했듯이 코드의 다음 단계로 넘어 가기 전에 코드를 확인하는 다른 방법이 많이 있습니다.

  • EXISTS 문을 사용하면 모든 버전의 SQL Server에서 작동하는 코드를 만들 수 있습니다.
  • 하나의 명령문에서 복잡한 검사를 허용하는 부울 함수

아니요, 열 을 만드는 동일한 배치에서이 작업을 수행하는 경우 새 열에 삽입 할 수 없습니다 . 더 일반적으로, 방금 생성 한 후 동일한 배치에서 새 열을 정적으로 참조 할 수 없습니다. 이 경우 IF EXISTS 트릭이 작동하지 않습니다. DML을 동적으로 호출하거나 다른 배치로 수행하십시오.
— Andriy M

@AndriyM 죄송합니다. dbfiddle에 대해 잘못 설명하셨습니다. 그러나 당신은 당신의 인스턴스에서 이것을 시도 했습니까? 2017 SP1에서 작동합니다. GIF를 업로드 할 것이지만 시스템에서 테스트 했습니까?
— clifton_h

i.imgur.com/fhAC7lB.png 실제로 binsert 문 아래의 물결 선을 기준으로 컴파일되지 않는다는 것을 알 수 있습니다 . 저는 SQL Server 2014를 사용하고 있습니다.
— Andriy M

@AndriyM 재미있는. 나는 이것이 이전에 효과에 영향을 미치는 것을 보았으며 언급 한 SQL Server 2017과 같은 일부 버전의 SQL Server에서 작동하는 것으로 보입니다.
— clifton_h 2014 년

@AndriyM는 게시물에 대한 새로운 편집 참조
— clifton_h
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.