วิธีบันทึกรายละเอียดข้อผิดพลาดเมื่อใช้ลอง / จับสำหรับคำสั่งสำรอง SQL แบบไดนามิก


10

เมื่อออกคำสั่งสำรองข้อมูลภายในโพรซีเดอร์ที่เก็บไว้ซึ่งใช้ try catch และ dynamic sql ข้อความแสดงข้อผิดพลาดทั่วไปมากเมื่อเปรียบเทียบกับการรันคำสั่ง backup โดยตรง

ลอง / จับภายใน SP:

    begin try
        execute sp_executesql @sql;  -- a backup command
    end try
    begin catch  
        print ERROR_MESSAGE();  -- save to log, etc.
    end catch

ผลลัพธ์ใน

50000: usp_Backup: 117: BACKUP DATABASE สิ้นสุดลงอย่างผิดปกติ

ในขณะที่การออกคำสั่ง raw:

    backup DATABASE someDb to disk...

ผลลัพธ์โดยละเอียดยิ่งขึ้น:

ข้อผิดพลาดการค้นหา - ข้อผิดพลาดของฐานข้อมูลเซิร์ฟเวอร์ SQL: เกิดข้อผิดพลาด I / O ที่ไม่สามารถกู้คืนได้ในไฟล์ "H: \ FolderName \ Filename.bak:" 112 (มีเนื้อที่ไม่เพียงพอในดิสก์)

มีวิธีการจับรายละเอียดเหล่านี้เป็นตัวแปรภายในกระบวนงานที่เก็บไว้ (เพื่อเข้าสู่ระบบส่งกลับไปยังผู้โทรสำหรับตรรกะลองใหม่)? ดูเหมือนว่ารายละเอียดจะมาถึงในช่องข้อความ แต่ฉันต้องการให้พวกเขามีอยู่ใน SP


คุณอาจต้องการเห็นสิ่งนี้: stackoverflow.com/questions/5966670/…
8kb

คำตอบ:


13

เมื่อBACKUP DATABASEสร้างข้อผิดพลาดมันจะสร้างขึ้นสองอัน น่าเสียดายที่TRY/CATCHไม่สามารถจับข้อผิดพลาดแรกได้ มันจับข้อผิดพลาดที่สองเท่านั้น

ฉันสงสัยว่าทางออกที่ดีที่สุดของคุณในการจับภาพเหตุผลที่แท้จริงที่อยู่เบื้องหลังการสำรองข้อมูลที่ล้มเหลวคือการสำรองข้อมูลอัตโนมัติของคุณผ่านSQLCMD (ที่มี-oการส่งออกไปยังแฟ้ม) SSIS, C #, PowerShell ฯลฯ ซึ่งทั้งหมดนี้จะช่วยให้คุณสามารถควบคุมได้มากขึ้นกว่าการจับทั้งหมดของข้อผิดพลาด

คำตอบ SO ในความคิดเห็นแสดงให้เห็นการใช้DBCC OUTPUTBUFFER- ในขณะที่เป็นไปได้ดูเหมือนจะไม่เหมือนเด็กทุกคน รู้สึกอิสระที่จะได้สนุกกับขั้นตอนนี้จากเว็บไซต์ Erland Sommarskog ของTRY/CATCHแต่ตอนนี้ยังไม่ได้ดูเหมือนจะทำงานได้ดีในการทำงานร่วมกับ

วิธีเดียวที่ฉันดูเหมือนจะสามารถจับข้อความแสดงข้อผิดพลาดได้spGET_LastErrorMessageคือถ้าเกิดข้อผิดพลาดจริง หากคุณห่อไว้ในTRY/CATCHข้อผิดพลาดที่ได้รับการกลืนและขั้นตอนการจัดเก็บไม่ทำอะไรเลย:

BEGIN TRY
  EXEC sp_executesql N'backup that fails...';
END TRY
BEGIN CATCH
  EXEC dbo.spGet_LastErrorMessage;
END CATCH

ใน SQL Server <2012 คุณไม่สามารถยกข้อผิดพลาดซ้ำได้ แต่คุณสามารถทำได้ใน SQL Server 2012 และใหม่กว่า ดังนั้นรูปแบบทั้งสองนี้จึงทำงานได้:

CREATE PROCEDURE dbo.dothebackup
AS
BEGIN
  SET NOCOUNT ON;
  EXEC sp_executesql N'backup that fails...';
END
GO

EXEC dbo.dothebackup;
EXEC dbo.spGET_LastErrorMessage;

หรือในปี 2012 และเหนือกว่านี้ได้ผล แต่ในระดับมากก็สามารถเอาชนะวัตถุประสงค์ได้TRY/CATCHเนื่องจากข้อผิดพลาดดั้งเดิมยังคงถูกโยนทิ้ง:

CREATE PROCEDURE dbo.dothebackup2
AS
BEGIN
  SET NOCOUNT ON;
  BEGIN TRY
    EXEC sp_executesql N'backup that fails...';
  END TRY
  BEGIN CATCH
    THROW;
  END CATCH
END
GO

EXEC dbo.dothebackup2;
EXEC dbo.spGET_LastErrorMessage;

ในทั้งสองกรณีนี้ข้อผิดพลาดยังคงถูกส่งไปยังไคลเอนต์แน่นอน ดังนั้นหากคุณกำลังใช้TRY/CATCHเพื่อหลีกเลี่ยงปัญหานั้นเว้นแต่จะมีช่องโหว่บางอย่างที่ฉันไม่ได้คิดฉันกลัวว่าคุณจะต้องเลือก ... ให้ผู้ใช้เกิดข้อผิดพลาดและสามารถจับรายละเอียดเกี่ยวกับ หรือระงับทั้งข้อผิดพลาดและเหตุผลที่แท้จริง


