Apakah ada cara untuk menguji apakah DELETE akan gagal karena kendala?

10

Saya ingin dapat memprediksi apakah DELETE akan mengalami pelanggaran kendala, tanpa benar-benar melakukan penghapusan.

Apa pilihan saya untuk melakukan ini? Apakah ada cara sederhana untuk melakukan "dry run" dari DELETE?

Jay Sullivan
sumber
Apakah Anda mencoba untuk mencegah pengecualian untuk pernyataan ini saja, atau apakah Anda mencoba untuk memudahkan penanganan kesalahan dalam batch yang lebih besar yang berisi penghapusan ini?
Aaron Bertrand
3
Bisakah Anda memeriksa untuk melihat apakah ada FK, dan menjalankan pernyataan SELECT untuk memeriksa nilai-nilai?
SQLRockstar
Aaron: Kita perlu menjalankan beberapa DELETE dalam transaksi terpisah. Jika salah satu gagal, yang lain sudah berkomitmen. (Desain buruk sejak awal, saya tahu, tapi itu bukan aplikasi saya, dan itu tidak berubah.) Solusi terbaik saat ini terdengar seperti melakukan pemeriksaan kering untuk melihat apakah DELETE akan gagal.
Jay Sullivan
Masih tidak yakin saya mengerti. Apakah Anda mencoba untuk membiarkan sisa penghapusan berhasil, atau Anda mencoba memeriksa di muka bahwa semua penghapusan akan berhasil, atau tidak ada yang seharusnya?
Aaron Bertrand
Aaron: Maaf saya tidak menjelaskannya, tapi ya, saya berusaha memastikan mereka semua berhasil, atau tidak ada yang berhasil.
Jay Sullivan

Jawaban:

24

Jika tujuan Anda adalah untuk memproses semua penghapusan hanya jika semuanya berhasil, mengapa tidak menggunakan TRY / CATCH:

BEGIN TRANSACTION;
BEGIN TRY
  DELETE #1;
  DELETE #2;
  DELETE #3;
  COMMIT TRANSACTION;
END TRY
BEGIN CATCH
  ROLLBACK TRANSACTION;
END CATCH

Jika tujuannya adalah untuk memungkinkan semua penghapusan yang berhasil berhasil bahkan jika satu atau lebih akan gagal, maka Anda dapat menggunakan TRY / CATCH individu, misalnya

BEGIN TRY
  DELETE #1;
END TRY
BEGIN CATCH
  PRINT 1;
END CATCH

BEGIN TRY
  DELETE #2;
END TRY
BEGIN CATCH
  PRINT 1;
END CATCH
Aaron Bertrand
sumber
6

Salah satu opsi adalah memulai transaksi, menjalankan penghapusan Anda, dan kemudian selalu mengembalikan:

begin tran

delete Table1 where col1 = 1

-- Test whether it is there
select * from Table1 where col1 = 1

rollback tran

-- Confirm that it is still there
select * from Table1 where col1 = 1
GaTechThomas
sumber
1
Dan jika penghapusan berhasil, jalankan lagi? Bagaimana jika penghapusannya sangat mahal? Dan jika penghapusan gagal, lalu bagaimana? Anda telah melakukan satu penghapusan dan dua pemilihan. Bagaimana cara saya memutuskan untuk menghapus selanjutnya atau tidak?
Aaron Bertrand
1
Jika itu adalah bagian dari persyaratan, maka mereka harus ditangani. Jawaban ini berkaitan dengan "dry run" sederhana.
GaTechThomas
Yah mereka tidak ketika Anda pertama kali mengirimkan jawaban Anda, tetapi mereka telah diklarifikasi sekarang.
Aaron Bertrand
4
@GaTechThomas Aaron banyak berkontribusi, jadi dia kadang-kadang singkat, tapi saya yakin niatnya tidak untuk menjadi agresif. Saya membahas ini di The Heap dan saya akan berterima kasih atas kesempatan untuk melakukannya dengan Anda juga.
Jack bilang coba topanswers.xyz
1
@ JackDouglas Saya telah membaca komentar Heap yang Anda referensi dan mengerti intinya. Komentar komunitas itu masuk akal kecuali bagian di mana saya dipanggil badut karena menunjukkan agresi nya. Saya tidak mengerti bagaimana saya yang dianggap agresif. Saya memposting jawaban yang sah untuk pertanyaan seperti yang diajukan pada saat itu. Itu tidak meminta kualitas produksi - kadang-kadang Anda hanya perlu cepat dan mudah. Jadi pada jawaban saya, saya mendapat pertanyaan runcing. Penampilannya adalah dia merendahkan jawaban saya sehingga jawabannya akan dipilih. Haruskah kita mengambil utas ini di tempat lain?
GaTechThomas
0

Saya ingin meningkatkan solusi yang disediakan oleh Aaron Bertrand dengan beberapa kode, jika Anda ingin mencoba menambahkan elemen tabel, mengelola pengecualian untuk mengabaikan gagal atau juga menghentikan proses setelah kesalahan.

Yang ini akan memilih catatan dari tabel dan kemudian mencoba untuk menghapusnya tanpa kecuali:

DECLARE @MaxErrors INT
SET @MaxErrors = 5;    // Setting 0 will stop process after the first error!

SELECT
    [Id]
    , ROW_NUMBER() OVER (ORDER BY Id ASC) AS [Index]
INTO #DeletingItems
FROM myTable

DECLARE @Current INT, @Max INT, @Id INT, @TotErrors INT
SELECT
    @Current = 1
    , @TotErrors = 0
    , @Max = MAX([Index])
FROM #DeletingTable

WHILE @Current <= @Max
BEGIN
    SELECT
        @Id = [Id]
    FROM #DeletingItems
    WHERE
        [Index] = @Index;

    BEGIN TRANSACTION;    
    BEGIN TRY    
        DELETE FROM myTable WHERE [Id] = @Id;

        COMMIT TRANSACTION;    
    END TRY    
    BEGIN CATCH    
        ROLLBACK TRANSACTION;

        SET @TotErrors = @TotErrors + 1;

        IF @TotErrors > @MaxErrors
            BREAK;
    END CATCH

    SET @Current = @Current + 1;
END
MAXE
sumber
1
Mengapa? Bagaimana ini merupakan perbaikan dari jawaban yang diterima?
ToolmakerSteve