في استنساخ StackOverflow، ما هي العلاقة التي يجب أن تربط جدول التعليقات بالأسئلة والأجوبة؟
-
13-09-2019 - |
سؤال
في تطبيق مشابه لـ StackOverflow الذي أقوم بإنشائه، أحاول تحديد العلاقة التي تربطني Questions
, Answers
و Comments
ينبغي أن يكون للجداول.
يمكنني الحصول علي Questions
و Answers
كلاهما يتم تمثيلهما بجدول واحد Posts
.
وهذا من شأنه أن يسمح Comments
أن يكون لديك مفتاح خارجي واحد ل Posts
.
لكن اذا Questions
و Answers
هي جداول منفصلة، ما ينبغي أن العلاقات Comments
يجب على كل من هذه؟
تحديث:على الرغم من أن الإجابة المختارة توصي باتباع نهج Class Table Inheritance ويبدو أن هذا هو أفضل نهج من حيث قواعد البيانات، إلا أن هذا الخيار غير مدعوم من قبل Rails ORM.لذلك، في Rails، سيتعين على النماذج الخاصة بي استخدام Single Table Inheritance ومن المحتمل أن تبدو كما يلي:
class Post < ActiveRecord::Base
end
class Question < Post
has_many :answers, :foreign_key => :parent_id
has_many :comments, :foreign_key => :parent_id
end
class Answer < Post
belongs_to :question, :foreign_key => :parent_id
has_many :comments, :foreign_key => :parent_id
end
class Comment < Post
belongs_to :question, :foreign_key => :parent_id
belongs_to :answer, :foreign_key => :parent_id
end
class CreatePosts < ActiveRecord::Migration
def self.up
create_table :posts do |t|
t.string :type
t.string :author
t.text :content
t.integer :parent_id
t.timestamps
end
end
def self.down
drop_table :posts
end
end
CREATE TABLE "posts" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
"type" varchar(255),
"author" varchar(255),
"content" text,
"parent_id" integer,
"created_at" datetime,
"updated_at" datetime
);
المحلول
سأتبع نهج المشاركات.هذه هي أفضل طريقة لضمان التكامل المرجعي.
إذا كنت بحاجة إلى أعمدة إضافية للإجابات والأسئلة على التوالي، فضعها في جداول إضافية ذات علاقة رأس برأس مع المنشورات.
على سبيل المثال، في بناء جملة MySQL:
CREATE TABLE Posts (
post_id SERIAL PRIMARY KEY,
post_type CHAR(1), -- must be 'Q' or 'A'
-- other columns common to both types of Post
UNIQUE KEY (post_id, post_type) -- to support foreign keys
) ENGINE=InnoDB;
CREATE TABLE Comments (
comment_id SERIAL PRIMARY KEY,
post_id BIGINT UNSIGNED NOT NULL,
-- other columns for comments (e.g. date, who, text)
FOREIGN KEY (post_id) REFERENCES Posts(post_id)
) ENGINE=InnoDB;
CREATE TABLE Questions (
post_id BIGINT UNSIGNED PRIMARY KEY,
post_type CHAR(1), -- must be 'Q'
-- other columns specific to Questions
FOREIGN KEY (post_id, post_type) REFERENCES Posts(post_id, post_type)
) ENGINE=InnoDB;
CREATE TABLE Answers (
post_id BIGINT UNSIGNED PRIMARY KEY,
post_type CHAR(1), -- must be 'A'
question_id BIGINT UNSIGNED NOT NULL,
-- other columns specific to Answers
FOREIGN KEY (post_id, post_type) REFERENCES Posts(post_id, post_type)
FOREIGN KEY (question_id) REFERENCES Questions(post_id)
) ENGINE=InnoDB;
وهذا ما يسمى وراثة جدول الفصل.هناك نظرة عامة لطيفة على وراثة النمذجة باستخدام SQL في هذه المقالة:"الميراث في قواعد البيانات العلائقية."
قد يكون من المفيد استخدام post_type بحيث تكون المشاركة المحددة عبارة عن إجابة واحدة أو سؤال واحد فقط.لا تريد أن تشير كل من الإجابة والسؤال إلى مشاركة معينة.لذلك هذا هو الغرض من post_type
العمود أعلاه.يمكنك استخدام قيود التحقق لفرض القيم post_type
, أو استخدم مشغلًا إذا كانت قاعدة البيانات الخاصة بك لا تدعم قيود التحقق.
لقد قمت أيضًا بعمل عرض تقديمي قد يساعدك.الشرائح تصل إلى http://www.slideshare.net/billkarwin/sql-antipatterns-strike-back.يجب عليك قراءة الأقسام الخاصة بالارتباطات متعددة الأشكال وقيمة سمة الكيان.
إذا كنت تستخدم Single Table Inheritance، كما قلت أنك تستخدم Ruby on Rails، فسيبدو SQL DDL كما يلي:
CREATE TABLE Posts (
post_id SERIAL PRIMARY KEY,
post_type CHAR(1), -- must be 'Q' or 'A'
-- other columns for both types of Post
-- Question-specific columns are NULL for Answers, and vice versa.
) ENGINE=InnoDB;
CREATE TABLE Comments (
comment_id SERIAL PRIMARY KEY,
post_id BIGINT UNSIGNED NOT NULL,
-- other columns for comments (e.g. date, who, text)
FOREIGN KEY (post_id) REFERENCES Posts(post_id)
) ENGINE=InnoDB;
يمكنك استخدام قيد المفتاح الخارجي في هذا المثال، وأوصيك بذلك!:-)
تميل فلسفة ريلز إلى تفضيل تطبيق نموذج البيانات في طبقة التطبيق.ولكن بدون القيود التي تفرض التكامل في قاعدة البيانات، فإنك تتعرض لخطر أن تؤدي الأخطاء الموجودة في التطبيق الخاص بك، أو الاستعلامات المخصصة من أداة الاستعلام، إلى الإضرار بتكامل البيانات.
نصائح أخرى
في الشبكات الاجتماعية التي أقوم بإنشائها، أفعل شيئًا مختلفًا بعض الشيء.إذا فكرت في الأمر، فيمكن إرفاق تعليق بأي كيان في الموقع.يمكن أن يكون هذا منشورًا في مدونة، أو موضوعًا أو منشورًا في المنتدى، أو مقالًا، أو صورة لشخص ما، أو ملفًا شخصيًا لشخص ما، أو بائع خدمة، وما إلى ذلك.لهذا السبب أقوم بإنشاء جدول SystemObjects الذي يحتوي على نوع الكائن (مرجع الجدول).بالنسبة للجزء الأكبر، أقوم بإنشاء سجلات لكيانات نظامي التي ستقبل التعليقات... ولكن يتم تعيينها مباشرة إلى جداولي.يحتوي جدول SystemObjects على SystemObjectID واسم مألوف للرجوع إليه مستقبلاً (جدول بحث).
مع وضع هذا في مكانه، أقوم بعد ذلك بإنشاء جدول تعليقات يحتوي على مرجع SystemObjectID ليخبرني بالجدول الذي يجب أن أبحث فيه.ثم أقوم أيضًا بتضمين SystemObjectRecordID الذي يخبرني عن PK في الجدول المشار إليه الذي يهمني (إلى جانب جميع بيانات التعليق القياسية).
أستخدم فكرة جدول SystemObject للعديد من المفاهيم العامة الأخرى بعيدة المدى في مواقعي.فكر في العلامات والتقييمات والتعليقات وأي ثمار متدلية أخرى قد يتم إرفاقها عبر موقعك وتجميعها للاستخدام السريع.
اقرأ المزيد عن هذا في كتابي ASP.NET 3.5 الشبكات الاجتماعية.
يمكنك إنشاء جدول تعليقات واحد يحتوي على مفتاحين خارجيين، أحدهما لـ questions.questionID والآخر لـ Answers.answerId
ستحتاج إلى جدولي مجال يجمعان العلاقات معًا CommentsForQuestions وCommentsForAnswers.ستحتاج في الأساس إلى إنشاء 5 جداول لهذا الغرض:
Questions
Answers
Comments
CommentsForQuestions (relates comments to questions)
CommentsForAnswers (relates comments to answers)
المشكلة الوحيدة في هذا هي أنها أقل شأنا من فكرة المنشورات لأن التكامل المرجعي ليس قويا.يمكنني أن أضمن أن CommentsForQuestions يرتبط بتعليق وسؤال، لكن لا يمكنني منع سؤال وإجابة من الاتصال بنفس التعليق.
علاقة المفتاح الخارجي؛يمكنك إما الحصول على تعليقات الأسئلة وتعليقات الإجابة، أو يمكنك جعل التعليقات تحتوي على عمود مفتاح خارجي لكل من الأسئلة والأجوبة (وتكون هذه الأعمدة حصرية).
أنا شخصياً سأتبع نهج المشاركات.
يحرر:وبالنظر إلى ذلك، هناك نهج ثالث قد ينجح؛قد يكون لديك جدول تعليقات، ثم يكون لديك جدول اقتران يربط التعليقات إما بسؤال أو إجابة (بحيث تحتوي التعليقات على معرف والتعليق، وسيحتوي جدول الانضمام على معرف التعليق ومعرف الإجابة ومعرف السؤال ).أو، يمكن أن يكون لديك جدول تعليقات فقط، ثم يكون لديك جدول اقتران الإجابة والتعليق، وجدول اقتران سؤال وتعليق منفصل.
هناك طريقتان يمكنني التفكير فيهما.
أولاً، استخدم عمودًا آخر في جدول التعليق للإشارة إلى ما إذا كان التعليق ينتمي إلى سؤال أم إجابة.لذلك يصبح PK لجدول التعليق حيث يكون PostID هو المفتاح الخارجي للسؤال أو الإجابة ويمكن أن يكون PostType مثل 1=سؤال و2=إجابة.
ثانيًا، استخدم جدول العلاقات لكل سؤال وجواب.إذن لديك جدول سؤال وإجابة وتعليق وسؤال وتعليق وإجابة وتعليق.
لنفترض أن المفتاح الأساسي لجداول السؤال والإجابة والتعليق هو معرف السؤال ومعرف الإجابة ومعرف التعليق على التوالي.ثم ستكون أعمدة QuestionComment هي [QuestionID, CommentID].وبالمثل، ستكون أعمدة AnswerComment هي [AnswerID, CommentID].