Cara mengembalikan ketika 3 prosedur tersimpan dimulai dari satu prosedur tersimpan


23

Saya memiliki prosedur tersimpan yang hanya menjalankan 3 prosedur tersimpan di dalamnya. Saya hanya menggunakan 1 parameter untuk menyimpan jika master SP berhasil.

Jika prosedur tersimpan pertama berfungsi dengan baik dalam prosedur tersimpan master, tetapi prosedur tersimpan 2 gagal, maka akankah secara otomatis memutar kembali semua SP di master SP atau apakah saya harus membuat perintah?

Inilah prosedur saya:

CREATE PROCEDURE [dbo].[spSavesomename] 
    -- Add the parameters for the stored procedure here

    @successful bit = null output
AS
BEGIN
begin transaction createSavebillinginvoice
    begin Try
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

   BEGIN 

   EXEC [dbo].[spNewBilling1]

   END

   BEGIN 

   EXEC [dbo].[spNewBilling2]

   END

   BEGIN 

   EXEC [dbo].[spNewBilling3]

   END 

   set @successful  = 1

   end Try

    begin Catch
        rollback transaction createSavesomename
        insert into dbo.tblErrorMessage(spName, errorMessage, systemDate) 
             values ('spSavesomename', ERROR_MESSAGE(), getdate())

        return
    end Catch
commit transaction createSavesomename
return
END

GO

Jika spNewBilling3melempar kesalahan, tetapi Anda tidak ingin memutar kembali spNewBilling2atau spNewBilling1, kemudian hanya menghapus [begin|rollback|commit] transaction createSavebillinginvoicedari spSavesomename.
Mike

Jawaban:


56

Hanya diberi kode yang ditunjukkan dalam pertanyaan, dan dengan asumsi bahwa tidak satu pun dari ketiga sub-procs memiliki penanganan transaksi eksplisit, maka ya, kesalahan dalam salah satu dari tiga sub-procs akan ditangkap dan ROLLBACKdi CATCHblok akan memutar kembali semua pekerjaan.

