শ্রেণিবিন্যাস সহ টেবিলগুলি: বিদেশী কীগুলির মাধ্যমে বৃত্তাকারতা রোধ করতে একটি সীমাবদ্ধতা তৈরি করুন


10

ধরুন আমাদের কাছে একটি টেবিল রয়েছে যার নিজের কাছে একটি বিদেশী কী বাধা রয়েছে, যেমন:

CREATE TABLE Foo 
    (FooId BIGINT PRIMARY KEY,
     ParentFooId BIGINT,
     FOREIGN KEY([ParentFooId]) REFERENCES Foo ([FooId]) )

INSERT INTO Foo (FooId, ParentFooId) 
VALUES (1, NULL), (2, 1), (3, 2)

UPDATE Foo SET ParentFooId = 3 WHERE FooId = 1

এই টেবিলে নিম্নলিখিত রেকর্ড থাকবে:

FooId  ParentFooId
-----  -----------
1      3
2      1
3      2

এমন কেস রয়েছে যেখানে এই ধরণের নকশাটি বোঝাপড়া করতে পারে (যেমন আদর্শ "কর্মচারী-ও-বস-কর্মচারী" সম্পর্ক), এবং যে কোনও ক্ষেত্রে: আমি এমন একটি পরিস্থিতিতে আছি যেখানে আমার স্কিমায় এটি রয়েছে।

এই ধরণের ডিজাইন দুর্ভাগ্যক্রমে উপরের উদাহরণে দেখানো হিসাবে, ডেটা রেকর্ডগুলিতে বিজ্ঞপ্তির জন্য অনুমতি দেয়।

আমার প্রশ্নটি তখন:

  1. এটি পরীক্ষা করে এমন প্রতিবন্ধকতা লেখা কি সম্ভব ? এবং
  2. এটি পরীক্ষা করে এমন প্রতিবন্ধকতা লেখা কি সম্ভব ? (যদি কেবলমাত্র একটি নির্দিষ্ট গভীরতার প্রয়োজন হয়)

এই প্রশ্নের অংশ (2) এর জন্য এটি উল্লেখ করা প্রাসঙ্গিক হতে পারে যে আমি কেবলমাত্র শত শত বা সম্ভবত কিছু ক্ষেত্রে আমার টেবিলের কয়েক হাজার রেকর্ড আশা করি, সাধারণত প্রায় 5 থেকে 10 স্তরের চেয়ে আরও গভীর থেকে বাসা বাঁধে না।

পুনশ্চ. এমএস এসকিউএল সার্ভার ২০০৮


14 ই মার্চ 2012 আপডেট করুন
বেশ কয়েকটি ভাল উত্তর ছিল। আমি এখন সেইটিকেই গ্রহণ করেছি যা আমাকে উল্লিখিত সম্ভাবনা / সম্ভাব্যতা বুঝতে সাহায্য করেছে । যদিও আরও বেশ কয়েকটি দুর্দান্ত উত্তর রয়েছে, কিছু বাস্তবায়নের পরামর্শও রয়েছে, সুতরাং আপনি যদি একই প্রশ্নটি নিয়ে এখানে অবতী হন তবে সমস্ত উত্তর একবার দেখুন))

উত্তর:


6

আপনি অ্যাডজেন্সি তালিকার মডেলটি ব্যবহার করছেন , যেখানে এ জাতীয় সীমাবদ্ধতা প্রয়োগ করা কঠিন।

আপনি নেস্টেড সেট মডেলটি পরীক্ষা করতে পারেন , যেখানে কেবলমাত্র সত্য শ্রেণিবদ্ধ প্রতিনিধিত্ব করা যেতে পারে (কোনও বৃত্তাকার পথ নেই)। ধীরে সন্নিবেশ / আপডেটের মতো এটির অন্যান্য ঘাটতি রয়েছে।


+1 দুর্দান্ত লিঙ্ক এবং ডার্নিট আমি আশা করি আমি নেস্টেড সেট মডেলটি ঘুরে দেখে চেষ্টা করতে পার এবং তারপরে এই উত্তরটি আমার পক্ষে কাজ করে এমন হিসাবে গ্রহণ করুন।
জেরোইন

