Lesson 14 / الدرس 14
Deciding where a fact lives / تقرير أين تعيش الحقيقة
Schema decisions outlive every line of code around them. The rule that prevents most pain is boring and absolute: each fact is stored once, in one place, and everywhere else points at it.
قرارات المخطط تعيش أطول من كل سطر كود حولها. والقاعدة التي تمنع معظم الألم مملّة ومطلقة: كل حقيقة تُخزَّن مرة واحدة، في مكان واحد، وكل ما عداه يشير إليها.
You can rewrite a function in an afternoon. A column that a thousand rows depend on is a migration, a deploy and a risk. Schema is the slowest thing to change in an application, so it is worth more thought per line than anything else you write — and the thinking is mostly one question, asked repeatedly.
The question: is this fact stored anywhere else?
-- Wrong. The course title is written into every submission.
CREATE TABLE submissions (
id INT AUTO_INCREMENT PRIMARY KEY,
student_name VARCHAR(120),
course_title VARCHAR(120),
body TEXT
);
-- A student changes their name. A course is retitled. Now some rows
-- say one thing and some say another, and no query can tell you which
-- is current, because both are equally stored.
-- Right. Each fact once; the rest are pointers.
CREATE TABLE submissions (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
course VARCHAR(60) NOT NULL,
body TEXT NOT NULL,
created_at DATETIME NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id),
INDEX (course, created_at)
);
There is one honest exception, and knowing it stops the rule being applied blindly. Sometimes you want the value as it was, not as it is — an invoice records the price paid, not today's price, and a delivery records the address it went to. Those are not duplicates; they are different facts that happen to have been equal once.
Columns you will wish you had chosen better
-
NOT NULL by default. Allow null only where "unknown" is a real, meaningful state. A nullable column is a value every future query has to remember to handle, and most of them will not. NOT NULL افتراضًا. واسمح بـnull حيث يكون «مجهول» حالة حقيقية ذات معنى فقط. فالعمود القابل للإفراغ قيمةٌ على كل استعلام مستقبلي أن يتذكر معالجتها، ومعظمها لن يفعل.
-
DATETIME stored in UTC, formatted on the way out. Storing local time works perfectly until a clock changes or a second country appears, and by then the wrong values are indistinguishable from the right ones. DATETIME تُخزَّن بتوقيت UTC وتُنسَّق في طريق الخروج. فتخزين التوقيت المحلي يعمل تمامًا حتى تتغير ساعة أو يظهر بلد ثانٍ، وعندها تكون القيم الخاطئة غير مميزة عن الصحيحة.
-
DECIMAL for money, never FLOAT. A float cannot hold 0.10 exactly, so totals drift by fractions that accumulate — and the bug report is "the numbers are slightly wrong sometimes", which is the hardest kind to chase. وDECIMAL للمال، ولا FLOAT أبدًا. فالعدد العائم لا يسع 0.10 بالضبط، فتنحرف المجاميع بكسور تتراكم — وبلاغ الخلل «الأرقام خاطئة قليلًا أحيانًا»، وهو أصعب الأنواع تتبّعًا.
-
An index on what you filter and sort by. Without one the database reads every row; with a thousand rows that is invisible and with a million it is a timeout. The cost is slightly slower writes, which is almost always the right trade. وفهرس على ما ترشّح وترتّب به. فبدونه تقرأ قاعدة البيانات كل صف؛ وبألف صف يكون ذلك غير مرئي وبمليون يكون انتهاء مهلة. والتكلفة كتابات أبطأ قليلًا، وهي المقايضة الصائبة في معظم الأحيان.
deleted_at ومرشّح في استعلاماتك هو التصميم الصادق عادةً — لكن يصير عليك عندئذ أن تحذف فعلًا عند الطلب، فـ«أخفيناه» ليس ما وافق عليه من يطلب منك حذف بياناته.db/migrate.php في هذا الموقع بالضبط. فالمخطط الموجود في قاعدة بياناتك المحلية وحدها لا يستطيع غيرك إعادة إنشائه، ويوم تحتاج إعادة بنائه ليس يومًا هادئًا أبدًا.Check yourself / اختبر نفسك
1. Why store user_id rather than the student's name in a submissions table?
Each fact once, everywhere else a pointer. When the name lives in one row, changing it changes it everywhere, and there is no version of it that can be stale.
2. When is storing a copy of a value the right thing to do?
Those are not duplicates. They are different facts that happened to be equal once, and an invoice that silently follows today's prices is wrong in a way that matters legally.
3. Why DECIMAL rather than FLOAT for money?
The resulting bug report is "the numbers are slightly wrong sometimes", which is about the hardest thing there is to chase — and it is prevented entirely by the column type.
Score / النتيجة: 0 / 3