TAPI di sini ada beberapa hal yang perlu diperhatikan tentang transaksi (setidaknya dalam SQL Server):

  • Hanya ada satu transaksi nyata (yang pertama), tidak peduli berapa kali Anda meneleponBEGIN TRAN

    • Anda dapat nama transaksi (seperti yang Anda lakukan di sini) dan nama yang akan muncul di log, tapi penamaan hanya memiliki makna untuk pertama / luar paling-transaksi (karena lagi-lagi, yang pertama adalah yang transaksi).
    • Setiap kali Anda menelepon BEGIN TRAN, apakah namanya disebutkan, penghitung transaksi bertambah 1.
    • Anda dapat melihat level saat ini dengan melakukan SELECT @@TRANCOUNT;
    • Setiap COMMITperintah dikeluarkan saat @@TRANCOUNTberada di 2 atau di atas melakukan apa-apa lebih dari mengurangi, satu per satu, counter transaksi.
    • Tidak ada yang pernah dilakukan sampai COMMITdikeluarkan ketika @@TRANCOUNTitu di1
    • Hanya dalam kasus informasi di atas tidak menunjukkan dengan jelas: terlepas dari tingkat transaksi, tidak ada transaksi yang sebenarnya bersarang.
  • Simpan poin memungkinkan untuk menciptakan subset dari pekerjaan dalam satu transaksi yang dapat dibatalkan.

    • Simpan poin dibuat / ditandai melalui SAVE TRAN {save_point_name}perintah
    • Simpan poin menandai awal dari subset pekerjaan yang dapat dibatalkan tanpa memutar kembali seluruh transaksi.
    • Nama titik simpanan tidak harus unik, tetapi menggunakan nama yang sama lebih dari sekali masih menciptakan titik simpanan yang berbeda.
    • Simpan poin dapat diulang.
    • Poin simpanan tidak dapat dilakukan.
    • Simpan poin dapat dibatalkan melalui ROLLBACK {save_point_name}. (lebih lanjut tentang ini di bawah)
    • Menggulirkan kembali titik penyimpanan akan membatalkan pekerjaan apa pun yang terjadi setelah panggilan terbaru ke SAVE TRAN {save_point_name}, termasuk setiap titik penyimpanan yang dibuat setelah yang digulirkan kembali telah dibuat (karenanya "bersarang").
    • Membalikkan titik penyimpanan tidak mempengaruhi hitungan / level transaksi
    • Pekerjaan apa pun yang dilakukan sebelum inisial SAVE TRANtidak dapat dibatalkan kecuali dengan mengeluarkan ROLLBACKseluruh transaksi.
    • Hanya untuk menjadi jelas: mengeluarkan COMMITsaat @@TRANCOUNTberada di 2 atau di atas, tidak berpengaruh pada poin simpanan (karena sekali lagi, tingkat transaksi di atas 1 tidak ada di luar penghitung itu).
  • Anda tidak dapat melakukan transaksi bernama tertentu. "Nama" transaksi, jika disediakan bersama dengan COMMIT, diabaikan dan hanya ada untuk dibaca.

  • Yang ROLLBACKdikeluarkan tanpa nama akan selalu mengembalikan SEMUA transaksi.

  • Yang ROLLBACKditerbitkan dengan nama harus sesuai dengan:

    • Transaksi pertama, dengan asumsi namanya:
      Asumsikan tidak SAVE TRANtelah dipanggil dengan nama transaksi yang sama, ini akan mengembalikan semua transaksi.
    • "Save point" (dijelaskan di atas):
      Perilaku ini akan "membatalkan" semua perubahan yang dibuat sejak yang terakhir SAVE TRAN {save_point_name} dipanggil.
    • Jika transaksi pertama adalah a) bernama dan b) telah memiliki SAVE TRANperintah yang dikeluarkan dengan namanya, maka setiap ROLLBACK dari nama transaksi tersebut akan membatalkan setiap titik penyimpanan hingga tidak ada lagi yang tersisa dari nama itu. Setelah itu, ROLLBACK yang diterbitkan dengan nama itu akan mengembalikan semua transaksi.
    • Misalnya, asumsikan perintah berikut dijalankan dalam urutan yang ditunjukkan:

      BEGIN TRAN A -- @@TRANCOUNT is now 1
      -- DML Query 1
      SAVE TRAN A
      -- DML Query 2
      SAVE TRAN A
      -- DML Query 3
      
      BEGIN TRAN B -- @@TRANCOUNT is now 2
      SAVE TRAN B
      -- DML Query 4

      Sekarang, jika Anda mengeluarkan (masing-masing skenario berikut ini tidak tergantung satu sama lain):

      • ROLLBACK TRAN Bsekali: Ini akan membatalkan "DML Permintaan 4". @@TRANCOUNTmasih 2.
      • ROLLBACK TRAN Bdua kali: Ini akan membatalkan "DML Query 4" dan kemudian kesalahan karena tidak ada titik penyimpanan yang sesuai untuk "B". @@TRANCOUNTmasih 2.
      • ROLLBACK TRAN Asekali: Ini akan membatalkan "DML Kueri 4" dan "DML Kueri 3". @@TRANCOUNTmasih 2.
      • ROLLBACK TRAN Adua kali: Ini akan membatalkan "DML Kueri 4", "DML Kueri 3", dan "DML Kueri 2". @@TRANCOUNTmasih 2.
      • ROLLBACK TRAN Atiga kali: Ini akan membatalkan "DML Query 4", "DML Query 3", dan "DML Query 2". Kemudian akan mengembalikan seluruh transaksi (yang tersisa hanyalah "DML Kueri 1"). @@TRANCOUNTsekarang 0.
      • COMMITsekali: @@TRANCOUNTturun ke 1.
      • COMMITsekali dan kemudian ROLLBACK TRAN Bsekali: @@TRANCOUNTturun ke 1. Lalu itu akan membatalkan "DML Permintaan 4" (membuktikan bahwa COMMIT tidak melakukan apa-apa). @@TRANCOUNTmasih 1.
  • Nama transaksi dan simpan nama titik:

    • dapat memiliki hingga 32 karakter
    • diperlakukan sebagai yang memiliki Binary Collation (tidak case-sensitive seperti yang dinyatakan oleh dokumentasi saat ini), terlepas dari Instance-level atau Collations-level Collations.
    • Untuk perincian, silakan lihat bagian Nama Transaksi pada posting berikut: Apa yang ada di Nama ?: Di dalam Dunia Aneh Pengidentifikasi T-SQL
  • Prosedur tersimpan bukan merupakan transaksi implisit. Setiap permintaan jika tidak ada transaksi eksplisit yang telah dimulai, merupakan transaksi implisit. Inilah sebabnya mengapa transaksi eksplisit di sekitar kueri tunggal tidak diperlukan kecuali jika ada alasan terprogram untuk melakukan ROLLBACK, jika tidak, setiap kesalahan dalam kueri adalah kemunduran otomatis kueri itu.

  • Saat memanggil prosedur tersimpan, harus keluar dengan nilai @@TRANCOUNTyang sama seperti ketika dipanggil. Artinya, Anda tidak dapat:

    • Mulai BEGIN TRANdi dalam proc tanpa melakukan itu, berharap untuk melakukan dalam proses panggilan / orang tua.
    • Anda tidak dapat mengeluarkan a ROLLBACKjika transaksi eksplisit dimulai sebelum proc dipanggil karena akan kembali @@TRANCOUNTke 0.

    Jika Anda keluar dari prosedur tersimpan dengan jumlah transaksi yang lebih tinggi atau lebih rendah daripada ketika itu menatap, Anda akan mendapatkan kesalahan yang mirip dengan:

    Msg 266, Level 16, Negara 2, Prosedur YourProcName, Baris 0
    jumlah transaksi setelah EXECUTE menunjukkan jumlah BEGIN dan pernyataan COMMIT yang tidak sesuai. Hitungan sebelumnya = X, hitungan saat ini = Y.

  • Variabel tabel, seperti halnya variabel biasa, tidak terikat oleh transaksi.


