هيكِل مخطّطاتك من الصيغة العادية الأولى 1NF إلى الثالثة 3NF لإزالة الشذوذات، واعرف متى تُلغي التطبيع عن وعي كامل.
افتح هذا الدرس في Kodokonالمخطّط السيّئ يكلّفك سنوات. يهدف التطبيع إلى شيء واحد: أن يُخزَّن كل معطى مرة واحدة فقط، لإزالة ثلاثة أنواع من الشذوذ: التحديث (تغيير بريد إلكتروني واحد في عشرة أماكن)، والإدراج (تعذّر إضافة منتج دون طلب)، والحذف (حذف آخر طلب يمحو العميل). انظر إلى هذا المخطّط المُفسَد عمدًا.
CREATE TABLE bad_orders (
id INTEGER PRIMARY KEY,
customer_name TEXT,
customer_email TEXT,
product_names TEXT,
total REAL
);
INSERT INTO bad_orders
(customer_name, customer_email,
product_names, total)
VALUES
('Alice', 'alice@mail.com',
'keyboard,mouse', 74.0),
('Alice', 'alice@mail.co',
'monitor', 199.0);الصيغ العادية الثلاث الأولى، عمليًا. 1NF: قيم ذرّية - ينتهكها product_names = 'keyboard,mouse'، ما يجعل أي WHERE أو JOIN على المنتجات غير عملي. 2NF (مفتاح مركّب): يعتمد كل عمود على المفتاح بأكمله - إن تخزين سعر منتج من الكتالوج في سطر طلب مُعرَّف بـ (الطلب، المنتج) ينتهكها، لأن السعر يعتمد على المنتج وحده. 3NF: لا يعتمد أي عمود غير مفتاحي على عمود آخر غير مفتاحي - ينتهكها customer_email الذي يعتمد على customer_name؛ ومن هنا يأتي التكرار alice@mail.co. وإليك المخطّط المُصحَّح.
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE
);
CREATE TABLE products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
price REAL NOT NULL
);
CREATE TABLE purchases (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL
REFERENCES customers(id)
);
CREATE TABLE purchase_items (
purchase_id INTEGER NOT NULL
REFERENCES purchases(id),
product_id INTEGER NOT NULL
REFERENCES products(id),
quantity INTEGER NOT NULL,
PRIMARY KEY (purchase_id, product_id)
);PRAGMA foreign_keys = ON;
INSERT INTO customers (name, email)
VALUES ('Alice', 'alice@mail.com');
INSERT INTO products (name, price)
VALUES ('keyboard', 49.0), ('mouse', 25.0);
INSERT INTO purchases (customer_id) VALUES (1);
INSERT INTO purchase_items
(purchase_id, product_id, quantity)
VALUES (1, 1, 1), (1, 2, 1);
SELECT c.name, p.name AS product,
i.quantity, p.price
FROM purchase_items AS i
JOIN purchases AS pu ON pu.id = i.purchase_id
JOIN customers AS c ON c.id = pu.customer_id
JOIN products AS p ON p.id = i.product_id;إذن، متى ينبغي أن تُلغي التطبيع؟ عندما تصبح عمليات القراءة عنق الزجاجة المقيس: عمليات ربط بين خمسة جداول على شاشة حسّاسة، أو تجميع يُعاد حسابه في كل عرض. الأنماط النزيهة: عمود تخزين مؤقت (purchases.total مُجمَّد وقت الطلب - وهو أمر مشروع: فالسعر المدفوع معطى تاريخي يختلف عن سعر الكتالوج)، أو عدّاد يحافظ عليه مُطلِق (trigger)، أو جدول تقارير يُعاد بناؤه دفعةً واحدة. الثمن الواجب دفعه: يمكن لكل نسخة أن تنحرف، وأصبح كودك، لا المحرّك، هو الذي يضمن الاتساق.