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.