Mengenai memiliki penanganan transaksi dalam procs yang dapat disebut secara independen (dan karenanya memerlukan penanganan transaksi) atau panggilan dari procs lain (karenanya tidak memerlukan penanganan transaksi): ini dapat dilakukan dalam beberapa cara yang berbeda.

Cara saya telah menanganinya selama beberapa tahun sekarang yang tampaknya bekerja dengan baik adalah dengan hanya BEGIN/ COMMIT/ ROLLBACKpada lapisan paling luar. Panggilan sub-proc cukup lewati saja perintah transaksi. Saya telah menguraikan di bawah ini apa yang saya masukkan ke dalam setiap proc (well, masing-masing membutuhkan penanganan transaksi).

  • Di bagian atas setiap proc, DECLARE @InNestedTransaction BIT;
  • Di tempat yang sederhana BEGIN TRAN, lakukan:

    IF (@@TRANCOUNT = 0)
    BEGIN
       SET @InNestedTransaction = 0;
       BEGIN TRAN; -- only start a transaction if not already in one
    END;
    ELSE
    BEGIN
       SET @InNestedTransaction = 1;
    END;
  • Di tempat yang sederhana COMMIT, lakukan:

    IF (@@TRANCOUNT > 0 AND @InNestedTransaction = 0)
    BEGIN
       COMMIT;
    END;
  • Di tempat yang sederhana ROLLBACK, lakukan:

    IF (@@TRANCOUNT > 0 AND @InNestedTransaction = 0)
    BEGIN
       ROLLBACK;
    END;

Metode ini harus bekerja sama terlepas dari apakah transaksi dimulai dalam SQL Server atau jika dimulai pada lapisan aplikasi.

Untuk templat lengkap penanganan Transaksi ini dalam TRY...CATCHkonstruk, silakan lihat jawaban saya untuk pertanyaan DBA.SE berikut: Apakah kami diharuskan menangani Transaksi dalam Kode C # dan juga dalam prosedur tersimpan .


