التكرار ومشكلات التحديث
لماذا يتلف الجدول الواحد الكبير بياناتك مع الوقت؟ ⏱ نحو 25 دقيقة.
- تكتشف التكرار في جدول مسطح.
- تشرح شذوذ التحديث والحذف والإدخال بأمثلة تشغّلها.
- تعرّف الاعتماد الوظيفي
A→B. - تبيّن لماذا يُقسَّم الجدول حسب موضوعاته.
١تصميم يبدو بسيطًا
موظف جديد صمّم قاعدة المتجر جدولًا واحدًا يشبه ورقة Excel: كل صف منتج في طلب، ومعه كل شيء عن العميل:
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;
| order_id | order_date | customer | phone | city | product | price |
|---|---|---|---|---|---|---|
| 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 |
يعمل الآن. لكن انظر: «سعود العتيبي» وجواله ومدينته مكتوبة ثلاث مرات، و«تمر عجوة» وسعره مرتين. هذا التكرار Redundancy أصل ثلاث مشكلات تسمى شذوذ البيانات Anomalies.
٢شذوذ التحديث
غيّر سعود رقم جواله. الموظف حدّث الصف الأول فقط ونسي البقية:
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 = 'سعود العتيبي';
| customer | phone |
|---|---|
| سعود العتيبي | 0559999999 |
| سعود العتيبي | 0551000001 |
صار لسعود رقمان، ولا نعرف أيهما الصحيح. التعديل في نسخة واحدة يترك النسخ الأخرى قديمة. هذا شذوذ التحديث Update Anomaly.
٣شذوذ الحذف
ألغى خالد طلبه الوحيد (رقم 4)، فحذفناه:
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_id | order_date | customer | phone | city | product | price |
|---|---|---|---|---|---|---|
| (لا توجد صفوف تحقق الشرط) | ||||||
حذفنا الطلب، فاختفى معه كل ما نعرفه عن خالد: اسمه وجواله ومدينته. أردنا حذف حقيقة واحدة فخسرنا حقيقة أخرى. هذا شذوذ الحذف Delete Anomaly.
٤شذوذ الإدخال
وصل منتج جديد «زعفران»، ونريد تسجيله بسعره قبل أن يطلبه أحد. لكن رقم الطلب إلزامي:
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);
لا يمكن إدخال منتج دون طلب، ولا عميل دون طلب. حقيقة مستقلة تعتمد على وجود حقيقة أخرى. هذا شذوذ الإدخال 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.الخلاصة
- التكرار يسبب ثلاثة شذوذات: التحديث (تعارض)، والحذف (فقد)، والإدخال (عجز).
- السبب: خلط موضوعات مختلفة في جدول واحد.
- الاعتماد الوظيفي
A→B: معرفةAتكفي لمعرفةB. - العلاج: كل مجموعة اعتمادات في جدول، عبر التطبيع.
تحدٍّ برمجي: استخرج العملاء بلا تكرار
سهلالخطوة الأولى لإصلاح الجدول المسطح: قائمة العملاء، كل عميل مرة واحدة فقط، باسمه وجواله ومدينته. الكلمة DISTINCT بعد SELECT تحذف الصفوف المكررة من النتيجة.
- اعرض الأعمدة
customerوphoneوcityمنsales_sheet. - كل عميل يظهر مرة واحدة.
👀 اعرض النتيجة المطلوبة
| customer | phone | city |
|---|---|---|
| سعود العتيبي | 0551000001 | المدينة المنورة |
| نورة القحطاني | 0551000002 | الرياض |
| خالد الحربي | 0551000003 | المدينة المنورة |
- الكود يعمل بلا أخطاء
- ثلاثة عملاء، كل واحد مرة واحدة
- تستخدم DISTINCT
💡 تلميح 1
SELECT DISTINCT customer, phone, city FROM …حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
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;