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.