TSQL-BEGIN .. END 블록 내에서 GO를 사용하는 방법?


96

여러 개발 데이터베이스의 변경 사항을 스테이징 / 프로덕션으로 자동 마이그레이션하는 스크립트를 생성하고 있습니다. 기본적으로 많은 변경 스크립트를 사용하여 단일 스크립트로 병합하여 각 스크립트를 IF whatever BEGIN ... END명령문으로 래핑 합니다.

그러나 일부 스크립트에는 GO예를 들어 SQL 파서가 새 열이 생성 된 후 새 열에 대해 알 수 있도록 문이 필요합니다 .

ALTER TABLE dbo.EMPLOYEE 
ADD COLUMN EMP_IS_ADMIN BIT NOT NULL
GO -- Necessary, or next line will generate "Unknown column:  EMP_IS_ADMIN"
UPDATE dbo.EMPLOYEE SET EMP_IS_ADMIN = whatever

그러나 일단 IF블록으로 감싸면 다음과 같습니다.

IF whatever
BEGIN
    ALTER TABLE dbo.EMPLOYEE ADD COLUMN EMP_IS_ADMIN BIT NOT NULL
    GO
    UPDATE dbo.EMPLOYEE SET EMP_IS_ADMIN = whatever
END

BEGIN일치하지 않는를 보내고 있기 때문에 실패합니다 END. 그러나 내가 제거 GO하면 알 수없는 열에 대해 다시 불평합니다.

단일 IF블록 내에서 동일한 열을 만들고 업데이트하는 방법이 있습니까?


2
참조 stackoverflow.com/questions/4855537/...을 하시기 바랍니다
— GBN

2
@gbn : 예, 왜 이런 일이 발생하는지 압니다 (두 번째 단락 참조) . 하지만 난 그것을 해결하려면 방법에 아무 생각이 없다 - 내가 않는 정말 문자열의 무리로 모든 쿼리를 설정해야합니다을!?
— BlueRaja-Danny Pflughoeft 2011-06-16

@BlueRaja : 문제가 무엇입니까? 작동한다면 하루가 끝날 때 그게 전부입니다. 제공된 솔루션에 합법적 인 비즈니스 문제가있는 경우이를 표현하십시오. 모든 쿼리를 일련의 문자열로 변환하는 것에 대해 특별히 당황스러운 것이 있습니까?
— mellamokb

1
@mellamokb : 예, 문제가 있습니다. GO라는 단어가 다른 컨텍스트 (예 : 주석 또는 문자열)에서 사용되는 경우 스크립트가 작동하지 않습니다. 또한 문제가 발생하면 오류 메시지에서 유용한 줄 번호를 잃게됩니다. 거래를 할 수있는 방법이 없나요? 아니면 시도 / 캐치?
— BlueRaja-Danny Pflughoeft 2011-06-16

@BlueRaja : 1) GO그 자체로 한 줄에 있어야 한다고 생각 합니다. 따라서 단어의 모든 인스턴스가 아닌 해당 케이스 만 검색 할 수 있습니다 GO. 2) 어떤 문장이 성공적으로 완료되었는지 항상 기록 할 수 있습니다. 또는 모든 것을 try / catch로 래핑하고 @lineNo와 같은 일부 변수를 사용하여 자신의 줄 번호를 사용하여 추적하고 오류를보고 할 수 있습니다. 자동으로 생성되므로 이와 같이 변경하는 것은 매우 쉽습니다. 모든 우려 사항에 대한 해결책이 있다고 생각할 때이 경로를 탐색하고 싶지 않은 것 같습니다.
— mellamokb 2011 년

답변:


45

나는 같은 문제가 있었고 마침내 SET NOEXEC를 사용하여 해결했습니다 .

IF not whatever
BEGIN
    SET NOEXEC ON; 
END

ALTER TABLE dbo.EMPLOYEE ADD COLUMN EMP_IS_ADMIN BIT NOT NULL
GO
UPDATE dbo.EMPLOYEE SET EMP_IS_ADMIN = whatever

SET NOEXEC OFF; 

2
이것은 훌륭한 솔루션입니다!
— Bazinga

+1! 이것은 Statements 내부의 일부 호출과 함께 다른 SS 스크립트 (즉, 하위 배포 스크립트)를 호출 ( 명령을 통해) 하는 SS 모드 스크립트 (예 : 마스터 배포 스크립트) 에서 사용하기위한 유일한 실용적인 답변 입니다. 오뎃의, mellamokb의 및 둘러싸의 앤디 목공 답변 모두에게 있는 그 문 전화는 / - 의 비 우선이다. 또한 - 메서드는 Statement 가있는 경우 작동하지 않습니다 (예 : 명시 적 선행이 필요함 ). 하지만, "신성한 이중 네거티브, 배트맨!" ;)SQLCMD:rifexecbeginendbeginendcreatego
— Tom