Bergerak melampaui "dasar-dasar", ada beberapa nuansa transaksi tambahan yang harus diperhatikan:

  • Secara default, sebagian besar waktu Transaksi tidak dibatalkan / dibatalkan secara otomatis saat terjadi kesalahan. Ini biasanya bukan masalah selama Anda memiliki penanganan kesalahan yang tepat dan menelepon ROLLBACKdiri sendiri. Namun, kadang-kadang hal menjadi rumit, seperti dalam kasus kesalahan batch-dibatalkan, atau ketika menggunakan OPENQUERY(atau Server Terkait pada umumnya) dan kesalahan terjadi pada sistem jarak jauh. Sementara sebagian besar kesalahan dapat terjebak menggunakan TRY...CATCH, ada dua yang tidak bisa dijebak seperti itu (tidak bisa mengingat yang mana pada saat ini - meneliti). Dalam kasus ini, Anda harus menggunakan SET XACT_ABORT ONuntuk mengembalikan transaksi dengan benar.

    SET XACT_ABORT ON menyebabkan SQL Server segera memutar kembali segala Transaksi (jika ada yang aktif) dan membatalkan kumpulan jika ada kesalahan. Pengaturan ini ada sebelum SQL Server 2005, yang memperkenalkan TRY...CATCHkonstruk. Sebagian besar, TRY...CATCHmenangani sebagian besar situasi dan sebagian besar kebutuhan usang XACT_ABORT ON. Namun, ketika menggunakan OPENQUERY(dan mungkin satu skenario lain yang saya tidak ingat saat ini), maka Anda masih harus menggunakan SET XACT_ABORT ON;.

  • Di dalam Pemicu, XACT_ABORTsecara implisit diatur ke ON. Hal ini menyebabkan setiap kesalahan dalam Pemicu untuk membatalkan seluruh pernyataan DML yang menembakkan Pemicu.

  • Anda harus selalu memiliki penanganan kesalahan yang tepat, terutama saat menggunakan Transaksi. The TRY...CATCHmembangun, diperkenalkan pada SQL Server 2005, menyediakan sarana penanganan hampir semua situasi, menyambut perbaikan atas pengujian untuk @@ERRORsetelah setiap pernyataan, yang tidak membantu banyak dengan kesalahan batch-batal.

    TRY...CATCHmemperkenalkan "negara" baru, namun. Ketika tidak menggunakan TRY...CATCHkonstruk, jika Anda memiliki Transaksi aktif dan terjadi kesalahan, maka ada beberapa jalur yang dapat diambil:

    • XACT_ABORT OFFdan kesalahan pembatalan pernyataan: Transaksi masih aktif dan pemrosesan dilanjutkan dengan pernyataan berikutnya , jika ada.
    • XACT_ABORT OFFdan sebagian besar kesalahan pembatalan bets: Transaksi masih aktif dan pemrosesan berlanjut dengan bets berikutnya , jika ada.
    • XACT_ABORT OFFdan kesalahan pembatalan bets tertentu: Transaksi dibatalkan dan pemrosesan dilanjutkan dengan bets berikutnya , jika ada.
    • XACT_ABORT ONdan kesalahan apa pun : Transaksi dibatalkan dan pemrosesan dilanjutkan dengan batch berikutnya , jika ada.


    NAMUN, saat menggunakan TRY...CATCH, kesalahan yang dibatalkan batch tidak membatalkan batch, tetapi alih-alih mentransfer kontrol ke CATCHblok. Ketika XACT_ABORTadalah OFF, Transaksi akan tetap aktif sebagian besar waktu, dan Anda akan perlu COMMIT, atau paling mungkin, ROLLBACK. Tapi ketika menghadapi kesalahan batch-batal tertentu (seperti dengan OPENQUERY), atau ketika XACT_ABORTadalah ON, Transaksi akan berada dalam keadaan baru, "uncommitable". Dalam keadaan ini Anda tidak bisa COMMIT, Anda juga tidak bisa melakukan operasi DML. Semua dapat Anda lakukan adalah ROLLBACKdan SELECTpernyataan. Akan tetapi, dalam keadaan "tidak dapat diterima" ini, Transaksi dibatalkan karena kesalahan yang terjadi, dan mengeluarkannya ROLLBACKhanyalah formalitas, tetapi harus dilakukan.

    Fungsi, XACT_STATE , dapat digunakan untuk menentukan apakah suatu Transaksi aktif, tidak dikomunikasikan, atau tidak ada. Dianjurkan (oleh beberapa orang, setidaknya) untuk memeriksa fungsi ini di CATCHblok untuk menentukan apakah hasilnya -1(yaitu uncommitable) alih-alih menguji jika @@TRANCOUNT > 0. Tetapi dengan XACT_ABORT ON, itu harus menjadi satu-satunya keadaan yang memungkinkan untuk dilakukan, sehingga tampaknya pengujian untuk @@TRANCOUNT > 0dan XACT_STATE() <> 0setara. Di sisi lain, ketika XACT_ABORTadalah OFFdan ada transaksi aktif, maka ada kemungkinan untuk memiliki keadaan baik 1atau -1di CATCHblok, yang memungkinkan untuk kemungkinan menerbitkan COMMITbukan ROLLBACK(meskipun, saya tidak bisa memikirkan kasus ketika seseorang inginCOMMITjika Transaksi berkomitmen). Informasi dan penelitian lebih lanjut tentang penggunaan XACT_STATE()dalam CATCHblok XACT_ABORT ONdapat ditemukan dalam jawaban saya untuk pertanyaan DBA.SE berikut: Dalam kasus apa transaksi dapat dilakukan dari dalam blok CATCH ketika XACT_ABORT disetel ke ON? . Harap perhatikan bahwa ada bug minor XACT_STATE()yang menyebabkannya salah 1dalam skenario tertentu: XACT_STATE () mengembalikan 1 ketika digunakan dalam SELECT dengan beberapa variabel sistem tetapi tanpa klausa FROM dari klausa


