خيارات التخلص من الأعمدة البارزة من نموذج DB (من أجل تجنب منطق SQL المكون من ثلاثة قيمة)؟

StackOverflow https://stackoverflow.com/questions/3079885

سؤال

منذ ذلك الحين ، كنت أقرأ من خلال الكتاب SQL ونظرية العلائقية بواسطة CJ Date. يشتهر المؤلف بانتقاد منطق SQL المكون من ثلاثة قيمة (3VL).1)

يقدم المؤلف بعض النقاط القوية حول سبب تجنب 3VL في SQL ، ومع ذلك فهو لا يحدد كيف سيبدو نموذج قاعدة البيانات إذا لم يكن مسموحًا لأعمدة لا لبس. لقد فكرت في هذا الأمر قليلاً وتوصلت إلى الحلول التالية. إذا فاتني خيارات التصميم الأخرى ، أود أن أسمع عنها!

1) تم انتقاد نقد التاريخ لـ SQL's 3VL بدوره أيضًا: انظر هذه الورقة من قبل كلود روبنسون (يشمل النقد الأصلي بواسطة CJ Date).


مثال على الجدول:

على سبيل المثال ، خذ الجدول التالي حيث لدينا عمود واحد لاغية (DateOfBirth):

#  +-------------------------------------------+
#  |                   People                  |
#  +------------+--------------+---------------+
#  |  PersonID  |  Name        |  DateOfBirth  |
#  +============+--------------+---------------+
#  |  1         |  Banana Man  |  NULL         |
#  +------------+--------------+---------------+

الخيار 1: المحاكاة NULL من خلال العلم والقيمة الافتراضية:

بدلاً من جعل العمود باطلاً ، يتم تحديد أي قيمة افتراضية (على سبيل المثال 1900-01-01). بالإضافة BOOLEAN سيحدد العمود ما إذا كانت القيمة في DateOfBirth يجب تجاهل ببساطة أو ما إذا كان يحتوي بالفعل على البيانات.

#  +------------------------------------------------------------------+
#  |                              People'                             |
#  +------------+--------------+----------------------+---------------+
#  |  PersonID  |  Name        |  IsDateOfBirthKnown  |  DateOfBirth  |
#  +============+--------------+----------------------+---------------+
#  |  1         |  Banana Man  |  FALSE               |  1900-01-01   |
#  +------------+--------------+----------------------+---------------+

الخيار 2: تحويل عمود لاجل إلى جدول منفصل:

يتم استبدال العمود الباطل بجدول جديد (DatesOfBirth). إذا لم يكن لدى السجل بيانات لهذا العمود ، فلن يكون هناك سجل في الجدول الجديد:

#  +---------------------------+ 1    0..1 +----------------------------+
#  |         People'           | <-------> |         DatesOfBirth       |
#  +------------+--------------+           +------------+---------------+
#  |  PersonID  |  Name        |           |  PersonID  |  DateOfBirth  |
#  +============+--------------+           +============+---------------+
#  |  1         |  Banana Man  |
#  +------------+--------------+

في حين أن هذا يبدو وكأنه الحل الأفضل ، فإن هذا قد يؤدي إلى العديد من الجداول التي تحتاج إلى انضمام إلى استعلام واحد. حيث OUTER JOINلن يُسمح (لأنهم سيقدمون NULL في مجموعة النتائج) ، ربما لم يعد يمكن جلب جميع البيانات اللازمة مع استعلام واحد كما كان من قبل.


سؤال:هل هناك أي خيارات أخرى للقضاء NULL (وإذا كان الأمر كذلك، ما هي)؟

هل كانت مفيدة؟

المحلول

رأيت زميل التاريخ هيو داروين يناقش هذه المشكلة في عرض تقديمي ممتاز "كيفية التعامل مع المعلومات المفقودة دون استخدام NULL" ، وهو متاح على موقع البيان الثالث.

حله هو البديل في نهجك الثاني. إنه الشكل السادس الطبيعي ، مع وجود جداول لعقد تاريخ الميلاد والمعرفات حيث يكون غير معروف:

#  +-----------------------------+ 1    0..1 +----------------------------+
#  |         People'             | <-------> |         DatesOfBirth       |
#  +------------+----------------+           +------------+---------------+
#  |  PersonID  |  Name          |           |  PersonID  |  DateOfBirth  |
#  +============+----------------+           +============+---------------+
#  |  1         |  Banana Man    |           ! 2          | 20-MAY-1991   |
#  |  2         |  Satsuma Girl  |           +------------+---------------+
#  +------------+----------------+
#                                  1    0..1 +------------+
#                                  <-------> | DobUnknown |
#                                            +------------+
#                                            |  PersonID  |
#                                            +============+
#                                            | 1          |
#                                            +------------+

يتطلب الاختيار من بين الأشخاص الانضمام إلى جميع الجداول الثلاثة ، بما في ذلك Boilerplate للإشارة إلى تواريخ الولادة غير المعروفة.

بالطبع ، هذا نظري إلى حد ما. لا تزال حالة SQL هذه الأيام غير متقدمة بما فيه الكفاية للتعامل مع كل هذا. يغطي عرض هيو هذه العيوب. شيء واحد يذكره غير صحيح تمامًا: بعض نكهات SQL تدعم مهمة متعددة - على سبيل المثال أدخل Oracle جميع بناء الجملة.

نصائح أخرى

أنصحك بالذهاب إلى الخيار الخاص بك 2. أنا متأكد من أن كريس ديست 6nf, ، أعلى شكل طبيعي ممكن كان التاريخ مسؤولاً بشكل مشترك عن تقديمه. أنا الثانية الموصى بها ورقة داروين على التعامل مع المعلومات المفقودة.

نظرًا لأنه لن يتم السماح بـ Outer Joins (لأنها ستقدم NULL في مجموعة النتائج) ، فإن جميع البيانات اللازمة لا يمكن جلبها مع استعلام واحد كما كان من قبل.

... هذا ليس هو الحال ، لكنني أوافق على أن مسألة Join Outer لم يتم ذكرها صراحة في ورقة داروين ؛ كان الشيء الوحيد الذي تركني أريد. يمكن العثور على الإجابة الصريحة في كتاب آخر من التاريخ ...

أولاً ، لاحظ أن التاريخ ولغة داروين العلائقية حقًا البرنامج التعليمي د لديه ما عدا نوع واحد انضم كونه الانضمام الطبيعي. التبرير هو أن نوع انضمام واحد فقط مطلوب بالفعل.

كتاب التاريخ الذي أشرت إليه هو ممتاز SQL والنظرية العلائقية: كيفية كتابة رمز SQL دقيق:

4.6: ملاحظة عن الانضمام الخارجي: "من الناحية العلوية ، [الوصلة الخارجية] نوع من زواج البنادق: إنه يجبر الجداول على نوع من الاتحاد - نعم ، لا أقصد الاتحاد ، وليس الانضمام - حتى عندما تفشل الجداول المعنية تتوافق مع المتطلبات المعتادة للاتحاد ... إنها تفعل ذلك ، في الواقع ، عن طريق حشوة واحدة أو كلاهما من الجداول مع خالية قبل القيام بالاتحاد ، مما يجعلها تتوافق مع هذه المتطلبات المعتادة بعد كل شيء. ولكن لا يوجد سبب يدعو إلى ذلك الحشو لا ينبغي أن يتم بالقيم المناسبة بدلاً من الفهود

باستخدام مثالك والقيمة الافتراضية "1900-01-01" كـ "حشوة" ، يمكن أن يبدو البديل للانضمام الخارجي هكذا:

SELECT p.PersonID, p.Name, b.DateOfBirth
  FROM Person AS p
       INNER JOIN BirthDate AS b
          ON p.PersonID = b.PersonID
UNION
SELECT p.PersonID, p.Name, '1900-01-01' AS DateOfBirth
  FROM Person AS p
 WHERE NOT EXISTS (
                   SELECT * 
                     FROM BirthDate AS b
                    WHERE p.PersonID = b.PersonID
                  );