가독성을 위해 (예 : 이중 음수를 극복하고 가상 if 블록을 시뮬레이션하고 있음을 더 명확하게하기 위해 ) 블록 앞에 -- If whatever주석을 붙이고, 블록을 들여 쓰기하고, 블록 뒤에 주석을 붙 --end If whatever입니다.
— 톰

당신이 내 베이컨을 구 했어요! 나는 merge 문을 실행하고 있었고 그 바보 GO는 IF BEGIN END ELSE 안에있는 것을 좋아하지 않는다
— Omzig

흠, set noexec on이 실행 된 후 어떻게 든 업데이트에서 오류가 발생합니까? (업데이트 할 열 이름이 잘못되었다는 오류) 쿼리 편집기에서 MSSQL 2014에서 실행 중입니다. 조건이 거짓으로 바뀌면 정상적으로 작동합니다 (따라서 noexec가 꺼져 있음)
— Jerry

43

GO SQL이 아닙니다. 일부 MS SQL 도구에서 사용되는 배치 구분 기호 일뿐입니다.

이를 사용하지 않으면 명령문이 개별적으로 실행되는지 확인해야합니다. 다른 배치에서 또는 모집단에 대한 동적 SQL을 사용하여 (@gbn에게 감사드립니다) :

IF whatever
BEGIN
    ALTER TABLE dbo.EMPLOYEE ADD COLUMN EMP_IS_ADMIN BIT NOT NULL;

    EXEC ('UPDATE dbo.EMPLOYEE SET EMP_IS_ADMIN = whatever')
END

8
네, 이해합니다. 이것은 질문에 대한 답이 아닙니다 IF. 동일한 블록 에서 열을 만들고 업데이트해야합니다 .
— BlueRaja-Danny Pflughoeft

@Oded : ;여기서 도움이 될까요? ) 오 : - 당신은 당신의 대답을 편집 한
— 닐 기사에게

@Neil-이것은 내 생각입니다.
— Oded

;작동하지 않습니다-파서가 여전히 "잘못된 열 이름 'EMP_IS_ADMIN'"을 제공합니다.
— BlueRaja-Danny Pflughoeft 2011-06-16

배치가 컴파일 될 때 EMP_IS_ADMIN이 존재하지 않습니다. stackoverflow.com/questions/4855537/…
— gbn 2011-06-16

16

당신은 시도해 볼 수도 있습니다 sp_executesql각각의 내용을 분할, GO아래 예제와 같이 별도의 문자열로 문을 실행 할 수 있습니다. 또한 예외가 발생한 위치를 쉽게 디버깅 할 수 있도록 실행중인 문을 추적하는 @statementNo 변수가 있습니다. 줄 번호는 오류를 일으킨 관련 명령문 번호의 시작 부분을 기준으로합니다.

BEGIN TRAN

DECLARE @statementNo INT
BEGIN TRY
    IF 1=1
    BEGIN
        SET @statementNo = 1
        EXEC sp_executesql
            N'  ALTER TABLE dbo.EMPLOYEE
                    ADD COLUMN EMP_IS_ADMIN BIT NOT NULL'

        SET @statementNo = 2
        EXEC sp_executesql
            N'  UPDATE dbo.EMPLOYEE
                    SET EMP_IS_ADMIN = 1'

        SET @statementNo = 3
        EXEC sp_executesql
            N'  UPDATE dbo.EMPLOYEE
                    SET EMP_IS_ADMIN = 1x'
    END
END TRY
BEGIN CATCH
    PRINT 'Error occurred on line ' + cast(ERROR_LINE() as varchar(10)) 
       + ' of ' + 'statement # ' + cast(@statementNo as varchar(10)) 
       + ': ' + ERROR_MESSAGE()
    -- error occurred, so rollback the transaction
    ROLLBACK
END CATCH
-- if we were successful, we should still have a transaction, so commit it
IF @@TRANCOUNT > 0
    COMMIT

