বিবৃতি মুছে ফেলুন রেফারেন্সের সীমাবদ্ধতার সাথে সাংঘর্ষিক


10

আমার পরিস্থিতি এমন দেখাচ্ছে:

সারণী STOCK_ARTICLES:

ID *[PK]*
OTHER_DB_ID
ITEM_NAME

সারণী অবস্থান:

ID *[PK]*
LOCATION_NAME

সারণী WORK_PLACE:

ID *[PK]*
WORKPLACE_NAME

সারণী INVENTORY_ITEMS:

ID *[PK]*
ITEM_NAME
STOCK_ARTICLE *[FK]*
LOCATION *[FK]*
WORK_PLACE *[FK]*

INVENTORY_ITEMS এ থাকা 3 টি FKs অবশ্যই সম্পর্কিত অন্যান্য সারণীতে "আইডি" কলামগুলি উল্লেখ করেছে।

এখানে সম্পর্কিত টেবিলগুলি হল STOCK_ARTICLE এবং INVENTORY_ITEMS।

এখন একটি এসকিউএল কাজ রয়েছে যাতে বেশ কয়েকটি পদক্ষেপ (এসকিউএল স্ক্রিপ্ট) রয়েছে যা উপরে উল্লিখিত ডাটাবেসটিকে অন্য একটি ডাটাবেস (OTHER_DB) এর সাথে "সিঙ্ক্রোনাইজ করে" । এই কাজের অভ্যন্তরের একটি পদক্ষেপ "ক্লিনআপ" এর জন্য। এটি STOCK_ITEMS থেকে সমস্ত রেকর্ড মুছে ফেলে যেখানে একই আইডি সহ অন্যান্য ডাটাবেসে কোনও সম্পর্কিত রেকর্ড নেই। দেখে মনে হচ্ছে:

DELETE FROM STOCK_ARTICLES
 WHERE
    NOT EXISTS
     (SELECT OTHER_DB_ID FROM
     [OTHER_DB].[dbo].[OtherTable] AS other
               WHERE other.ObjectID = STOCK_ARTICLES.OTHER_DB_ID)

তবে এই পদক্ষেপটি সর্বদা এতে ব্যর্থ হয়:

মোছা বিবৃতিটি রেফারেন্সের বাধা "FK_INVENTORY_ITEMS_STOCK_ARTICLES" এর সাথে সাংঘর্ষিক। "FIRST_DB" ডাটাবেস, টেবিল "dbo.INVENTORY_ITEMS", কলাম 'STOCK_ARTICLES' এ দ্বন্দ্ব দেখা দিয়েছে। [এসকিউএলসেট 23000] (ত্রুটি 547) বিবৃতিটি সমাপ্ত করা হয়েছে। [এসকিউএলসেট 01000] (ত্রুটি 3621)। পদক্ষেপ ব্যর্থ হয়েছে।

সুতরাং সমস্যাটি হ'ল এটি STOCK_ARTICLES থেকে রেকর্ডগুলি মুছে ফেলতে পারে না যখন সেগুলি INVENTORY_ITEMS দ্বারা রেফারেন্স করা হয়। তবে এই পরিষ্কার করার কাজ করা দরকার। যার অর্থ হ'ল আমার সম্ভবত ক্লিনআপ স্ক্রিপ্টটি প্রসারিত করতে হবে যাতে এটি প্রথমে STOCK_ITEMS থেকে মুছে ফেলা হবে এমন রেকর্ডগুলি সনাক্ত করে তবে তা করতে পারে না কারণ সংশ্লিষ্ট আইডি INVENTORY_ITEMS এর ভিতরে থেকে রেফারেন্স করা হয়েছে। তারপরে এটি প্রথমে INVENTORY_ITEMS এর মধ্যে থাকা এই রেকর্ডগুলি মুছে ফেলা উচিত এবং তারপরে STOCK_ARTICLES এর মধ্যে রেকর্ডগুলি মুছতে হবে। আমি কি সঠিক? এসকিউএল কোডটি তখন কেমন হবে?

ধন্যবাদ.

উত্তর:


13

বিদেশী কী বাধাগুলির পুরো বিষয়টি: এগুলি আপনাকে রেফারেন্সিয়াল অখণ্ডতা বজায় রাখার জন্য অন্য কোথাও উল্লেখ করা ডেটা মুছে ফেলা বন্ধ করে দেয়।

দুটি বিকল্প রয়েছে:

  1. INVENTORY_ITEMSপ্রথম থেকে সারিগুলি মুছুন , তারপরে সারিগুলি STOCK_ARTICLES
  2. ON DELETE CASCADEমূল সংজ্ঞায় এর জন্য ব্যবহার করুন ।

