طَبِّقْهاتعلّم المبدأ، وطبّقه بيدكمقرر 121 · مبادئ قواعد البيانات
الدرس 12 · الوحدة 6: تحليل وتحسين نماذج البيانات

التكرار ومشكلات التحديث

لماذا يتلف الجدول الواحد الكبير بياناتك مع الوقت؟ ⏱ نحو 25 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تكتشف التكرار في جدول مسطح.
  • تشرح شذوذ التحديث والحذف والإدخال بأمثلة تشغّلها.
  • تعرّف الاعتماد الوظيفي A → B.
  • تبيّن لماذا يُقسَّم الجدول حسب موضوعاته.

١تصميم يبدو بسيطًا

موظف جديد صمّم قاعدة المتجر جدولًا واحدًا يشبه ورقة Excel: كل صف منتج في طلب، ومعه كل شيء عن العميل:

query.sql
CREATE TABLE sales_sheet (  order_id   INTEGER NOT NULL,  order_date TEXT,  customer   TEXT,  phone      TEXT,  city       TEXT,  product    TEXT,  price      REAL);INSERT INTO sales_sheet VALUES (1, '2025-01-10', 'سعود العتيبي', '0551000001', 'المدينة المنورة', 'قهوة عربية', 35), (1, '2025-01-10', 'سعود العتيبي', '0551000001', 'المدينة المنورة', 'تمر عجوة', 90), (2, '2025-01-12', 'نورة القحطاني', '0551000002', 'الرياض', 'عسل سدر', 150), (3, '2025-02-05', 'سعود العتيبي', '0551000001', 'المدينة المنورة', 'دلة قهوة', 75), (4, '2025-02-18', 'خالد الحربي', '0551000003', 'المدينة المنورة', 'تمر عجوة', 90);SELECT * FROM sales_sheet;
النتيجة · 5 صفوف
order_idorder_datecustomerphonecityproductprice
12025-01-10سعود العتيبي0551000001المدينة المنورةقهوة عربية35
12025-01-10سعود العتيبي0551000001المدينة المنورةتمر عجوة90
22025-01-12نورة القحطاني0551000002الرياضعسل سدر150
32025-02-05سعود العتيبي0551000001المدينة المنورةدلة قهوة75
42025-02-18خالد الحربي0551000003المدينة المنورةتمر عجوة90

يعمل الآن. لكن انظر: «سعود العتيبي» وجواله ومدينته مكتوبة ثلاث مرات، و«تمر عجوة» وسعره مرتين. هذا التكرار Redundancy أصل ثلاث مشكلات تسمى شذوذ البيانات Anomalies.

٢شذوذ التحديث

غيّر سعود رقم جواله. الموظف حدّث الصف الأول فقط ونسي البقية:

query.sql
CREATE TABLE sales_sheet (  order_id   INTEGER NOT NULL,  order_date TEXT,  customer   TEXT,  phone      TEXT,  city       TEXT,  product    TEXT,  price      REAL);INSERT INTO sales_sheet VALUES (1, '2025-01-10', 'سعود العتيبي', '0551000001', 'المدينة المنورة', 'قهوة عربية', 35), (1, '2025-01-10', 'سعود العتيبي', '0551000001', 'المدينة المنورة', 'تمر عجوة', 90), (2, '2025-01-12', 'نورة القحطاني', '0551000002', 'الرياض', 'عسل سدر', 150), (3, '2025-02-05', 'سعود العتيبي', '0551000001', 'المدينة المنورة', 'دلة قهوة', 75), (4, '2025-02-18', 'خالد الحربي', '0551000003', 'المدينة المنورة', 'تمر عجوة', 90);UPDATE sales_sheet SET phone = '0559999999'WHERE order_id = 1 AND product = 'قهوة عربية'; SELECT DISTINCT customer, phone FROM sales_sheet WHERE customer = 'سعود العتيبي';
النتيجة · صفّان
customerphone
سعود العتيبي0559999999
سعود العتيبي0551000001

صار لسعود رقمان، ولا نعرف أيهما الصحيح. التعديل في نسخة واحدة يترك النسخ الأخرى قديمة. هذا شذوذ التحديث Update Anomaly.

٣شذوذ الحذف

ألغى خالد طلبه الوحيد (رقم 4)، فحذفناه:

query.sql
CREATE TABLE sales_sheet (  order_id   INTEGER NOT NULL,  order_date TEXT,  customer   TEXT,  phone      TEXT,  city       TEXT,  product    TEXT,  price      REAL);INSERT INTO sales_sheet VALUES (1, '2025-01-10', 'سعود العتيبي', '0551000001', 'المدينة المنورة', 'قهوة عربية', 35), (1, '2025-01-10', 'سعود العتيبي', '0551000001', 'المدينة المنورة', 'تمر عجوة', 90), (2, '2025-01-12', 'نورة القحطاني', '0551000002', 'الرياض', 'عسل سدر', 150), (3, '2025-02-05', 'سعود العتيبي', '0551000001', 'المدينة المنورة', 'دلة قهوة', 75), (4, '2025-02-18', 'خالد الحربي', '0551000003', 'المدينة المنورة', 'تمر عجوة', 90);DELETE FROM sales_sheet WHERE order_id = 4; SELECT * FROM sales_sheet WHERE customer = 'خالد الحربي';
النتيجة · لا صفوف
order_idorder_datecustomerphonecityproductprice
(لا توجد صفوف تحقق الشرط)

