اختبار: Postgres المتعمق: الجزء 2
هل أنت SQL صلب؟ أم SQL لين؟
الجزء 2 من 2. العودة إلى الجزء 1
مرحبًا بك في الجزء 2 من اختبار Deep Postres Quiz الخاص بي!
يغطي هذا الاختبار مزيجًا من ميزات PostgreSQL المألوفة والأقل شهرة والمفاجآت: JOIN، NULL، SQL القياسي (ANSI)، المعاملات، التفاصيل الداخلية، الفهرسة، أخذ عينات البيانات… والمزيد!
حظ سعيد! 🍀
أي من التالي ليس ❌ تعبير JOIN صالحًا في PostgreSQL؟
كنت تعرف CROSS JOIN، أليس كذلك؟
هل جعلك JOIN ALL تفكر في عملية أخرى، UNION ALL؟
الإجابة هنا هي JOIN ALL ❌.
أنواع JOIN الصحيحة هي:
INNER JOIN(JOIN الافتراضي)LEFT JOIN(أوLEFT OUTER JOIN)RIGHT JOIN(أوRIGHT OUTER JOIN)FULL JOIN(أوFULL OUTER JOIN)CROSS JOIN(المنتج الديكارتي، ما لم يتم تصفيته لاحقًا بشرطWHERE)
ما هي الصيغة (معيار SQL) لعمود IDENTITY ذي الزيادة التلقائية في PostgreSQL (الإصدار 10+)؟
بينما لا يزال SERIAL شائع الاستخدام، فإن الطريقة وفق معيار SQL هي:
id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEYid INT IDENTITY(1,1)هي صيغة SQL Server.id INT AUTO_INCREMENTهي صيغة MySQL.id INTEGER AUTO_INCREMENT PRIMARY KEYهي صيغة MySQL.
وهذا يوفر عدة مزايا مقارنة بـ SERIAL:
- متوافق مع معيار SQL
- معالجة أفضل لملكية التسلسل
- دلالات أوضح حول الإدراج اليدوي
- يعمل بشكل أفضل مع النسخ المتماثل
SERIAL هو في الواقع مجرد اختصار لإنشاء تسلسل وتعيين قيمة افتراضية، مما قد يؤدي إلى مشاكل في التفريغ والاستعادة.
أي من هذه الميزات في PostgreSQL ليست ❌ جزءًا من معيار SQL ANSI؟
أمثلة على إضافات PostgreSQL إلى SQL ANSI:
- عبارة
RETURNING. ILIKEللمطابقة غير الحساسة لحالة الأحرف.- أنواع المصفوفات والعمليات.
- أنواع JSON/JSONB.
يتضمن معيار SQL ANSI الأصلي:
WITH(التعبيرات الجدولية الشائعة).LIKEللمطابقة الأنماط.- وصلات
LATERAL. - دوال النوافذ.
باستثناء “يعتمد على التأثيرات الجانبية” ماذا يحدث بعد حدوث خطأ؟
BEGIN;UPDATE students SET status = 'alumni' WHERE grade = 12;UPDATE students SET graduation_date = CURRENT_DATE WHERE grade = 12;UPDATE students SET grade = 'GRADUATED'; -- 🚨 Error: type mismatchCOMMIT;عند حدوث خطأ هنا:
- يتم التراجع عن المعاملة بأكملها
- لا يتم التزام أي تغييرات
- يتم وضع علامة فشل على المعاملة
للالتزام الجزئي:
BEGIN;UPDATE ...;SAVEPOINT my_save;UPDATE ...; -- ErrorROLLBACK TO my_save;COMMIT;أي شرط ON يمكن لـ PostgreSQL استخدامه في HASH JOIN؟
SELECT *FROM students aJOIN grades bON /* What goes here? ⁉ */;يمكن استخدام hash join مع شرط المساواة (=) في جملة ON.
=هو شكل المسند الأساسي لـ hash joins>و>=ليسا من مسندات hash joinHASH JOINليس بناء جملة صالحًا في PostgreSQL
قد يختار planner still loop متداخل أو merge join اعتمادًا على حجم الجدول والإحصائيات والفهارس وإعدادات الذاكرة.
أي فهرس هو الأفضل لهذا الاستعلام؟
-- Query:SELECT * FROM studentsWHERE grade_level = 42 AND last_name IN ('Levy', 'Smith');الشيء المدهش هنا هو أنه يمكن استخدام فهرس متعدد الأعمدة حتى عندما لا يتطابق ترتيب الأعمدة في نص الاستعلام مع تعريف الفهرس.
ملاحظة: مع فهارس B-tree، العمود الرئيسي هو المهم. نظرًا لأن هذا الاستعلام يقوم بالتصفية حسب grade_level و last_name، فإن الفهرس الذي يبدأ بـ grade_level ثم last_name هو مناسب.
كلما تمكنت SQL من تحديد أقل عدد من الصفوف بشكل أسرع، كان الأداء أفضل.
على سبيل المثال، إذا كان لديك جدول task، وتعرف أن status أكثر انتقائية من created_at، فقد تختار فهرسًا يبدأ بـ status لاستعلام مثل status = 'done' AND created_at > '2024-01-01'. ترتيب الشروط في نص جملة WHERE ليس هو المهم؛ ترتيب أعمدة الفهرس وأشكال المسندات هي المهمة.
يمكن استخدام فهرس B+Tree من أجل:
- العمود الرئيسي، أو بادئة يسرى من الأعمدة.
- عوامل تشغيل مثل
=،>،<،BETWEEN، وIN، اعتمادًا على شكل الاستعلام. - مطابقة البادئة،
LIKE 'prefix%'.
بفضل u/mwdb2 للإشارة إلى خطأ سابق في هذا الشرح!
أي من التالي صحيح لأسماء الأعمدة الحساسة لحالة الأحرف؟
CREATE TABLE grades ( "Student Name" VARCHAR(100), 'Student Grade' INTEGER);في PostgreSQL:
- علامات الاقتباس المزدوجة (
") للمُعرّفات (أسماء الجداول، أسماء الأعمدة) - علامات الاقتباس المفردة (
') للنصوص الحرفية
أفضل الممارسات:
- تجنب الأحرف المختلطة أو المسافات في الأسماء
- استخدم snake_case للمُعرّفات
- استخدم المُعرّفات المقتبسة فقط عند الضرورة القصوى
كيف تحدد اسم عمود يحتوي على نقطة؟
CREATE TABLE teachers ( id INT GENERATED BY DEFAULT AS IDENTITY, first.name TEXT, -- How do you include the column: first.name? salary NUMERIC);يستخدم PostgreSQL علامات الاقتباس المزدوجة لاقتباس المعرفات:
- علامات الاقتباس المزدوجة
"..."للمعرفات (أسماء الأعمدة، أسماء الجداول) - علامات الاقتباس المفردة
'...'للنصوص الحرفية - الصيغة
first.nameتُفسر على أنهاtable_name.column_name
بدون علامات الاقتباس، سيتم تفسير النقطة في اسم العمود كفاصل بين المخطط/الجدول! بالإضافة إلى ذلك، جميع المعرفات غير المقتبسة تُحوّل إلى أحرف صغيرة افتراضيًا.
لتقليل المفاجآت، من الأفضل استخدام snake_case وتجنب استخدام الأحرف الخاصة.
ما هي الصيغة الصحيحة لأخذ عينة عشوائية بنسبة 10% من students؟
هذا السؤال صعب! يحتوي على حوالي 6 فخاخ، بعضها يشمل:
- وصف عالي المستوى + أجزاء من الصيغة. يختبر كيف تتكيف وتجمع المعلومات غير المكتملة.
- يتطلب معرفة مسبقة بصيغة
TABLESAMPLE، حيث لا تستخدم أي من خياراتTABLESAMPLEالصيغة الصحيحة! (شرير! أعلم! استمر، أنا أثق بك!!! ❤️) ROW_NUMBER() OVER (ORDER BY RAND())وORDER BY RANDOM() > 10هما مجرد تشويش. يبدوان صحيحين لكنهما ليسا كذلك.RAND()غير موجود. وRANDOM() > 10هو تعبير منطقي سيكون دائمًا خطأ لأنRANDOM()دائمًا في النطاق0.0-1.0. مشكلة أخرى،ORDER BY RANDOM() > 10سيرتب أيضًا بناءً على قيمة منطقية، وليس رقمًا عشوائيًا. لكن> 10بدا وكأنه يمكن أن يكون نسبة مئوية، أليس كذلك؟BERNOULLIصحيح، لكنSAMPLETABLEليس كذلك.WHERE RANDOM() >= 0.1هو فخ!RANDOM()تُرجع قيمة في النطاق0.0 <= x < 1.0، لذا>= 0.1سترجع حوالي 90% من الصفوف، وليس 10%.
بالنسبة لطريقة TABLESAMPLE، يدعم PostgreSQL طريقتين لأخذ العينات:
BERNOULLI: كل صف له احتمال متساوٍSYSTEM: أخذ عينات على مستوى الكتلة (أسرع لكن أقل عشوائية)
لمعرفة المزيد عن أخذ العينات، اطّلع على هذه المقالة من Render.
مثال:
-- Sample 10% of rowsSELECT * FROM students TABLESAMPLE BERNOULLI (10);
-- For repeatable results, use REPEATABLE:SELECT * FROM studentsTABLESAMPLE BERNOULLI (10) REPEATABLE (42);أيضًا جدير بالذكر، TABLESAMPLE تم الإبلاغ أنه ليس عشوائيًا جدًا. إذا كنت بحاجة إلى عينة بحجم ثابت أكثر عشوائية، فكر في استخدام ORDER BY RANDOM() LIMIT 10. لكن كن على علم أن هذا يُرجع 10 صفوف، وليس 10%، ويمكن أن يكون بطيئًا على الجداول الكبيرة.
هناك أيضًا بعض الإضافات الخارجية التي توفر طرقًا أفضل لأخذ العينات العشوائية، لكن هذا خارج نطاق هذا السؤال! 😅
ماذا تفعل هذه التعويذة؟
CREATE INDEX idx_active_studentsON students(last_name)WHERE status = 'active';تُسمى هذه الحيلة Partial Index.
وهي مفيدة لتقليل استخدام القرص وتسريع الاستعلامات عندما يتم الاستعلام عادةً عن مجموعة فرعية فقط من الصفوف.
سيستخدم Postgres الفهرس فقط عندما يكون الشرط status = 'active' موجودًا في الاستعلام.
تذكير: لا يدعم Postgres ‘تلميحات الاستعلام’ مثل أنظمة RDBMS الأخرى. سيقرر مخطط الاستعلام متى يستخدم أي فهرس مناسب.
كم عدد الصفوف التي سيعيدها هذا الاستعلام؟
SELECT COUNT(*)FROM studentsWHERE grade = NULL;حسنًا، كان هذا صعبًا بعض الشيء. الفخ هو أن x = NULL لن تطابق أي صفوف أبدًا، وستعيد دائمًا صفًا واحدًا بعدد 0.
بما أن NULL قيمة غير معروفة، فلا يمكن مقارنتها بأي شيء، حتى بنفسها.
الطريقة الصحيحة للتحقق من قيم NULL هي باستخدام IS NULL.
مثال:
postgres=# SELECT count(*) FROM students WHERE age = null;count------- 0(1 row)
postgres=#postgres=# SELECT count(*) FROM students WHERE age is null;count------- 3(1 row)ماذا يفعل EXPLAIN ANALYZE مع عبارات UPDATE؟
EXPLAIN ANALYZEUPDATE studentsSET grade = grade + 1WHERE status = 'active';EXPLAIN ANALYZE ينفذ الاستعلام فعليًا! كن حذرًا عند تحليل عملية كتابة، خاصة عبارات UPDATE وDELETE وINSERT. قد تضطر إلى تشغيل عمليات التنظيف بعد كل EXPLAIN ANALYZE للحصول على نتائج قابلة للتكرار.
أفضل الممارسات:
- استخدم
EXPLAIN(بدون ANALYZE) مع UPDATE/DELETE/INSERT - لف التعديلات في معاملات:
BEGIN;EXPLAIN ANALYZE ...;ROLLBACK;أحسنت! لقد تعمقت في عدة مجالات من PostgreSQL! 🐘
أتمنى أن تكون قد تعلمت شيئًا جديدًا، أو على الأقل حصلت على درجة تتفاخر بها! 🏆
هل تريد المزيد من الإثارة في الحياة؟ اطّلع على مجموعة الاختبارات للمتعة التي لا تنتهي*!