ไร้สาระอย่างที่เป็นอยู่วิธีการ Sommarskog ไม่ได้ดูเหมือนคำถามถ้าฉันแค่ต้องการให้บริบทบางอย่างแก่ผู้โทรภายในอินเทอร์เฟซ ดีกว่าเริ่มกระบวนการแยกต่างหาก คุณกำลังบอกว่ามันจะไม่ทำงานภายใน TRY / CATCH หรือไม่?
crokusek

@crokusek ฉันลองรูปแบบหนึ่งแล้วผลลัพธ์ก็จะว่างเปล่า ฉันจะให้อีกนัดวันนี้
Aaron Bertrand

ในกรณีที่ไม่มีข้อผิดพลาด LastErrorMessage () รับผลลัพธ์ข้อผิดพลาดก่อนหน้านี้จากเซสชันหรือไม่ จากนั้นถ้าผู้บริหารสองคนสุดท้ายทำงานเป็นสคริปต์บางทีผู้บริหารคนแรกอาจจะถูกรวมไว้ในการลอง / การจับและการจับให้ตั้งค่าตัวแปรแล้วโยนอีกครั้ง LastError จะถูกเรียกใช้เฉพาะเมื่อตัวแปรถูกตั้งค่า สมมติว่าการโยนใหม่ไม่ข้ามการโทรครั้งที่ 2 ซึ่งฉันคิดว่าเป็นเรื่องจริงในบริบทของสคริปต์ ฉันยังคงไม่สามารถใช้วิธีการนี้ได้ในที่สุดเนื่องจากไม่สามารถวางไว้ใน SP ทั้งหมดหากฉันเข้าใจอย่างถูกต้อง ขอบคุณนะ!
crokusek

2

ฉันรู้ว่านี่เป็นเธรดเก่าและฉันรู้ว่าสิ่งที่ฉันกำลังจะเสนอคือแฮ็คที่ซับซ้อน แต่ในกรณีที่สามารถช่วยใครก็ได้ที่นี่จะไป: เนื่องจากข้อผิดพลาดการสำรองข้อมูลเหล่านี้ถูกบันทึกคุณสามารถใช้ xp_readerrorlog บล็อกเพื่อขูดบันทึกข้อความที่เกี่ยวข้อง (ข้อผิดพลาดหรือข้อมูล) คุณสามารถ google สำหรับ xp_readerrorlog params แต่ในระยะสั้นคุณสามารถระบุสตริงการค้นหาและตัวกรองเวลาเริ่มต้นซึ่งมีประโยชน์ในกรณีนี้ ไม่แน่ใจว่าสิ่งนี้จะช่วยให้ตรรกะลองใหม่ของคุณหรือไม่ แต่ถ้าต้องการบันทึกข้อมูลหรือข้อผิดพลาดในการเข้าสู่ระบบฉันมาพร้อมกับสิ่งนี้ ...

IF OBJECT_ID('tempdb.dbo.#Results') IS NOT NULL DROP TABLE #Results
CREATE TABLE #Results (LogDate datetime,ProcessInfo nvarchar(100),LogText nvarchar(4000))
BEGIN TRY
SELECT @begintime = GETDATE()
EXEC sp_executesql @SQL --your backup statement string
INSERT #Results
EXEC  xp_readerrorlog 0, 1, N'backed up',@databasename,@begintime
SELECT @result = LogText from #Results where ProcessInfo = 'Backup' order by logdate desc
END TRY
BEGIN CATCH
INSERT #Results
EXEC  xp_readerrorlog 0, 1, N'Backup',@databasename,@begintime
SELECT @result = LogText from #Results where ProcessInfo = 'spid'+cast(@@SPID as varchar(6)) order by logdate desc
END CATCH
PRINT @result

HTH


วิธีนี้ใช้งานได้ดีสำหรับข้อผิดพลาดทั่วไป แต่มีข้อผิดพลาดบางอย่างที่เห็นได้ชัดว่าส่งไปยังลูกค้าโดยตรงเท่านั้น บันทึก sp_readerrorlog จะรวมถึงข้อความที่อ้างถึง "บันทึกของแอปพลิเคชัน" ซึ่งฉันถือว่าโดย "แอปพลิเคชัน" พวกเขาหมายถึงกระบวนการภายนอกที่ออกคำสั่ง SO Link
crokusek

0

คุณสามารถบันทึกรายละเอียดข้อผิดพลาดในตาราง คุณสามารถสร้างไฟล์บันทึกได้ด้วย แต่อาจต้องใช้ CLR หรือ xp_cmdshell คุณยังสามารถส่งจดหมายฐานข้อมูล แต่อาจทำให้เกิดปัญหาสแปมและไม่ใช่บันทึกที่เหมาะสม

ตารางง่ายที่สุด

  1. สร้างตารางสำหรับการจัดเก็บข้อผิดพลาด
  2. สร้างกระบวนงานที่เก็บไว้ที่แทรกเข้าไปในตารางข้อผิดพลาด
  3. เรียกกระบวนงานที่เก็บไว้ใน catch catch

ดูตัวอย่างของ Jeremy Kadlec ที่ให้ไว้ในลิงค์ด้านล่าง:

http://www.mssqltips.com/sqlservertip/1152/standardized-sql-server-error-handling-and-centralized-logging/


3
CATCHปัญหาไม่ได้เกี่ยวกับสิ่งที่จะทำอย่างไรกับข้อผิดพลาดก็ว่าข้อผิดพลาดที่เหมาะสมคือไม่สามารถใช้ได้สำหรับคำสั่งบางอย่างภายใน นี่เป็นเพราะข้อความแสดงข้อผิดพลาดล่าสุดเท่านั้นที่ส่งคืนในERROR_MESSAGE()...
Aaron Bertrand
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.