আমি এই উত্তর গ্রহণ করছি, কারণ এটি এক যে সাহায্য করেছে আমাকে বুঝতে ছিল সম্ভাবনা এবং feasability , IE এটা আমার জন্য প্রশ্নের উত্তর। যাইহোক, এই প্রশ্ন যে কাউকে অবতরণ @ a1ex07 এর কটাক্ষপাত থাকা উচিত উত্তর একটি বাধ্যতা জন্য যে সহজ ক্ষেত্রেই কাজ, এবং @ JohnGietzen এর উত্তর মহান সংযোগগুলি জন্য HIERARCHYIDযা নেস্টেড সেট মডেল একটি নেটিভ MSSQL2008 বাস্তবায়ন বলে মনে হয়।
জেরোইন

7

আমি এটি প্রয়োগের 2 প্রধান উপায় দেখেছি:

1, পুরানো উপায়:

CREATE TABLE Foo 
    (FooId BIGINT PRIMARY KEY,
     ParentFooId BIGINT,
     FooHierarchy VARCHAR(256),
     FOREIGN KEY([ParentFooId]) REFERENCES Foo ([FooId]) )

FooHierarchy কলামে এই জাতীয় মান থাকবে:

"|1|27|425"

যেখানে নম্বরগুলি FooId কলামে মানচিত্র করে। তারপরে আপনি প্রয়োগ করবেন যে হায়ারার্কি কলামটি "| id" দিয়ে শেষ হবে এবং বাকী স্ট্রিংটি প্যারেন্টের ফুহাইরাটচির সাথে মেলে।

2, নতুন উপায়:

এসকিউএল সার্ভার 2008 একটি নতুন ডাটাটাইপ বলা HierarchyID , যে তোমার জন্য এই সব করে।

এটি ওল্ড পদ্ধতিতে একই অধ্যক্ষের উপর পরিচালিত হয়, তবে এটি দক্ষতার সাথে এসকিউএল সার্ভার দ্বারা পরিচালিত হয় এবং এটি আপনার "প্যারেন্টিড" কলামের রিপ্লেসমেন্ট হিসাবে ব্যবহারের জন্য উপযুক্ত।

CREATE TABLE Foo 
    (FooId BIGINT PRIMARY KEY,
     FooHierarchy HIERARCHYID )

1
আপনার কি এমন উত্স বা সংক্ষিপ্ত ডেমো প্রদর্শন করছে যা HIERARCHYIDশ্রেণিবদ্ধ লুপগুলি রোধ করে?
নিক চ্যামাস

6

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

পরিবর্তে, আমি জড়িত পথ ব্যবহার করব।

চক্র এড়ানোর আরেকটি উপায় হল একটি চেক (আইডি> প্যারেন্টআইডি) রাখা, যা সম্ভবত খুব সম্ভবত সম্ভবও নয় is

চক্র এড়ানোর আরেকটি উপায় হ'ল লেভেলইনারইয়েরচি এবং প্যারেন্টলিভিলইনারচি, দুটি (ক্যারেন্ট), (আইডি, লেভেলইনইয়ারচি) উল্লেখ করুন এবং একটি চেক (লেভেলইনহায়ারচি> প্যারেন্টলিভিলিইনারচি) যুক্ত করুন।


চেক সীমাবদ্ধতায় ইউডিএফগুলি কাজ করে না। আপনি একবারে এক সারিতে চলে এমন একটি ফাংশন থেকে পোস্ট-আপডেটের প্রস্তাবিত রাজ্যের টেবিল-স্তরের সামঞ্জস্যপূর্ণ ছবি পেতে পারবেন না। আপনাকে অবশ্যই একটি আফ্রিকার ট্রিগার এবং রোল ব্যাক বা একটি ইনস্টল করা ট্রিগার ব্যবহার করতে হবে এবং আপডেট করতে অস্বীকার করতে হবে।
এরিক

তবে এখন আমি বহু-সারি আপডেট সম্পর্কে অন্যান্য উত্তরের মন্তব্যগুলি দেখতে পাচ্ছি।
এরিক

@ এরিক ঠিক বলেছেন, চেক সীমাবদ্ধতায় ইউডিএফ কাজ করে না।
এ কে

@ অ্যালেক্স সম্মত এটি একবারে দৃ prove়ভাবে প্রমাণ করতে কয়েক ঘন্টা সময় নিয়েছি।
এরিক

4

আমি বিশ্বাস করি এটি সম্ভব:

create function test_foo (@id bigint) returns bit
as
begin
declare @retval bit;

