Yabancı anahtar kısıtlamalarının bütün mesele budur: referans bütünlüğünü korumak için başka bir yerde atıfta bulunulan verileri silmenizi durdururlar.
İki seçenek vardır:
- Satırları sil
INVENTORY_ITEMS
, ilk sonra gelen satırlar STOCK_ARTICLES
.
ON DELETE CASCADE
Anahtar tanımında için kullanın .
1: Doğru Sırayla Silme
Bunu yapmanın en etkili yolu, hangi satırların silineceğine karar veren sorgunun karmaşıklığına bağlı olarak değişir. Genel bir örüntü şunlar olabilir:
BEGIN TRANSACTION
SET XACT_ABORT ON
DELETE INVENTORY_ITEMS WHERE STOCK_ARTICLE IN (<select statement that returns stock_article.id for the rows you are about to delete>)
DELETE STOCK_ARTICLES WHERE <the rest of your current delete statement>
COMMIT TRANSACTION
Bu, basit sorgular için veya tek bir stok öğesini silmek için uygundur, ancak delete deyiminiz WHERE NOT EXISTS
içinde WHERE IN
çok verimsiz bir plan oluşturabilecek bir iç içe yerleştirme içerir, bu nedenle gerçekçi bir veri kümesi boyutuyla test edin ve gerekirse sorguyu yeniden düzenleyin.
Ayrıca işlem ifadelerine dikkat edin: her iki silme işleminin de tamamlandığından veya hiçbirinin olmadığından emin olmak istersiniz. İşlem zaten bir işlem içinde gerçekleşiyorsa, bunu geçerli işlem ve hata işleme işleminize uyacak şekilde değiştirmeniz gerekir.
2: Kullanım ON DELETE CASCADE
Basamak seçeneğini yabancı anahtarınıza eklerseniz, SQL Server bunu sizin için otomatik olarak yapar INVENTORY_ITEMS
ve hiçbir şeyin sildiğiniz satırlara başvurmaması gereken kısıtlamayı karşılamak için satırları kaldırır . Sadece ON DELETE CASCADE
FK tanımına şöyle ekleyin :
ALTER TABLE <child_table> WITH CHECK
ADD CONSTRAINT <fk_name> FOREIGN KEY(<column(s)>)
REFERENCES <parent_table> (<column(s)>)
ON DELETE CASCADE
Buradaki bir avantaj, silme işlem ve kilit ayarları hakkında endişelenme ihtiyacını azaltan (her zamanki gibi% 100 kaldırılmadan) bir atom ifadesidir. Kaskad hatta birden ebeveyn / çocuk / büyük-çocuk üzerinde çalışabilir / ... seviyeler ise ebeveyn ve tüm soyundan arasında yalnızca bir yol (nerede bu olabilir değil işin örnekler için "çoklu kaskad yolları" için arama) bulunmaktadır.
NOT: Ben ve diğerleri, basamaklı silme işlemlerinin tehlikeli olduğunu düşünüyoruz, bu nedenle bu seçeneği kullanırsanız, veritabanı tasarımınızda düzgün bir şekilde belgelendirmek için çok dikkatli olun, böylece siz ve diğer geliştiriciler tehlikeyi daha sonra atlamazsınız . Bu nedenle mümkün olan her yerde basamaklı silmelerden kaçınırım.
Basamaklı silme işlemlerinin neden olduğu yaygın bir sorun, birisinin UPDATE
veya yerine satırları bırakarak ve yeniden oluşturarak verileri güncellemesidir MERGE
. Bu genellikle "zaten var olan satırları güncelleyin, olmayan satırları ekleyin" (bazen UPSERT işlemi olarak da adlandırılır) gerektiğinde ve MERGE
ifadeden habersiz kişilerin yapmayı daha kolay bulduğu durumlarda görülür :
DELETE <all rows that match IDs in the new data>
INSERT <all rows from the new data>
göre
-- updates
UPDATE target
SET <col1> = source.<col1>
, <col2> = source.<col2>
...
, <colN> = source.<colN>
FROM <target_table> AS target JOIN <source_table_or_view_or_statement> AS source ON source.ID = target.ID
-- inserts
INSERT <target_table>
SELECT *
FROM <source_table_or_other> AS source
LEFT OUTER JOIN
<target_table> AS target
ON target.ID = source.ID
WHERE target.ID IS NULL
Buradaki sorun, delete ifadesinin alt satırlara basamaklanacağı ve insert ifadesinin bunları yeniden oluşturmayacağıdır, bu nedenle üst tabloyu güncellerken yanlışlıkla alt tablolardan veri kaybedersiniz.
özet
Evet, önce alt satırları silmeniz gerekir.
Başka bir seçenek daha var: ON DELETE CASCADE
.
Ancak ON DELETE CASCADE
tehlikeli olabilir , bu yüzden dikkatli kullanın.
Yan Not: Kullanım MERGE
(veya UPDATE
-ve- INSERT
nerede MERGE
kullanılamaz) Bir gerektiğinde UPSERT
operasyon, değil DELETE
-daha sonra-replace-Kurun- INSERT
kullanan diğer kişilerle tuzaklarına düşmekten kaçınmak için ON DELETE CASCADE
.
INVENTORY_ITEMS
ikiDELETE
s arasına yeni eklenen olarak görebilirsiniz .