TSQL - วิธีใช้ GO ภายใน BEGIN .. END block?


100

ฉันกำลังสร้างสคริปต์สำหรับย้ายการเปลี่ยนแปลงโดยอัตโนมัติจากฐานข้อมูลการพัฒนาหลายฐานข้อมูลไปยังการจัดเตรียม / การผลิต โดยทั่วไปจะใช้สคริปต์การเปลี่ยนแปลงจำนวนมากและรวมเข้าด้วยกันเป็นสคริปต์เดียวโดยรวมแต่ละสคริปต์ไว้ใน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
@gbn: ใช่ฉันรู้ว่าทำไมสิ่งนี้จึงเกิดขึ้น(ดูย่อหน้าที่สอง) ; แต่ผมไม่มีความคิดวิธีการทำงานรอบ ๆ มัน - ฉันจริงๆต้องเปิดทุกแบบสอบถามออกเป็นพวงของสตริง !?
— BlueRaja - Danny Pflughoeft

@BlueRaja: กังวลอะไร? หากได้ผลนั่นคือสิ่งที่สำคัญในตอนท้ายของวัน หากมีปัญหาทางธุรกิจที่ถูกต้องเกี่ยวกับโซลูชันที่ให้มาโปรดแจ้งให้ทราบ มีบางอย่างที่ทำให้สับสนเป็นพิเศษเกี่ยวกับการแปลงแบบสอบถามทั้งหมดให้เป็นสตริงจำนวนมากหรือไม่?
— mellamokb

2
@mellamokb: ใช่มีปัญหา; หากคำว่า GO ถูกใช้ในบริบทอื่น ๆ (เช่นความคิดเห็นหรือสตริง) สคริปต์จะไม่ทำงาน นอกจากนี้เราสูญเสียหมายเลขบรรทัดที่มีประโยชน์ในข้อความแสดงข้อผิดพลาดในกรณีที่มีสิ่งผิดปกติเกิดขึ้น ไม่มีทางทำธุรกรรมได้หรือไม่? หรือลอง / จับ?
— BlueRaja - Danny Pflughoeft

@BlueRaja: 1) ผมเชื่อว่าจะต้องมีในสายด้วยตัวเองเพื่อให้คุณสามารถค้นหากรณีที่ว่าเท่านั้นและไม่ได้ตัวอย่างของคำทุกGO GO2) คุณสามารถบันทึกได้ตลอดเวลาว่าข้อความใดเสร็จสมบูรณ์ หรือคุณสามารถสรุปข้อมูลทั้งหมดด้วยการลอง / จับและใช้หมายเลขบรรทัดของคุณเองโดยใช้ตัวแปรบางตัวเช่น @lineNo ที่คุณติดตามและรายงานข้อผิดพลาด เนื่องจากคุณกำลังสร้างสิ่งเหล่านี้โดยอัตโนมัติการเปลี่ยนแปลงเช่นนี้จึงเป็นเรื่องง่าย ดูเหมือนว่าคุณจะไม่ต้องการสำรวจเส้นทางนี้เมื่อฉันคิดว่ามีวิธีแก้ปัญหาที่คุณกังวล
— mellamokb

คำตอบ:


49

ฉันมีปัญหาเดียวกันและในที่สุดก็มีการจัดการที่จะแก้ปัญหาโดยใช้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! นี่เป็นคำตอบที่ใช้ได้จริงเท่านั้นจนถึงตอนนี้สำหรับใช้ในSQLCMDสคริปต์โหมดSS (เช่นสคริปต์การปรับใช้หลัก) ที่เรียกใช้ (ผ่าน:rคำสั่ง) สคริปต์ SS อื่น ๆ (เช่นสคริปต์การปรับใช้ย่อย) พร้อมกับการเรียกบางส่วนในifคำสั่ง คำตอบของ Oded, mellamokb และ Andy Joiner ของการแนบข้อความทั้งหมดในการexecโทร / begin- endไม่ใช่การเริ่มต้น นอกจากนี้begin- endวิธีการจะไม่ทำงานหากมีcreateคำชี้แจง (เช่นต้องระบุอย่างชัดเจนgoก่อนหน้านั้น) แต่ผู้ชาย "โฮลี่เนกาทีฟแบทแมน! ;)
— ทอม

เพื่อความสามารถในการอ่าน (เช่นเพื่อช่วยเอาชนะ double-Negative และทำให้ชัดเจนขึ้นว่ากำลังจำลองบล็อกเสมือนจริง if ) ฉันจะนำหน้าบล็อกด้วย-- If whateverความคิดเห็นเยื้องบล็อกและแก้ไขบล็อกด้วย--end If whateverความคิดเห็น
— ทอม

คุณช่วยเบคอนของฉัน! ฉันใช้คำสั่งผสานและ GO โง่พวกนั้นไม่ชอบอยู่ใน IF BEGIN END ELSE
— Omzig

