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 GOTOLABEL문을 통합하여 코드를 건너 뛰고 GO키워드를 그대로 둘 수 있습니다.


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