1: সঠিক অর্ডার মুছে ফেলা হচ্ছে

এটি করার সর্বাধিক দক্ষ উপায় কোয়েরির জটিলতার উপর নির্ভর করে যা কোন সারিগুলি মুছবে তা স্থির করে। একটি সাধারণ প্যাটার্ন হতে পারে:

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

এটি সাধারণ জিজ্ঞাসার জন্য বা একটি একক স্টক আইটেম মুছে ফেলার জন্য ঠিক আছে, তবে আপনার মুছে ফেলার বিবৃতিতে এমন একটি WHERE NOT EXISTSক্লজ বাসা বেঁধে রয়েছে যা ভিতরে WHERE INএকটি খুব অদক্ষ পরিকল্পনা তৈরি করতে পারে তাই একটি বাস্তবসম্মত ডেটা সেট আকারের সাথে পরীক্ষা করে প্রয়োজনে কোয়েরিটিকে পুনরায় সাজিয়ে তুলতে পারে।

লেনদেনের বিবৃতিগুলিও নোট করুন: আপনি নিশ্চিত করতে চান যে মোছা দুটিই সম্পূর্ণ হয়েছে বা সেগুলি দুটিও সম্পন্ন করে না। যদি অপারেশনটি ইতিমধ্যে কোনও লেনদেনের মধ্যেই ঘটে থাকে তবে আপনার বর্তমান লেনদেন এবং ত্রুটি পরিচালনার প্রক্রিয়াটি মেলে আপনাকে অবশ্যই এটি পরিবর্তন করতে হবে।

2: ব্যবহার করুন ON DELETE CASCADE

আপনি যদি আপনার বিদেশী কীতে ক্যাসকেড বিকল্পটি যুক্ত করেন তবে এসকিউএল সার্ভার স্বয়ংক্রিয়ভাবে আপনার জন্য এটি করবে, সীমাগুলি INVENTORY_ITEMSসীমাবদ্ধতা সরিয়ে নেওয়ার জন্য সরিয়ে ফেলা হবে যেগুলি আপনি মুছে ফেলছেন তার সারিগুলির কোনও উল্লেখ করা উচিত নয়। ঠিক এর ON DELETE CASCADEমতো এফকে সংজ্ঞা যুক্ত করুন :

ALTER TABLE <child_table> WITH CHECK 
ADD CONSTRAINT <fk_name> FOREIGN KEY(<column(s)>)
REFERENCES <parent_table> (<column(s)>)
ON DELETE CASCADE

এখানে একটি সুবিধা হ'ল মোছা হ'ল একটি পারমাণবিক বিবৃতি হ্রাস করা (যদিও, যথারীতি, 100% অপসারণ নয়) লেনদেন এবং লক সেটিংস সম্পর্কে উদ্বেগের প্রয়োজন। কাসকেড এমনকি একাধিক পিতামাতা / শিশু / গ্র্যান্ড-চাইল্ড / ... স্তরের উপরেও পরিচালনা করতে পারে যদি পিতা বা মাতা এবং সমস্ত বংশধরদের মধ্যে কেবল একটি পাথ থাকে (যেখানে এটি কার্যকর না পারে তার উদাহরণগুলির জন্য "একাধিক ক্যাসকেড পাথ" অনুসন্ধান করুন)।

দ্রষ্টব্য: আমি এবং আরও অনেকে, ক্যাসকেড মুছে ফেলাগুলি বিপজ্জনক বলে মনে করি সুতরাং আপনি যদি এই অপশনটি ব্যবহার করেন তবে এটির সঠিকভাবে আপনার ডাটাবেস ডিজাইনে নথী করার জন্য যত্নবান হন যাতে আপনি এবং অন্যান্য বিকাশকারীরা পরে বিপদটি কাটাতে পারেন না । আমি এই কারণে যেখানেই সম্ভব ক্যাসকেডিং মোছা এড়াতে চাই।

ক্যাসকেড মুছে ফেলাগুলির সাথে সৃষ্ট একটি সাধারণ সমস্যা হ'ল যখন কেউ UPDATEবা ব্যবহারের পরিবর্তে সারিগুলি বাদ দিয়ে এবং পুনরায় তৈরি করে ডেটা আপডেট করে MERGE। এটি প্রায়শই দেখা যায় যেখানে "ইতিমধ্যে বিদ্যমান সারিগুলি আপডেট করুন, যা সারণিগুলি সন্নিবেশ করান" (কখনও কখনও ইউপিএসইআরটি অপারেশন নামে পরিচিত) প্রয়োজন হয় এবং MERGEবিবৃতি সম্পর্কে অসচেতন লোকেরা এটি করা সহজ করে:

DELETE <all rows that match IDs in the new data>
INSERT <all rows from the new data>

চেয়ে

-- 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

এখানে সমস্যাটি হ'ল মোছার বিবৃতিটি শিশুদের সারিগুলিতে ক্যাসকেড করবে এবং সন্নিবেশ বিবৃতিটি তাদের পুনরায় তৈরি করবে না, তাই পিতামাতার সারণি আপডেট করার সময় আপনি ঘটনাক্রমে শিশু টেবিল (গুলি) থেকে ডেটা হারাবেন।

সারসংক্ষেপ

হ্যাঁ, আপনাকে প্রথমে সন্তানের সারিগুলি মুছতে হবে।

সেখানে অন্য কোনো বিকল্প নেই: ON DELETE CASCADE

তবে ON DELETE CASCADEবিপজ্জনক হতে পারে , তাই যত্ন সহ ব্যবহার করুন।

পার্শ্ব দ্রষ্টব্য: যখন আপনার কোনও অপারেশন প্রয়োজন হয় তখন ব্যবহার করুন MERGE(বা- UPDATEএবং- INSERTযেখানে MERGEউপলব্ধ নেই) ব্যবহার না করে অন্য লোকেরা যে ফাঁদে পড়েছেন তাতে আটকাতে না -বদলে প্রতিস্থাপন করুন ।UPSERT DELETEINSERTON DELETE CASCADE


2

আপনি কেবল একবার মুছতে আইডি পেতে পারেন, এগুলি অস্থায়ী সারণীতে সংরক্ষণ করতে এবং ক্রিয়াকলাপ মুছতে ব্যবহার করতে পারেন। তারপরে আপনি কী মুছে ফেলছেন তা আপনার আরও নিয়ন্ত্রণ করতে হবে।

এই অপারেশনটি ব্যর্থ হওয়া উচিত নয়:

SELECT sa.ID INTO #StockToDelete
FROM STOCK_ARTICLES sa
LEFT JOIN [OTHER_DB].[dbo].[OtherTable] other ON other.ObjectID = sa.OTHER_DB_ID
WHERE other.ObjectID IS NULL

DELETE ii
FROM INVENTORY_ITEMS ii
JOIN #StockToDelete std ON ii.STOCK_ARTICLE = std.ID

DELETE sa
FROM STOCK_ARTICLES sa
JOIN #StockToDelete std ON sa.ID = std.ID

2
যদিও বৃহৎ সংখ্যক STOCK_ARTICLES সারি মুছে ফেলা যদি টেম্প টেবিলটি তৈরি করার কারণে এটি অন্যান্য বিকল্পগুলির চেয়ে খারাপ সম্পাদন করতে পারে (সারি সংখ্যক সারিগুলির মধ্যে পার্থক্যটি তাত্পর্যপূর্ণ হওয়ার সম্ভাবনা কম)। একযোগে অ্যাক্সেস অসম্ভব না হলে তিনটি বিবৃতিটিকে পারমাণবিক ইউনিট হিসাবে কার্যকর করা হয়েছে তা নিশ্চিত করার জন্য যথাযথ লেনদেনের নির্দেশাবলী ব্যবহারের বিষয়েও খেয়াল রাখুন, নইলে আপনি INVENTORY_ITEMSদু'জনের মধ্যে নতুন সংযোজন হিসাবে ত্রুটি দেখতে পাবেন DELETE
ডেভিড স্পিলিট

1

আমিও এই ইস্যুতে দৌড়েছি এবং আমি এটি সমাধান করতে সক্ষম হয়েছি। এখানে আমার অবস্থা:

আমার ক্ষেত্রে, আমার কাছে বিশ্লেষণ (MYTARGET_DB) রিপোর্ট করার জন্য একটি ডেটাবেস রয়েছে, যা উত্স সিস্টেম (MYSOURCE_DB) থেকে টানছে। কিছু 'MYTARGET_DB' সারণী সেই সিস্টেমে অনন্য, এবং সেখানে ডেটা তৈরি এবং পরিচালনা করা হয়; বেশিরভাগ টেবিলগুলি 'MYSOURCE_DB' থেকে আসে এবং এমন একটি কাজ রয়েছে যা 'MYSOURCE_DB' থেকে 'MYTARGET_DB' তে ডেটা মুছে / সন্নিবেশ করে।

অনুসন্ধানের টেবিলগুলির মধ্যে একটি [PRODUCT] উত্স থেকে আসা, এবং সেখানে একটি ডাটা টেবিল রয়েছে [ইনভেন্টরিআউটসোর্সড] TARGET এ সঞ্চিত। সারণিতে নকশাকৃত রেফারেন্সিয়াল অখণ্ডতা রয়েছে। সুতরাং যখন আমি মুছুন / সন্নিবেশ চালানোর চেষ্টা করি তখন আমি এই বার্তাটি পাই।