หืมฉันได้รับข้อผิดพลาดในการอัปเดตหลังจากที่ set noexec on ถูกเรียกใช้งานแล้ว? (ข้อผิดพลาดที่ชื่อคอลัมน์ที่จะอัปเดตไม่ถูกต้อง) ทำงานบน MSSQL 2014 ในตัวแก้ไขแบบสอบถาม ใช้งานได้ดีหากเงื่อนไขเปลี่ยนเป็นเท็จ (ดังนั้น noexec ยังคงปิดอยู่)
— Jerry

44

GO ไม่ใช่ SQL - เป็นเพียงตัวคั่นชุดงานที่ใช้ในเครื่องมือ MS SQL บางตัว

หากคุณไม่ใช้สิ่งนั้นคุณต้องตรวจสอบให้แน่ใจว่ามีการดำเนินการคำสั่งแยกกันไม่ว่าจะเป็นชุดงานที่แตกต่างกันหรือโดยใช้ Dynamic 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: จะ;ช่วยได้ไหม - คุณเพิ่งแก้ไขคำตอบของคุณ: o)
— Neil Knight

@ นีล - นี่คือความคิดของฉันใช่
— Oded

;ก็ใช้ไม่ได้เช่นกัน - โปรแกรมแยกวิเคราะห์ยังคงให้"ชื่อคอลัมน์ไม่ถูกต้อง" EMP_IS_ADMIN ""
— BlueRaja - Danny Pflughoeft

เมื่อคอมไพล์แบตช์แล้ว EMP_IS_ADMIN จะไม่มีอยู่ stackoverflow.com/questions/4855537/…
— gbn

17

คุณสามารถลอง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

@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

9

ในที่สุดฉันก็ทำให้มันใช้งานได้โดยแทนที่ทุกอินสแตนซ์GOในบรรทัดของมันเองด้วย

END
GO

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

วิธีนี้ดีกว่าอย่างมากในการตัดคำสั่งทุกกลุ่มในสตริง แต่ก็ยังห่างไกลจากอุดมคติ หากใครพบวิธีแก้ไขที่ดีกว่าโพสต์และฉันจะรับมันแทน


6
หากเงื่อนไขแรกคือ "ถ้าไม่มีคอลัมน์นี้" คำสั่งแรกในบล็อกคือ "เพิ่มคอลัมน์นี้" จากนั้นการตรวจสอบเงื่อนไขครั้งที่สองจะพบคอลัมน์และไม่ดำเนินการคำสั่งที่สอง
— Damien_The_Unbeliever

@Damien: จริง; โชคดีที่จะไม่เกิดขึ้นในกรณีของฉัน (เงื่อนไขคือการตรวจสอบค่าเฉพาะในตารางเฉพาะซึ่งจะถูกเพิ่มเป็นคำสั่งสุดท้ายของIFบล็อกเสมอ) ดูเหมือนว่าจะไม่มีวิธีที่ดีในการทำใน SQL
— BlueRaja - Danny Pflughoeft

set noexecคำตอบของ Mina Jacob เป็นคำตอบที่ใช้ได้จริงเท่านั้นจนถึงตอนนี้สำหรับการใช้งานในSQLCMDสคริปต์โหมดSS (เช่นสคริปต์การปรับใช้หลัก) ที่เรียก (ผ่าน:rคำสั่ง) สคริปต์ SS อื่น ๆ (เช่นสคริปต์การปรับใช้งานย่อย) ด้วยการเรียกบางส่วนภายในifคำสั่ง คำตอบของ Oded, mellamokb และ Andy Joiner ของการแนบข้อความทั้งหมดในการexecโทร / begin- endไม่ใช่การเริ่มต้น นอกจากนี้begin- endวิธีการจะไม่ทำงานหากมีcreateคำชี้แจง (เช่นต้องระบุอย่างชัดเจนgoก่อนหน้านั้น)
— ทอม

8

คุณสามารถใส่คำสั่งใน BEGIN และ END แทน GO ในระหว่าง

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 batching ทำงานให้ฉันโดยใช้ SQL Server 2016 IMO นี่เป็นไวยากรณ์ที่สะอาดที่สุด
— Uber Schnoz

set noexecคำตอบของ Mina Jacob เป็นคำตอบที่ใช้ได้จริงเท่านั้นจนถึงตอนนี้สำหรับการใช้งานในSQLCMDสคริปต์โหมดSS (เช่นสคริปต์การปรับใช้หลัก) ที่เรียก (ผ่าน:rคำสั่ง) สคริปต์ SS อื่น ๆ (เช่นสคริปต์การปรับใช้งานย่อย) ด้วยการเรียกบางส่วนภายในifคำสั่ง คำตอบของ Oded, mellamokb และ Andy Joiner ของการแนบข้อความทั้งหมดในการexecโทร / begin- endไม่ใช่การเริ่มต้น นอกจากนี้begin- endวิธีการจะไม่ทำงานหากมีcreateคำชี้แจง (เช่นต้องระบุอย่างชัดเจนgoก่อนหน้านั้น)
— ทอม

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คำหลักไม่เสียหาย


6
ดูเหมือนว่า LABEL จะไม่สามารถอ้างอิงในคำสั่ง GO ได้เนื่องจากไม่ได้อยู่ในชุดที่ส่งไปยัง SQL
— berkeleybross
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.