เป็นไปได้หรือไม่ที่จะเลือก RAISERROR หรือ THROW ขึ้นอยู่กับรุ่นของ SQL Server


11

นี่คือรหัสของฉันตอนนี้:

BEGIN TRY
INSERT INTO TABLE (F1,F2,F3) 
VALUES ('1','2','3')
END TRY
BEGIN CATCH
;THROW
END CATCH

ใช้งานได้ดีเว้นแต่ว่ามันจะทำงานบนเครื่องที่มี SQL 2008 ฉันต้องการให้ CATCH block ทำการตรวจสอบกับเวอร์ชันของ SQL และเรียกใช้ THROW ถ้ามันเท่ากับหรือสูงกว่าถึง 2012 และ RAISERROR ถ้าเป็น 2008 ฉันจะทำงานต่อไป ข้อผิดพลาดทางไวยากรณ์และฉันสงสัยว่ามันเป็นไปได้ แม้แต่สิ่งที่เรียบง่ายเช่นนี้ก็ไม่ได้ผลสำหรับฉัน

BEGIN CATCH
IF ((SELECT SERVERPROPERTY('productversion')) >= 11) ;THROW
END CATCH

คำแนะนำใด ๆ ที่ชื่นชม

คำตอบ:


9

ไม่เป็นไปไม่ได้

นี่เป็นไวยากรณ์ที่ไม่ถูกต้องในรุ่นก่อนหน้าและจะทำให้เกิดข้อผิดพลาดในการรวบรวม

มันเป็นไปไม่ได้ที่จะซ่อนTHROWในEXECด้านในของบล็อก catch ไม่ว่าจะเป็นการโยนแบบไม่มีพารามิเตอร์จะต้องมีอยู่ภายใน catch

คุณจะต้องปรับใช้รุ่นรหัสที่คุณต้องการตามรุ่นของ SQL Server ที่คุณกำลังปรับใช้ (และน่าเสียดายที่ไม่มีการสนับสนุนที่ดีสำหรับเรื่องนี้ในการใช้เครื่องมือ SSDT ที่ฉันรู้ - ไม่เท่ากับการรวมรหัสบรรทัดที่เลือกผ่าน การรวบรวมตามเงื่อนไข)


4

มันควรจะชี้ให้เห็นว่าแม้ว่ามันจะเป็นไปได้ในทางเทคนิคในการสลับระหว่างTHROWและRAISERRORคุณ (ส่วนใหญ่) จะไม่ต้องการทำสิ่งนี้จริง ๆ ทำไม? เนื่องจากความสามารถที่ดีของผู้ไม่ใช้พารามิเตอร์THROWในการโยนข้อผิดพลาดอีกครั้งโดยใช้หมายเลขข้อความเดียวกัน (เช่นMsg 8134แทนที่จะเป็นMsg Xที่X> = 50000) ไม่ใช่ความแตกต่างเพียงอย่างเดียวระหว่างพวกเขาTHROWนั่นคือการยกเลิกแบทช์ในขณะที่RAISERRORไม่ใช่ นี่อาจเป็นความแตกต่างด้านพฤติกรรมที่สำคัญดังแสดงด้านล่าง

ทดสอบการตั้งค่า

--DROP PROC ##Throw;
--DROP PROC ##RaisError;

GO
CREATE PROCEDURE ##Throw
AS
SET NOCOUNT ON;
BEGIN TRY
  SELECT 1/0 AS [DivideByZero];
END TRY
BEGIN CATCH
  THROW;
END CATCH;
SELECT 1 AS [AA];
GO

CREATE PROCEDURE ##RaisError
AS
SET NOCOUNT ON;
BEGIN TRY
  SELECT 1/0 AS [DivideByZero];
END TRY
BEGIN CATCH
  RAISERROR('test, yo!', 16, 1);
  -- RETURN; -- typically at end of CATCH block when using RAISERROR
END CATCH;
SELECT 2 AS [BB];
GO

ทดสอบ 1

EXEC ##Throw;
SELECT 3 AS [CC];

ผลตอบแทน:

"Results" Tab:

DivideByZero
{empty result set}

"Messages" Tab:

Msg 8134, Level 16, State 1, Procedure ##Throw, Line 38
Divide by zero error encountered.

ทดสอบ 2

EXEC ##RaisError;
SELECT 4 AS [DD];

ผลตอบแทน:

"Results" Tab:

DivideByZero
{empty result set}

BB
2

DD
4

"Messages" Tab:

Msg 50000, Level 16, State 1, Procedure ##RaisError, Line 45
test, yo!

เพื่อความเป็นธรรมมันเป็นไปได้ที่จะปกปิดความแตกต่างนี้โดยทำดังต่อไปนี้:

  • เสมอห่อทุกสายที่จะใช้รหัสTHROWภายในTRY...CATCHโครงสร้าง (แสดงให้เห็นถึงด้านล่าง)
  • อย่าวางโค้ดหลังจากTHROW(ดียกเว้นEND CATCH;)

ทดสอบ 3

BEGIN TRY
  EXEC ##Throw;
  SELECT 5 AS [EE];
END TRY
BEGIN CATCH
  SELECT ERROR_NUMBER() AS [ErrorNumber], ERROR_MESSAGE() AS [ErrorMessage];
END CATCH;
SELECT 6 AS [FF];
GO

ผลตอบแทน:

"Results" Tab:

DivideByZero
{empty result set}

ErrorNumber     ErrorMessage
8134            Divide by zero error encountered.

FF
6

ทดสอบ 4

BEGIN TRY
  EXEC ##RaisError;
  SELECT 7 AS [GG];
END TRY
BEGIN CATCH
  SELECT ERROR_NUMBER() AS [ErrorNumber], ERROR_MESSAGE() AS [ErrorMessage];
END CATCH;
SELECT 8 AS [HH];
GO

ผลตอบแทน:

"Results" Tab:

DivideByZero
{empty result set}

ErrorNumber     ErrorMessage
50000           test, yo!

HH
8

3

ฉันเชื่อว่าคำตอบของมาร์ตินสมิ ธนั้นเกือบ 100% ถูกต้อง

วิธีเดียวในการทำเช่นนี้คือกับ SQL แบบไดนามิกและคุณจะต้องทำซ้ำโค้ดจำนวนมากโดยการห่อบล็อคลอง / catch ทั้งหมดของคุณ (หรือคำสั่งสร้างโพรซีเดอร์ทั้งหมดหากคุณจะมีสองเวอร์ชันของทั้งหมด เหล่านั้น) ที่ทำงานขึ้นอยู่กับรุ่น

นั่นจะเป็นฝันร้ายที่ต้องรักษา อย่าทำมัน

มีวิธีดำเนินการคำสั่ง SQL ตามรุ่นของ SQL Server หรือไม่?

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