ورقة داروين تثير جدولين صريحين ، على سبيل المثال BirthDate و BirthDateKnown, ، لكن SQL لن يكون مختلفًا كثيرًا على سبيل المثال BirthDateKnown بدلاً من الفرق BirthDate في الاعلى.

لاحظ الاستخدامات أعلاه JOIN و INNER JOIN فقط لأن SQL-92 القياسية NATURAL JOIN و UNION CORRESPONDING لم يتم تنفيذها على نطاق واسع في منتجات SQL الخاصة بالحياة الحقيقية (لا يمكن العثور على اقتباس ولكن IIRC Darwen كان مسؤولاً إلى حد كبير عن الأخيرين الذي يجعله في المعيار).

ملاحظة أخرى ، يبدو أن بناء الجملة أعلاه منذ فترة طويلة فقط لأن SQL بشكل عام هو طويل الأمد. في الجبر العلائقي النقي يشبه (رمز الزائفة):

Person JOIN BirthDate UNION Person NOT MATCHING BirthDate ADD '1900-01-01' AS DateOfBirth;

لم أقرأها ، ولكن هناك مقال يسمى كيفية التعامل مع المعلومات المفقودة باستخدام S-By-C على بيان ثالث موقع الويب الذي يديره هيو داروين و CJ Date. هذا لا يكتب بواسطة CJ Date ، لكنني أفترض أنه نظرًا لأنها واحدة من المقالات الموجودة على هذا الموقع ، فربما تشبه آرائه.

بديل واحد قد يكون كيان-قيمة القيمة نموذج:

 entity  attribute    value
 1       name         Banana Man
 1       birthdate    1968-06-20

إذا كانت عملية الميلاد غير معروفة ، فستغفل صفها.

الخيار 3: المسؤولية على كاتب التسجيل:

CREATE TABLE Person
(
  PersonId int PRIMARY KEY IDENTITY(1,1),
  Name nvarchar(100) NOT NULL,
  DateOfBirth datetime NOT NULL
)

لماذا تشويه نموذج للسماح بتمثيل فارغ عندما يكون هدفك هو القضاء عليها؟

يمكنك القضاء null في الإخراج أيضًا باستخدام COALESCE.

SELECT personid  /*primary key, will never be null here*/
       , COALESCE(name, 'no name') as name
       , COALESCE(birthdate,'no date') as birthdate
FROM people

لا تدعم جميع قواعد البيانات COALESCE ، ولكن جميعها تقريبًا لها خيار احتياطي يسمى
IFNULL(arg1, arg2) أو شيء من شأنه أن يفعل الشيء نفسه (ولكن فقط ل 2 حجج).

خيار واحد هو استخدام صريح أنواع الخيارات, ، مماثلة لشركة هاسكل Maybe functor.

لسوء الحظ ، يتمتع الكثير من تطبيقات SQL الحالية بدعم ضعيف لأنواع البيانات الجبرية المعرفة من قبل المستخدم وحتى الدعم الأضعف لمقاعد النوع المعرفة من قبل المستخدم والتي تحتاج حقًا إلى القيام بذلك بشكل نظيف.

هذا يسترد نوعًا من "الفارغ" فقط لتلك السمات التي تطلبها صراحة ، ولكن بدونها nullمنطق سخيف ثلاثية القيمة. Nothing == Nothing هو True, ، ليس unknown أو null.

يساعد الدعم لأنواع الجبرية المعرفة من قبل المستخدم أيضًا عندما يكون هناك بعض الأسباب لعمل المعلومات المفقودة ، على سبيل المثال ، سيكون معادل قاعدة البيانات لنوع Haskell التالي حلاً جيدًا للتطبيق الواضح:

data EmploymentStatus = Employed EmployerID | Unemployed | Unknown

(بالطبع ، ستحتاج قاعدة بيانات تدعم هذا أيضًا إلى دعم القيد المفتاح الأجنبي الأكثر تعقيدًا من المعتاد الذي يأتي معها.)

أقل من هذا ، أنا أتفق مع APC'رمل Onedayhenإجابات عن 6nf.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top