with t1 as (select @id as FooId, 0 as lvl  
union all 
 select f.FooId , t1.lvl+1 from t1 
 inner join Foo f ON (f.ParentFooId = t1.FooId)
 where lvl<11) -- you said that max nested level 10, so if there is any circular   
-- dependency, we don't need to go deeper than 11 levels to detect it

 select @retval =
 CASE(COUNT(*)) 
 WHEN 0 THEN 0 -- for records that don't have children
 WHEN 1 THEN 0 -- if a record has children
  ELSE 1 -- recursion detected
 END
 from t1
 where t1.FooId = @id ;

return @retval; 
end;
GO
alter table Foo add constraint CHK_REC1 CHECK (dbo.test_foo(ParentFooId) = 0)

আমি হয়ত কিছু মিস করেছি (দুঃখিত, আমি এটির মাধ্যমে পরীক্ষা করতে পারছি না), তবে এটি কার্যকর বলে মনে হচ্ছে।


1
আমি সম্মতি দিচ্ছি যে "এটি কাজ করে বলে মনে হচ্ছে", তবে এটি বহু-সারি আপডেটের জন্য ব্যর্থ হতে পারে, স্ন্যাপশট বিচ্ছিন্নকরণের অধীনে ব্যর্থ হতে পারে এবং খুব ধীর গতির হয়।
এ কে

@ অ্যালেক্সকুজনসভ: আমি বুঝতে পারি যে পুনরাবৃত্তির অনুসন্ধানগুলি তুলনামূলকভাবে ধীর এবং আমি একমত যে মাল্টি-সারি আপডেটগুলি সমস্যা হতে পারে (যদিও তারা অক্ষম হতে পারে)।
a1ex07

এই পরামর্শের জন্য @ a1ex07 Thx। আমি এটি চেষ্টা করেছিলাম, এবং সাধারণ ক্ষেত্রে এটি সত্যই কাজ করে বলে মনে হয়। মাল্টি-সারি আপডেটে ব্যর্থতা কোনও সমস্যা কিনা তা এখনও নিশ্চিত নয় (যদিও এটি সম্ভবত)। "তারা অক্ষম হতে পারে" বলতে আপনি কী বোঝাতে চাইছেন তা সম্পর্কে আমি নিশ্চিত নই
জেরোইন

আমার উপলব্ধিতে, টাস্কটি কার্সার (বা সারি) ভিত্তিক যুক্তি বোঝায়। সুতরাং এটি আপডেটগুলি অক্ষম করে তোলে যা 1 টিরও বেশি সারি সংশোধন করে (আপডেট ট্রিগরের পরিবর্তে সহজ যা tableোকানো টেবিলের 1 টিরও বেশি সারি থাকে তবে ত্রুটি উত্থাপন করে)।
a1ex07

আপনি যদি টেবিলটি আবার ডিজাইন করতে না পারেন, আমি এমন একটি পদ্ধতি তৈরি করব যা সমস্ত সীমাবদ্ধতাগুলি পরীক্ষা করে এবং রেকর্ড যুক্ত / আপডেট করে। তারপরে আমি নিশ্চিত করব যে এই এসপি ব্যতিত অন্য কেউ এই টেবিলটি সন্নিবেশ / আপডেট করতে পারবেন না।
a1ex07

3

এখানে অন্য বিকল্পটি রয়েছে: একটি ট্রিগার যা মাল্টি-সারি আপডেটের অনুমতি দেয় এবং কোনও চক্র প্রয়োগ করে না। এটি পূর্বপুরুষের চেইনটিকে অতিক্রম করে কাজ করে যতক্ষণ না এটি কোনও মূল উপাদান (প্যারেন্ট এনএলএল সহ) খুঁজে না পায়, সুতরাং প্রমাণ করে যে কোনও চক্র নেই। এটি 10 ​​প্রজন্মের মধ্যে সীমাবদ্ধ কারণ অবশ্যই একটি চক্র অন্তহীন।

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