또한 위의 예에서 설명한 것처럼 작은 따옴표 ( ') 로 묶어 여러 줄 문을 쉽게 실행할 수 있습니다 . ''스크립트를 생성 할 때 문자열 안에 포함 된 작은 따옴표를 큰 따옴표 ( ) 로 이스케이프하는 것을 잊지 마십시오 .


여러 줄로 분할 된 명령에 대해 이것이 작동 할 것이라고 생각하지 않습니까?
— BlueRaja-Danny Pflughoeft 2011-06-16

@BlueRaja : 어떻게 작동하는지 보여주기 위해 예제를 업데이트했습니다. 이중 작은 따옴표 ( '')를 사용하여 내부에 포함 된 작은 따옴표 ( ')를 이스케이프하는 한 이러한 문자열은 여러 줄이 될 수 있습니다.
— mellamokb

1
@mellamokb : 엄밀히 말하면 UPDATE에만 sp_executesql이 필요합니다 ... stackoverflow.com/questions/4855537/…
— gbn

1
@gbn : 맞습니다. 그러나 100 개의 문에 대해이를 자동화하려는 경우 필요한시기와 위치를 결정하는 대신 모든 문에 맹목적으로 적용하는 것이 더 쉬울 것입니다.
— mellamokb

@gbn @mellamokb : 나는 SELECT * <newline> FROM whatever. 자체 EXEC 문으로 모든 줄을 실행하면 중단됩니다. 아니면 내가 모든 GO진술을 중단하라고 제안하는 건가요?
— BlueRaja-Danny Pflughoeft 2011-06-16

9

궁극적으로 GO자체 라인 의 모든 인스턴스 를

END
GO

---Automatic replacement of GO keyword, need to recheck IF conditional:
IF whatever
BEGIN

이 문자열에 문의 모든 그룹을 포장에 매우 바람직하지만 여전히 멀리 이상적에서. 누구든지 더 나은 해결책을 찾으면 게시하고 대신 수락하겠습니다.


6
첫 번째 조건이 "if this column does not exist"인 경우 블록의 첫 번째 문은 "add this column"이며, 조건의 두 번째 검사는 열을 찾고 두 번째 문을 실행하지 않습니다.
— Damien_The_Unbeliever

@Damien : 사실; 다행히도 제 경우에는 이런 일이 발생하지 않습니다 (조건부는 항상 특정 테이블의 특정 값에 대한 검사이며 항상 IF블록 의 마지막 문으로 추가됨 ). SQL에서 이것을 수행하는 좋은 방법이없는 것처럼 보입니다.
— BlueRaja - 대니 Pflughoeft

Mina Jacob 's set noexecAnswer는 지금까지 Statements 내부의 일부 호출과 함께 다른 SS 스크립트 (예 : 하위 배포 스크립트)를 호출 하는 SS 모드 스크립트 (예 : 마스터 배포 스크립트) 에서 사용할 수 있는 유일한 실용적인 답변 입니다. 오뎃의, mellamokb의 및 둘러싸의 앤디 목공 답변 모두에게 있는 그 문 전화는 / - 의 비 우선이다. 또한 - 메서드는 Statement 가있는 경우 작동하지 않습니다 (예 : 명시 적 선행이 필요함 ). SQLCMD:rifexecbeginendbeginendcreatego
— Tom

8

GO inbetween 대신 BEGIN 및 END로 명령문을 묶을 수 있습니다.

IF COL_LENGTH('Employees','EMP_IS_ADMIN') IS NULL --Column does not exist
BEGIN
    BEGIN
        ALTER TABLE dbo.Employees ADD EMP_IS_ADMIN BIT
    END

    BEGIN
        UPDATE EMPLOYEES SET EMP_IS_ADMIN = 0
    END
END

(Northwind 데이터베이스에서 테스트 됨)

편집 : (아마 SQL2012에서 테스트 됨)


1
-1에 대한 이유를 알려주세요
— Andy Joiner

1
왜 반대표를 받았는지 모르겠네요 ... 제겐 매력처럼 작동합니다.
— Thorarin

10
SQL Server 2008 R2를 사용하면이 기능이 작동하지 않는 것 같습니다. 여전히 '잘못된 열 이름'EMP_IS_ADMIN '이라는 오류가 발생합니다. UPDATE 라인에서.
— MerickOWA

BEGIN-END 일괄 처리는 SQL Server 2016을 사용하여 저에게 효과적이었습니다. IMO 이것은 가장 깨끗한 구문입니다.
— Uber Schnoz

Mina Jacob 's set noexecAnswer는 지금까지 Statements 내부의 일부 호출과 함께 다른 SS 스크립트 (예 : 하위 배포 스크립트)를 호출 하는 SS 모드 스크립트 (예 : 마스터 배포 스크립트) 에서 사용할 수 있는 유일한 실용적인 답변 입니다. 오뎃의, mellamokb의 및 둘러싸의 앤디 목공 답변 모두에게 있는 그 문 전화는 / - 의 비 우선이다. 또한 - 메서드는 Statement 가있는 경우 작동하지 않습니다 (예 : 명시 적 선행이 필요함 ). SQLCMD:rifexecbeginendbeginendcreatego
— Tom

1

이 솔루션을 시도해 볼 수 있습니다.

if exists(
SELECT...
)
BEGIN
PRINT 'NOT RUN'
RETURN
END

--if upper code not true

ALTER...
GO
UPDATE...
GO

1
if-else 블록이 여러 개있는 경우별로 유용하지 않습니다.
— Jerry

0

나는 RAISERROR이것을 위해 과거에 사용했습니다

IF NOT whatever BEGIN
    RAISERROR('YOU''RE ALL SET, and sorry for the error!', 20, -1) WITH LOG
END

ALTER TABLE dbo.EMPLOYEE ADD COLUMN EMP_IS_ADMIN BIT NOT NULL
GO
UPDATE dbo.EMPLOYEE SET EMP_IS_ADMIN = whatever

-1

a GOTO및 LABEL문을 통합하여 코드를 건너 뛰고 GO키워드를 그대로 둘 수 있습니다.


5
그들이 SQL로 전송 일괄하지 않기 때문에 레이블 GO 문을 통해 참조 할 수 없습니다 것 같다
— berkeleybross
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.