Catatan tentang kode asli:

  • Anda dapat menghapus nama yang diberikan untuk transaksi karena tidak membantu.
  • Anda tidak perlu BEGINdan ENDsekitar setiap EXECpanggilan

2
Itu jawaban yang sangat bagus, bagus.
McNets

1
Wow, itu satu jawaban komprehensif! Terima kasih! BTW dos halaman berikut membahas kesalahan yang Anda singgung yang tidak terjebak oleh Coba ... Tangkap? (Di bawah judul "Kesalahan Terpengaruh oleh TRY ... CATCH Membangun"? Technet.microsoft.com/en-us/library/ms175976(v=sql.110).aspx
jrdevdba

1
@jrdevdba Terima kasih :-). Dan selamat datang. Mengenai kesalahan yang tidak terjebak, saya cukup berarti dua ini: Compile errors, such as syntax errors, that prevent a batch from runningdan Errors that occur during statement-level recompilation, such as object name resolution errors that occur after compilation because of deferred name resolution.. Tetapi mereka tidak sering terjadi, dan ketika Anda menemukan situasi seperti itu, perbaikilah (jika itu bug dalam kode) atau letakkan di sub-proses ( EXECatau sp_executesql) sehingga TRY...CATCHdapat menjebaknya.
Solomon Rutzky

2

Ya, jika karena kesalahan kode rollback dalam pernyataan tangkap prosedur tersimpan master Anda akan dieksekusi, itu akan mengembalikan semua operasi yang dilakukan oleh pernyataan langsung atau melalui salah satu prosedur tersimpan yang tersimpan di dalamnya.

Bahkan jika Anda belum menerapkan transaksi eksplisit dalam prosedur tersimpan bersarang Anda, prosedur tersimpan ini akan menggunakan transaksi implisit dan akan melakukan penyelesaian TETAPI baik Anda telah melakukan transaksi eksplisit atau implisit dalam prosedur tersimpan tersimpan yang tersimpan. Mesin SQL Server akan mengabaikannya dan akan kembalikan semua tindakan dengan prosedur tersimpan bersarang ini jika prosedur tersimpan master gagal dan transaksi didukung oleh roll.

Setiap kali transaksi dilakukan atau dibatalkan berdasarkan tindakan yang dilakukan pada akhir transaksi terluar. Jika transaksi luar dilakukan, transaksi bersarang dalam juga dilakukan. Jika transaksi luar dibatalkan, maka semua transaksi dalam juga dibatalkan, terlepas dari apakah transaksi dalam itu dilakukan secara individual atau tidak.

Untuk referensi http://technet.microsoft.com/en-us/library/ms189336(v=sql.105).aspx

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.