حذفنا الطلب، فاختفى معه كل ما نعرفه عن خالد: اسمه وجواله ومدينته. أردنا حذف حقيقة واحدة فخسرنا حقيقة أخرى. هذا شذوذ الحذف Delete Anomaly.

٤شذوذ الإدخال

وصل منتج جديد «زعفران»، ونريد تسجيله بسعره قبل أن يطلبه أحد. لكن رقم الطلب إلزامي:

query.sql
CREATE TABLE sales_sheet (  order_id   INTEGER NOT NULL,  order_date TEXT,  customer   TEXT,  phone      TEXT,  city       TEXT,  product    TEXT,  price      REAL);INSERT INTO sales_sheet VALUES (1, '2025-01-10', 'سعود العتيبي', '0551000001', 'المدينة المنورة', 'قهوة عربية', 35), (1, '2025-01-10', 'سعود العتيبي', '0551000001', 'المدينة المنورة', 'تمر عجوة', 90), (2, '2025-01-12', 'نورة القحطاني', '0551000002', 'الرياض', 'عسل سدر', 150), (3, '2025-02-05', 'سعود العتيبي', '0551000001', 'المدينة المنورة', 'دلة قهوة', 75), (4, '2025-02-18', 'خالد الحربي', '0551000003', 'المدينة المنورة', 'تمر عجوة', 90);INSERT INTO sales_sheet (product, price) VALUES ('زعفران', 55);
✓ أُضيف 5 صفوف
✗ خطأ (السطر 16): مخالفة قيد NOT NULL: العمود sales_sheet.order_id لا يقبل قيمة فارغة

لا يمكن إدخال منتج دون طلب، ولا عميل دون طلب. حقيقة مستقلة تعتمد على وجود حقيقة أخرى. هذا شذوذ الإدخال Insert Anomaly.

الشذوذماذا يحدث؟سببه
التحديثنسخ متعارضة للمعلومة نفسها.المعلومة مكررة في صفوف كثيرة.
الحذفحذف حقيقة يمحو حقيقة أخرى.حقيقتان مختلفتان في صف واحد.
الإدخاللا تستطيع تسجيل حقيقة حتى توجد أخرى.حقيقتان مختلفتان في صف واحد.

٥الاعتماد الوظيفي: مفتاح الحل

نقول إن العمود B يعتمد وظيفيًّا على العمود A، ونكتب A → B، إذا كانت معرفة قيمة A تكفي لمعرفة قيمة B دائمًا:

الاعتماديُقرأصحيح؟
phone → customerرقم الجوال يحدد العميلنعم
customer → cityالعميل يحدد مدينتهنعم
product → priceالمنتج يحدد سعرهنعم
order_id → order_dateرقم الطلب يحدد تاريخهنعم
city → customerالمدينة تحدد العميللا: في المدينة عملاء كثيرون

في الجدول السابق مجموعات مختلفة من الاعتمادات خُلطت في جدول واحد: معلومات العميل، ومعلومات المنتج، ومعلومات الطلب. والعلاج: كل مجموعة في جدولها، وهو بالضبط ما في «متجر الواحة»: customers و products و orders و order_items. وطريقة الوصول إليه خطوة خطوة اسمها التطبيع، وهو موضوع الدرس القادم.

توقّع وتحقّق

رقمان مختلفان لعميل واحد بعد تعديل ناقص:

Update Anomaly.

حذف آخر طلب لعميل يمحو بياناته:

Delete Anomaly.

product → price تعني:

اعتماد وظيفي.

أصل الشذوذات الثلاثة:

Redundancy.

الخلاصة

تحدٍّ برمجي: استخرج العملاء بلا تكرار

سهل

الخطوة الأولى لإصلاح الجدول المسطح: قائمة العملاء، كل عميل مرة واحدة فقط، باسمه وجواله ومدينته. الكلمة DISTINCT بعد SELECT تحذف الصفوف المكررة من النتيجة.

المطلوب:
  • اعرض الأعمدة customer و phone و city من sales_sheet.
  • كل عميل يظهر مرة واحدة.
👀 اعرض النتيجة المطلوبة
النتيجة المطلوبة
customerphonecity
سعود العتيبي0551000001المدينة المنورة
نورة القحطاني0551000002الرياض
خالد الحربي0551000003المدينة المنورة
✓ التحقق الآلي من حلّك:
  • الكود يعمل بلا أخطاء
  • ثلاثة عملاء، كل واحد مرة واحدة
  • تستخدم DISTINCT

💡 تلميح 1
SELECT DISTINCT customer, phone, city FROM …

حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.

solution.sql
CREATE TABLE sales_sheet (  order_id   INTEGER NOT NULL,  order_date TEXT,  customer   TEXT,  phone      TEXT,  city       TEXT,  product    TEXT,  price      REAL);INSERT INTO sales_sheet VALUES (1, '2025-01-10', 'سعود العتيبي', '0551000001', 'المدينة المنورة', 'قهوة عربية', 35), (1, '2025-01-10', 'سعود العتيبي', '0551000001', 'المدينة المنورة', 'تمر عجوة', 90), (2, '2025-01-12', 'نورة القحطاني', '0551000002', 'الرياض', 'عسل سدر', 150), (3, '2025-02-05', 'سعود العتيبي', '0551000001', 'المدينة المنورة', 'دلة قهوة', 75), (4, '2025-02-18', 'خالد الحربي', '0551000003', 'المدينة المنورة', 'تمر عجوة', 90); SELECT DISTINCT customer, phone, cityFROM sales_sheet;
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

📝 معلم؟ أرسل ملاحظة للمؤلف عن هذا الدرس