সত্যিকারের "বুদ্ধিমান" ট্রিগারটি কোনও আইটেম নিজেই পৌঁছেছে কিনা এবং তা জামিনযোগ্য কিনা তা পরীক্ষা করে সরাসরি চক্রগুলি সন্ধান করবে। যাইহোক, এর জন্য প্রতিটি লুপ চলাকালীন সমস্ত পূর্ববর্তী পাওয়া নোডগুলির স্থিতি পরীক্ষা করা প্রয়োজন এবং এই মুহুর্তে আমি যা করতে চেয়েছিলাম তার চেয়ে একটি লুপ এবং আরও কোডিং লাগে। এটি আর কোনও ব্যয়বহুল হওয়া উচিত না কারণ স্বাভাবিক ক্রিয়াকলাপটি চক্র না করানো এবং এই ক্ষেত্রে এটি প্রতিটি লুপের সময় পূর্ববর্তী সমস্ত নোডের চেয়ে কেবল পূর্বের প্রজন্মের সাথে দ্রুত কাজ করবে।

আমি @ অ্যালেক্সকুজনসভ বা অন্য কারও কাছ থেকে ইনপুট পছন্দ করব যাতে স্ন্যাপশট বিচ্ছিন্নতায় কীভাবে এই ভাড়া নেওয়া যায়। আমি সন্দেহ করি এটি খুব ভাল না হবে তবে এটি আরও ভাল করে বুঝতে চাই।

CREATE TRIGGER TR_Foo_PreventCycles_IU ON Foo FOR INSERT, UPDATE
AS
SET NOCOUNT ON;
SET XACT_ABORT ON;

IF EXISTS (
   SELECT *
   FROM sys.dm_exec_session
   WHERE session_id = @@SPID
   AND transaction_isolation_level = 5
)
BEGIN;
  SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
END;
DECLARE
   @CycledFooId bigint,
   @Message varchar(8000);

WITH Cycles AS (
   SELECT
      FooId SourceFooId,
      ParentFooId AncestorFooId,
      1 Generation
   FROM Inserted
   UNION ALL
   SELECT
      C.SourceFooId,
      F.ParentFooId,
      C.Generation + 1
   FROM
      Cycles C
      INNER JOIN dbo.Foo F
         ON C.AncestorFooId = F.FooId
   WHERE
      C.Generation <= 10
)
SELECT TOP 1 @CycledFooId = SourceFooId
FROM Cycles C
GROUP BY SourceFooId
HAVING Count(*) = Count(AncestorFooId); -- Doesn't have a NULL AncestorFooId in any row

IF @@RowCount > 0 BEGIN
   SET @Message = CASE WHEN EXISTS (SELECT * FROM Deleted) THEN 'UPDATE' ELSE 'INSERT' END + ' statement violated TRIGGER ''TR_Foo_PreventCycles_IU'' on table "dbo.Foo". A Foo cannot be its own ancestor. Example value is FooId ' + QuoteName(@CycledFooId, '"') + ' with ParentFooId ' + Quotename((SELECT ParentFooId FROM Inserted WHERE FooID = @CycledFooId), '"');
   RAISERROR(@Message, 16, 1);
   ROLLBACK TRAN;   
END;

হালনাগাদ

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

আমি যদি বর্তমান সেশনটি এসএনএপশট আইসোলেশন স্তরটিতে থাকে তবে রিড কমিটিকেটে একটি স্যুইচও যুক্ত করেছি। এটি অসঙ্গতিগুলি রোধ করবে, যদিও দুর্ভাগ্যক্রমে অবরুদ্ধ অবরোধ বৃদ্ধি পাবে। এটি হাতের কাজটির জন্য এক প্রকারের অনিবার্য।


আপনার (READCOMMITTEDLOCK) ইঙ্গিতটি ব্যবহার করা উচিত। হুগো কর্নেলিস একটি উদাহরণ লিখেছেন: sqlblog.com/blogs/hugo_kornelis/archive/2006/09/15/…
একে

ধন্যবাদ @ অ্যালেক্স those নিবন্ধগুলি ডায়নামাইট ছিল এবং আমাকে স্ন্যাপশট বিচ্ছিন্নতা আরও অনেক ভাল বুঝতে সহায়তা করেছিল। আমি আমার কোডে নিরীক্ষিত পড়তে শর্তযুক্ত সুইচ যুক্ত করেছি।
এরিক

2

যদি আপনার রেকর্ডগুলি 1 টিরও বেশি স্তরের হয়ে থাকে তবে কোনও বাধা কাজ করে না এটি করার একমাত্র উপায় হয় হয় মূল কোডে বা ট্রিগার সহ, তবে আপনি যদি একটি বড় টেবিল এবং একাধিক স্তরের দিকে তাকান তবে এটি বেশ নিবিড়।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.