Msg 50000, Level 16, State 1, Procedure uspJobInsertAllTables_AM, Line 249
The DELETE statement conflicted with the REFERENCE constraint "FK_InventoryOutsourced_Product". The conflict occurred in database "ProductionPlanning", table "dbo.InventoryOutsourced", column 'ProdCode'.

আমি তৈরি ওয়ার্কারআউন্ড হ'ল [@tempTable] টেবিল ভেরিয়েবলের মধ্যে [ইনভেন্টরিআউটসোর্সড] থেকে ডেটা ,োকানো, [ইনভেন্টরিআউটসোর্সড] এ ডেটা মুছে ফেলা, সিঙ্ক জবগুলি চালানো, [@tempTable] থেকে [ইনভেন্টরিআউটসোর্সড] এ sertোকানো to এটি অখণ্ডতা রাখে এবং অনন্য ডেটা সংগ্রহও বজায় থাকে। যা উভয় বিশ্বের সেরা। আশাকরি এটা সাহায্য করবে.

BEGIN TRY
    BEGIN TRANSACTION InsertAllTables_AM

        DECLARE
        @BatchRunTime datetime = getdate(),
        @InsertBatchId bigint
            select @InsertBatchId = max(IsNull(batchid,0)) + 1 from JobRunStatistic 

        --<DataCaptureTmp/> Capture the data tables unique to this database, before deleting source system reference tables
            --[InventoryOutsourced]
            DECLARE @tmpInventoryOutsourced as table (
                [ProdCode]      VARCHAR (12)    NOT NULL,
                [WhseCode]      VARCHAR (4)     NOT NULL,
                [Cases]          NUMERIC (8)     NOT NULL,
                [Weight]         NUMERIC (10, 2) NOT NULL,
                [Date] DATE NOT NULL, 
                [SourcedFrom] NVARCHAR(50) NOT NULL, 
                [User] NCHAR(50) NOT NULL, 
                [ModifiedDatetime] DATETIME NOT NULL
                )

            INSERT INTO @tmpInventoryOutsourced (
                [ProdCode]
               ,[WhseCode]
               ,[Cases]
               ,[Weight]
               ,[Date]
               ,[SourcedFrom]
               ,[User]
               ,[ModifiedDatetime]
               )
            SELECT 
                [ProdCode]
                ,[WhseCode]
                ,[Cases]
                ,[Weight]
                ,[Date]
                ,[SourcedFrom]
                ,[User]
                ,[ModifiedDatetime]
            FROM [dbo].[InventoryOutsourced]

            DELETE FROM [InventoryOutsourced]
        --</DataCaptureTmp> 

... Delete Processes
... Delete Processes    

        --<DataCaptureInsert/> Capture the data tables unique to this database, before deleting source system reference tables
            --[InventoryOutsourced]
            INSERT INTO [dbo].[InventoryOutsourced] (
                [ProdCode]
               ,[WhseCode]
               ,[Cases]
               ,[Weight]
               ,[Date]
               ,[SourcedFrom]
               ,[User]
               ,[ModifiedDatetime]
               )
            SELECT 
                [ProdCode]
                ,[WhseCode]
                ,[Cases]
                ,[Weight]
                ,[Date]
                ,[SourcedFrom]
                ,[User]
                ,[ModifiedDatetime]
            FROM @tmpInventoryOutsourced
            --</DataCaptureInsert> 

    COMMIT TRANSACTION InsertAllTables_AM
END TRY

0

আমি পুরোপুরি পরীক্ষা করে দেখিনি, তবে এর মতো কিছু কাজ করা উচিত।

--cte of Stock Articles to be deleted
WITH StockArticlesToBeDeleted AS
(
SELECT ID FROM STOCK_ARTICLES
 WHERE
    NOT EXISTS
     (SELECT OTHER_DB_ID FROM
     [OTHER_DB].[dbo].[OtherTable] AS other
               WHERE other.ObjectID = STOCK_ARTICLES.OTHER_DB_ID)
)
--delete from INVENTORY_ITEMS where we have a match on deleted STOCK_ARTICLE
DELETE a FROM INVENTORY_ITEMS a join
StockArticlesToBeDeleted b on
    b.ID = a.STOCK_ARTICLE;

--now, delete from STOCK_ARTICLES
DELETE FROM STOCK_ARTICLES
 WHERE
    NOT EXISTS
     (SELECT OTHER_DB_ID FROM
     [OTHER_DB].[dbo].[OtherTable] AS other
               WHERE other.ObjectID = STOCK_ARTICLES.OTHER_DB_ID);
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.