SQL Server / الأساسي / الدرس 2
المخططات وأنواع البيانات والمفاتيح
صمّم نظام طلبات صغيراً باستخدام أنواع صريحة ونصوص Unicode ومعرفات مستقرة، وافهم الضمانات التي توفرها القيود وما يحتاج إلى تصميم مستقل.
في هذه الصفحة
أهداف الدرس
- اختيار أنواع SQL Server الدقيقة.
- تمييز أسماء المخططات والمفاتيح وتوليد الهوية.
- شرح قرارات NULL وتخزين Unicode.
سمّ الكائنات وحدد معنى الصف
المخطط مساحة أسماء وحد صلاحيات داخل قاعدة البيانات. يمكن أن يتعايش sales.OrderHeader وarchive.OrderHeader لاختلاف اسمي المخططين. استخدم أسماء ثنائية الأجزاء في شيفرة القاعدة لتوضيح الملكية والقصد. المخطط ليس قاعدة منفصلة ولا يضمن فصلاً مادياً في التخزين. قبل اختيار الأعمدة اكتب جملة تحدد ما يمثله الصف.
في متجر التدريب يمثل صف العميل حساب عميل واحداً، ويمثل صف الطلب حدث طلب واحداً، ويمثل صف بند الطلب ظهور منتج داخل ذلك الطلب. يعرّض تكرار اسم العميل في كل بند البيانات لتعديلات متناقضة. افصل الكيانات واربطها بالمفاتيح. يبدأ التطبيع بالحقائق والاعتماديات، لا بتقسيم كل جدول طويل اعتباطياً.
اختر الأنواع وفق قواعد المجال
استخدم int للمعرفات عندما يكفي مداه طوال عمر النظام، وdecimal(p,s) للمبالغ الدقيقة، وdate للتواريخ التقويمية، وdatetime2 للأوقات التي تتطلب أجزاء من الثانية. يمثل float قيماً تقريبية وقد يفاجئك عند مقارنة الأسعار بالمساواة. اختر المنازل العشرية وفق قواعد العملة والحساب؛ فالتقريب عند حد متفق عليه يختلف عن فقد الدقة صامتاً أثناء التخزين.
يستخدم المثال متغير جدول لإظهار البنية والبيانات معاً. يحتوي صفه الوحيد مبلغاً دقيقاً واسماً عربياً. يخزن datetime2 تاريخاً ووقتاً دون إزاحة منطقة زمنية. إذا اعتمد النظام UTC فوثّق هذا الاصطلاح، واستخدم datetimeoffset عندما تكون الإزاحة نفسها مطلوبة. لا يعرف أي من النوعين المنطقة الزمنية الجغرافية للعميل تلقائياً.
DECLARE @Customer TABLE (
CustomerId int PRIMARY KEY,
DisplayName nvarchar(80) NOT NULL,
CreditLimit decimal(10,2) NOT NULL,
RegisteredOn date NOT NULL
);
INSERT @Customer VALUES (1, N'ليلى', 125.50, '20260115');
SELECT CustomerId, DisplayName, CreditLimit FROM @Customer;
CustomerId DisplayName CreditLimit
1 ليلى 125.50
يستخدم التاريخ صيغة مختصرة غير ملتبسة، ويحافظ الحرف N وعمود Unicode على الاسم العربي.
احفظ النص متعدد اللغات بقصد واضح
nvarchar هو الخيار الافتراضي في الدورة للأسماء متعددة اللغات؛ ضع N قبل الثوابت النصية Unicode. تقيس n أزواج بايت UTF-16 ولا تضمن عدداً ثابتاً من المحارف المرئية، لأن بعض المحارف تحتاج زوجين. حدد الطول دائماً في التصريحات والتحويلات. قد يقتطع متغير وسيط ضيق النص قبل وصول INSERT إلى عمود أوسع.
يدعم SQL Server 2019 أيضاً Unicode في varchar عند استخدام ترتيب محارف UTF-8 مناسب، لكن ذلك قرار صريح بشأن الترتيب والتخزين. لا تعمم أن كل عمود varchar يحفظ جميع الكتابات بأمان. يؤثر ترتيب المحارف كذلك في المقارنة والفرز وحساسية حالة الأحرف والعلامات. اختبر الأسماء والبحث بنصوص عربية ولاتينية ممثلة للاستخدام بدلاً من الاكتفاء بمعرفات ASCII.
افصل توليد الهوية عن التفرد
يشترط المفتاح الأساسي قيماً فريدة وغير فارغة، ويمنع المفتاح الخارجي الإشارة إلى قيم أب غير موجودة. يولد IDENTITY أرقاماً لكنه ليس قيد تفرد بحد ذاته ولا يعد بترقيم بلا فجوات. قد تترك عمليات إدراج فاشلة والتخزين المؤقت والحذف فجوات. وقد يحتاج ترقيم فواتير الأعمال إلى عملية منفصلة مصممة صراحة.
يوضح المثال الثاني مفتاحاً مركباً: يحدد الزوج البند مع أن OrderId يتكرر. يربط مفتاح خارجي في التصميم الدائم OrderId برأس الطلب. عرّف مفتاحاً أساسياً واحداً لكل جدول وأضف قيود UNIQUE للمعرفات التجارية المستقلة كالمرجع الخارجي للطلب. لا ينشئ SQL Server فهرساً تلقائياً على أعمدة الجدول المُشير عند إضافة مفتاح خارجي.
DECLARE @Line TABLE (
OrderId int NOT NULL, LineNo int NOT NULL,
Quantity int NOT NULL,
PRIMARY KEY (OrderId, LineNo)
);
INSERT @Line VALUES (10,1,2),(10,2,1),(11,1,3);
SELECT OrderId, LineNo, Quantity FROM @Line
ORDER BY OrderId, LineNo;
OrderId LineNo Quantity
10 1 2
10 2 1
11 1 3
يجوز تكرار OrderId لأن الزوج يظل فريداً؛ أما تكرار الزوج كله فيخالف المفتاح الأساسي.
مثّل المعلومات المفقودة بصدق
تعني NULL معلومة مفقودة أو غير معروفة، ولا تعني صفراً أو نصاً فارغاً أو تاريخاً خاصاً. قرر قابلية كل خاصية لأن تكون مجهولة. يمكن أن يبقى تاريخ التسليم NULL قبل الجدولة، بينما يجب أن تكون الكمية المطلوبة NOT NULL مع قاعدة مناسبة للقيمة الموجبة. تعطي القيم الافتراضية قيمة للحقل المحذوف من الإدراج لكنها لا تفسر معنى NULL الصريحة تجارياً.
وثّق الوحدات والمديات المقبولة بجانب المخطط: الكمية بعدد القطع والمبلغ بعملة محددة والوقت باصطلاح محدد. لا يستطيع النوع وحده فرض جميع هذه المعاني. تحمي القيود قواعد الصف المستقرة، ويوفر تحقق التطبيق رسائل مفيدة. ينبغي أن تتفق الطبقتان كي لا يتجاوز استيراد آخر أو خدمة أخرى قاعدة سلامة أساسية.
الأخطاء الشائعة وإصلاحها
String or binary data would be truncated.
- لماذا يحدث؟
- تتجاوز قيمة طول الوجهة المعلن.
- كيف تصلحه؟
- افحص أحجام الإدخال والمعلمة والمتغير الوسيط والعمود، ووسّع ما يتطلبه المجال فقط.
- كيف تتجنب تكراره؟
- حدد الأطوال واختبر حدود النصوص متعددة اللغات.
Violation of PRIMARY KEY constraint.
- لماذا يحدث؟
- يتكرر المفتاح الكامل لصف موجود.
- كيف تصلحه؟
- صحح المعرف أو ارفض التكرار وفق قواعد العمل.
- كيف تتجنب تكراره؟
- صمّم مفاتيح مستقرة واختبر الطلبات المكررة.
Cannot insert the value NULL into column.
- لماذا يحدث؟
- حُذفت قيمة مطلوبة أو أُرسلت NULL صراحة.
- كيف تصلحه؟
- وفر قيمة صالحة أو راجع قابلية NULL وفق المتطلبات الفعلية.
- كيف تتجنب تكراره؟
- ميّز الحقائق الاختيارية من المطلوبة في النموذج.
اختبر فهمك
1. هل يحمي IDENTITY وحده المرجع الخارجي للطلب من التكرار؟
اعرض الحل المشروح
لا؛ فهو يولد رقماً داخلياً. ضع UNIQUE على المرجع الخارجي وحدد هل هو مطلوب، ثم قرر كيف يُبلغ عن الطلب المكرر أو يعاد استخدام نتيجته.
2. مثّل تاريخ شحن غير معروف وسعراً قيمته 19.95.
اعرض الحل المشروح
استخدم date قابلاً لـ NULL لتاريخ الشحن وdecimal بحجم مناسب ومنزلتين عشريتين للسعر. لا تستخدم تاريخاً رمزياً أو float تقريبياً، ووثّق العملة مستقلة.
أهم ما تعلمته
- توضح أسماء المخططات هوية الكائن وصلاحياته.
- استخدم أطوالاً صريحة وثوابت Unicode للنص متعدد اللغات.
- تفرض المفاتيح الهوية، أما IDENTITY فيولد القيم.