الجزء الرابع معلومات متقدمة هامة في SQL

SQL - الأمر ALTER 

الأمر ALTER يستخدم عند الحاجة لإجراء تعديل على بنية الجدول مثل تغيير إسم الجدول, تغيير إسم العامود, تغيير نوع العامود, حذف العامود, إضافة عامود جديد, إضافة خصائص على العامود, و إزلة خصائص من العامود.

تنبيه: كن حذراً عند إجراء أي تعديل على الجدول لأنك لا تستطيع التراجع عن أي تعديل أجريته على جدول.


الشكل العام لإستخدام الأمر ALTER

ALTER TABLE table_name
operation;
	

  • مكان الكلمة table_name نضع إسم الجدول الذي نريد إجراء تعديلات عليه.

  • مكان الكلمة operation نحدد ما نريد تعديله, حذفه, أو إضافته.

تجهيز قاعدة البيانات التي سنطبق عليها

قم بتنفيذ الإستعلام التالي حتى تنشئ قاعدة بيانات جديدة إسمها harmash و تنشئ فيها جدول إسمه users يحتوي على بيانات 5 مستخدمين.

الإستعلام

-- سيتم حذفها harmash في حال كان يوجد بالأساس قاعدة بيانات إسمها
DROP DATABASE IF EXISTS harmash;

-- harmash هنا قمنا بإنشاء قاعدة بيانات جديدة إسمها
CREATE DATABASE harmash;

-- harmash هنا قمنا بتحديد أن أي إستعلام جديد سيتم تنفيذه على قاعدة البيانات
USE harmash;

-- يتألف من 4 أعمدة users هنا قمنا بإنشاء جدول جديد إسمه
-- لأننا قمنا بتحديدها قبل إستدعاء هذا الأمر harmash سيتم إنشاء هذا الجدول بداخل قاعدة البيانات
CREATE TABLE users (
    id         INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
    first_name VARCHAR(50),
    last_name  VARCHAR(50),
    country    VARCHAR(50)
);

-- هنا قمنا بإضافة 5 أسطر في الجدول, أي أضفنا معلومات 5 مستخدمين
-- لاحظ أننا لم نحدد أسماء الأعمدة التي سنضع فيها البيانات لأننا قمنا بملئ جميع المعلومات
-- في البداية هو حتى يكون عدد القيم الموضوعة يساوي عدد أعمدة الجدول null سبب وضع الكلمة
-- ستقوم قاعدة البيانات بوضع رقم تعرفة مختلف لكل سطر null مكان الكلمة
INSERT INTO users VALUES (null, "Rami", "Masri", "Lebanon");
INSERT INTO users VALUES (null, "Ahmad", "Naji", "Syria");
INSERT INTO users VALUES (null, "Hanan", "Mostafa", "KSA");
INSERT INTO users VALUES (null, "Abdullah", "Helmi", "Egypt");
INSERT INTO users VALUES (null, "Majed", "Alali", "Yaman");
		

بعد تنفيذ الإستعلام السابق في phpMyAdmin سيتم إنشاء قاعدة البيانات harmash و إنشاء الجدول users بداخلها.
قم بالنقر على إسم قاعدة البيانات harmash من القائمة اليسرى حتى تبدأ بالتعامل معها و تطبيق ما ستتعلمه في هذا الدرس.


البيانات التي قمنا بإضافتها بشكل إفتراضي في الجدول users.

id first_name last_name country
1 Rami Masri Lebanon
2 Ahmad Naji Syria
3 Hanan Mostafa KSA
4 Abdullah Helmi Egypt
5 Majed Alali Yaman

SQL طريقة حذف عامود من الجدول

لحذف عامود من الجدول, نكتب DROP COLUMN و من بعدها نحدد إسم العامود الذي نريد حذفه كالتالي.

ALTER TABLE table_name
DROP COLUMN column_name; 
	

مثال طريقة حذف عامود من الجدول

الإستعلام التالي يقوم بحذف العامود country من الجدول users.
بعدها يعرض جميع البيانات الموجودة في الجدول.

الإستعلام

ALTER TABLE users         -- users هنا قمنا بتحديد أننا نريد إجراء تعديل على بنية الجدول
DROP COLUMN country;      -- منه country هنا قمنا بتحديد أننا نريد حذف العامود

SELECT * FROM users;      -- لمعرفة التغيرات الجديدة users هنا قمنا بعرض جميع البيانات الموجودة في الجدول
		

سنحصل على النتيجة التالية عند تنفيذ الإستعلام.
نلاحظ أن العامود country تم حذفه بشكل نهائي من الجدول.

id first_name last_name
1 Rami Masri
2 Ahmad Naji
3 Hanan Mostafa
4 Abdullah Helmi
5 Majed Alali

طريقة إضافة عامود جديد في الجدول

لإضافة عامود جديد في الجدول, نكتب ADD و من بعدها نحدد إسم و نوع العامود الذي نريد إضافته كالتالي.

ALTER TABLE table_name
ADD column_name datatype; 
	

مثال طريقة إضافة عامود جديد في الجدول

الإستعلام التالي يقوم بإضافة عامود إسمه salary و نوعه DOUBLE في الجدول users.
بعدها يعرض جميع البيانات الموجودة في الجدول.

الإستعلام

ALTER TABLE users        -- users هنا قمنا بتحديد أننا نريد إجراء تعديل على بنية الجدول
ADD salary DOUBLE;       -- DOUBLE و نوعه salary هنا قمنا بتحديد أننا نريد إضافة عامود جديد إسمه

SELECT * FROM users;     -- لمعرفة التغيرات الجديدة users هنا قمنا بعرض جميع البيانات الموجودة في الجدول
		

سنحصل على النتيجة التالية عند تنفيذ الإستعلام.
نلاحظ أنه تم إضافة عامود جديد إسمه salary في آخر الجدول و جميع حقوله فارغة.

id first_name last_name salary
1 Rami Masri NULL
2 Ahmad Naji NULL
3 Hanan Mostafa NULL
4 Abdullah Helmi NULL
5 Majed Alali NULL

طريقة تغيير نوع القيم التي يمكن تخزينها في العامود

في البداية, عند تحويل نوع بيانات الجدول, عليك الإنتباه جيداً للأنواع التي تحول إليها لأنك قد تخسر القيم الموضوعة فيها أو قد تخسر جزء منها و إليك بعض الأمثلة.

  • إذا كان الجدول نوعه VARCHAR و قمت بتحويله للنوع INT سيتم تحويل كل القيم السابقة إلى Null لأنه بالمنطق لا يمكنك تحويل النص إلى رقم.

  • إذا كان الجدول نوعه DECIMAL أو DOUBLE أو FLOAT و قمت بتحويله للنوع INT ستخسر أي رقم موضوع بعد الفاصلة فقط لأن النوع INT لا يقبل أي رقم بعد الفاصلة.


الآن, عليك معرفة أن الكلمة التي تضعها لتغيير نوع القيم التي يمكن تخزينها في العامود تختلف من قاعدة بيانات لأخرى و لكن شكل الإستعلام هو نفسه تماماً.


في قواعد بيانات MySQL

لتغيير نوع القيم التي يمكن تخزينها في العامود نكتب MODIFY COLUMN و من بعدها نحدد إسم و نوع العامود الذي نريد تعديل نوعه كالتالي.
ملاحظة: في المثال التالي, سنكتب الإستعلام باستخدام أسلوب قواعد بيانات MySQL لأنها القاعدة التي نتعامل معها منذ بداية الدورة.

ALTER TABLE table_name
MODIFY COLUMN column_name datatype; 
	

في قواعد بيانات Access و SQL Server

لتغيير نوع القيم التي يمكن تخزينها في العامود نكتب ALTER مرة ثانية و من بعدها نحدد إسم و نوع العامود الذي نريد تعديل نوعه كالتالي.

ALTER TABLE table_name
ALTER column_name datatype; 
	

في قواعد بيانات Oracle قبل الإصدار 10G

لتغيير نوع القيم التي يمكن تخزينها في العامود نكتب MODIFY COLUMN و من بعدها نحدد إسم و نوع العامود الذي نريد تعديل نوعه كالتالي.

ALTER TABLE table_name
MODIFY COLUMN column_name datatype; 
	

في قواعد بيانات Oracle الإصدار 10G و الإصدارات الأحدث

لتغيير نوع القيم التي يمكن تخزينها في العامود, نكتب MODIFY فقط و من بعدها نحدد إسم و نوع العامود الذي نريد تعديل نوعه كالتالي.

ALTER TABLE table_name
MODIFY column_name datatype; 
	

مثال

الإستعلام التالي يقوم بتغيير نوع القيم التي يمكن تخزينها في العامود salary إلى DECIMAL.
بعدها يعرض جميع البيانات الموجودة في الجدول.

الإستعلام

ALTER TABLE users                      -- users هنا قمنا بتحديد أننا نريد إجراء تعديل على بنية الجدول
MODIFY COLUMN salary DECIMAL(7,2);     -- DECIMAL إلى النوع salary هنا قمنا بتحديد أننا نريد تغيير نوع العامود

SELECT * FROM users;                   -- لمعرفة التغيرات الجديدة users هنا قمنا بعرض جميع البيانات الموجودة في الجدول
		

سنحصل على النتيجة التالية عند تنفيذ الإستعلام.

id first_name last_name salary
1 Rami Masri NULL
2 Ahmad Naji NULL
3 Hanan Mostafa NULL
4 Abdullah Helmi NULL
5 Majed Alali NULL

طريقة تغيير إسم الجدول

لتغيير إسم الجدول, نكتب RENAME TO و من بعدها نضع الإسم الجديد الذي نريد إعطاؤه للجدول كالتالي.

ALTER TABLE table_name
RENAME TO new_table_name; 
	

مثال طريقة تغيير إسم الجدول

الإستعلام التالي يقوم بتغيير إسم الجدول إلى employees.

الإستعلام

ALTER TABLE users          -- users هنا قمنا بتحديد أننا نريد إجراء تعديل على بنية الجدول
RENAME TO employees;       -- employees هنا قمنا بتحديد أننا نريد تغيير إسم الجدول إلى
		

طريقة تغيير إسم العامود

طريقة تغيير إسم العامود تختلف بشكل كلي من قاعدة بيانات لأخرى, و الآن سنضع لك الطريقة الخاصة بقواعد بيانات MySQL لأننا نطبق الشرح باستخدامها.
لتغيير إسم الجدول نكتب CHANGE و من بعدها نحدد إسم العامود الذي نريد تغييره, ثم مسافة فارغة, ثم نضع الإسم الجديد للعامود, ثم نكتب نوع القيم التي يمكن تخزينها في العامود كالتالي.

ALTER TABLE tableName
CHANGE old_column_name new_column_name datatype(length);
	

مثال طريقة تغيير إسم العامود

الإستعلام التالي يقوم بتغيير إسم العامود salary إلى income.
بعدها يعرض جميع البيانات الموجودة في الجدول.

الإستعلام

ALTER TABLE employees                   -- employees هنا قمنا بتحديد أننا نريد إجراء تعديل على بنية الجدول
CHANGE salary income DECIMAL(7,2);      -- income إلى salary هنا قمنا بتحديد أننا نريد تغيير إسم العامود

SELECT * FROM users;                    -- لمعرفة التغيرات الجديدة employees هنا قمنا بعرض جميع البيانات الموجودة في الجدول
		

سنحصل على النتيجة التالية عند تنفيذ الإستعلام.

id first_name last_name income
1 Rami Masri NULL
2 Ahmad Naji NULL
3 Hanan Mostafa NULL
4 Abdullah Helmi NULL
5 Majed Alali NULL

مفهوم القيم الإفتراضية

عند إضافة سطر جديد في أي جدول, كنا نلاحظ أن الحقول التي لا نضيف فيها قيم يتم إظهار القيمة Null للإشارة إلى أنها فارغة.
إذاً, القيمة الإفتراضية لأي حقل هي القيمة Null.

في حال أردت وضع قيمة إفتراضية يتم إعطاءها للحقل الفارغ بدلاً من القيمة Null, يمكنك تحديد القيمة التي تريد وضعها بشكل إفتراضي عند تعريف العامود.

ملاحظة: عند وضع قيمة إفتراضية يجب مراعاة نوع القيم التي يمكن تخزينها في الجدول, فمثلاً إن كان نوع الجدول INT يجب أن تختار قيمة إفتراضية نوعها INT مثل الرقم 0 و ليس قيمة من نوع آخر مثل الكلمة 'Empty' لأنه بالمنطق لا يمكنك تخزين نص في حقل مخصص لتخزين رقم.


الشكل العام لإستخدامه

      ALTER TABLE table_name (
      column_name datatype DEFAULT default_value
      );
    

  • مكان الكلمة table_name نضع إسم الجدول الذي نريد إجراء تعديلات عليه.

  • مكان الكلمة column_name نحدد إسم العامود.

  • مكان الكلمة datatype نحدد نوع البيانات التي يمكن تخيزنها في العامود.

  • بعد الكلمة DEFAULT نضع القيمة الإفتراضية التي نريد وضعها للحقول الموضوعة تحت هذا العامود.

تجهيز قاعدة البيانات التي سنطبق عليها

قم بتنفيذ الإستعلام التالي حتى تنشئ قاعدة بيانات جديدة إسمها harmash و تنشئ فيها جدول إسمه users.
العامود country وضعنا فيه الكلمة 'Unknown' كقيمة إفتراضية.

الإستعلام

        -- سيتم حذفها harmash في حال كان يوجد بالأساس قاعدة بيانات إسمها
        DROP DATABASE IF EXISTS harmash;

        -- harmash هنا قمنا بإنشاء قاعدة بيانات جديدة إسمها
        CREATE DATABASE harmash;

        -- harmash هنا قمنا بتحديد أن أي إستعلام جديد سيتم تنفيذه على قاعدة البيانات
        USE harmash;

        -- يتألف من 4 أعمدة users هنا قمنا بإنشاء جدول جديد إسمه
        -- لأننا قمنا بتحديدها قبل إستدعاء هذا الأمر harmash سيتم إنشاء هذا الجدول بداخل قاعدة البيانات
        CREATE TABLE users (
        id         INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
        first_name VARCHAR(50),
        last_name  VARCHAR(50),
        country    VARCHAR(50) DEFAULT 'Unknown'
        );
      

بعد تنفيذ الإستعلام السابق في phpMyAdmin سيتم إنشاء قاعدة البيانات harmash و إنشاء الجدول users بداخلها.
قم بالنقر على إسم قاعدة البيانات harmash من القائمة اليسرى حتى تبدأ بالتعامل معها و تطبيق ما ستتعلمه في هذا الدرس.

مثال يوضح متى يتم إضافة القيم الإفتراضية التي وضعناها للأعمدة

الإستعلام التالي يضيف 4 مستخدمين في الجدول users. بعدها يعرض كل الأسطر التي تم إضافتها في الجدول.

الإستعلام

        -- فقط first_name مع إدخال قيمة في الحقل users هنا قمنا بإضافة سطر في الجدول
        INSERT INTO users (first_name) VALUES ("Mhamad");

        -- last_name و قيمة في الحقل first_name مع إدخال قيمة في الحقل users هنا قمنا بإضافة سطر في الجدول
        INSERT INTO users (first_name, last_name) VALUES ("Ayman", "Mostafa");

        -- مع إدخال قيم لكل الحقول الموجودة في السطر users هنا قمنا بإضافة سطر في الجدول
        INSERT INTO users (first_name, last_name, country) VALUES ("Ahmad", "Naji", "Syria");

        -- مع إدخال قيم لكل الحقول الموجودة في السطر users هنا قمنا بإضافة سطر في الجدول
        INSERT INTO users (first_name, last_name, country) VALUES ("Saly", "Harmush", null);

        -- users هنا قمنا بعرض جميع البيانات الموجودة في الجدول
        SELECT * FROM users;
      

سنحصل على النتيجة التالية عند تنفيذ الإستعلام.

id first_name last_name country
1 Mhamad NULL Unknown
2 Ayman Mostafa Unknown
3 Ahmad Naji Syria
4 Saly Harmush NULL

نلاحظ أن العامود country يظهر فيه عدة قيم من بينها القيمة NULL و القيمة الإفتراضية Unknown و سنضع قراءة تحليلة لكل سطر تم إدخاله حتى تعرف السبب.


السطر الأول

تم وضع القيمة NULL في الحقل last_name لأننا لم ندخل قيمة فيه.
تم وضع القيمة Unknown في الحقل country لأنها القيمة الإفتراضية التي حددنا أنه يتم وضعها في حال لم ندخل قيمة فيه.



السطر الثاني

تم وضع القيمة Unknown في الحقل country لأنها القيمة الإفتراضية التي حددنا أنه يتم وضعها في حال لم ندخل قيمة فيه.


السطر الثالث

لم يتم وضع القيمة الإفتراضية Unknown في الحقل country لأنه تم تمرير قيمة له.


السطر الرابع

لم يتم وضع القيمة الإفتراضية Unknown في الحقل country لأنه تم عمداً تمرير القيمة NULL في الإستعلام.

طريقة وضع قيمة إفتراضية لعامود معرف مسبقاً في الجدول

في حال أردت وضع قيمة إفتراضية لعامود موجود مسبقاً في الجدول, يمكنك تعديل العامود بواسطة الأمر ALTER.
طريقة وضع القيمة الإفتراضية لعامود تم تعريفه مسبقاً, تختلف من قاعدة بيانات لأخرى و لكن الفكرة هي نفسها تماماً.


في قواعد بيانات MySQL

      ALTER TABLE table_name
      MODIFY column_name datatype DEFAULT 'default_value'; 
    

في قواعد بيانات SQL Server

      ALTER TABLE table_name
      ADD CONSTRAINT df_column_name
      DEFAULT 'default_value' FOR column_name; 
    

في قواعد بيانات Oracle

      ALTER TABLE table_name
      MODIFY column_name DEFAULT 'default_value'; 
    

في قواعد بيانات Access

      ALTER TABLE table_name
      ALTER COLUMN column_name SET DEFAULT 'default_value'; 
    

مثال

في حال أردنا وضع الكلمة Missed كقيمة إفتراضية للحقل last_name في أي سطر جديد يتم إضافته, نكتب الإستعلام كالتالي في قواعد بيانات MySQL.

الإستعلام

        ALTER TABLE users                                 -- users هنا قمنا بتحديد أننا نريد إجراء تعديل على بنية الجدول
        MODIFY last_name VARCHAR(50) DEFAULT 'Missed';    -- 'Missed' الإفتراضية إلى last_name هنا قمنا بتحديد أننا نريد تغيير قيم العامود
      

طريقة إلغاء القيمة إفتراضية التي وضعناها للعامود

في حال أردت عدم وضع قيمة إفتراضية للعامود, أي إلغاء وضع القيمة الإفتراضية التي وضعتها سابقاً للعامود. يمكنك تعديل العامود بواسطة الأمر ALTER.
طريقة غلغاء القيمة الإفتراضية للعامود, تختلف من قاعدة بيانات لأخرى و لكن الفكرة هي نفسها تماماً.


طريقة إلغاء القيمة إفتراضية التي وضعناها للعامود في قواعد بيانات MySQL

      ALTER TABLE table_name
      ALTER column_name DROP DEFAULT;
    

في قواعد بيانات SQL Server / Oracle / Access

      ALTER TABLE table_name
      ALTER COLUMN column_name DROP DEFAULT;
    

مثال

في حال أردنا عدم وضع قيمة إفتراضية لقيم العامود country, نكتب الإستعلام كالتالي في قواعد بيانات MySQL.

الإستعلام

        ALTER TABLE users              -- users هنا قمنا بتحديد أننا نريد إجراء تعديل على بنية الجدول
        ALTER country DROP DEFAULT;    -- country هنا قمنا بتحديد أننا نريد إزالة القيمة الإفتراضية الموضوعة على العامود
      

مفهوم المفتاح الرئيسي ( Primary Key )

المفتاح الرئيسي هو العامود الذي نضعه في الجدول بهدف جعلنا قادرين على الوصول إلى كل سطر فيه بسهولة سواء بهدف الحصول على قيمه, تعديلها أو لحذف السطر كما هو من الجدول. بالإضافة إلى ذلك, فأننا نستخدمه لربط قيم الجداول ببعضها البعض و هذه الأمور سبق و شرحناها بتفصيل ممل.

كمثال بسيط, في الجدول التالي يمكننا الوصول لأي سطر من خلال رقم id السطر.

id first_name last_name
1 Ahmad Eid
2 Ramez Morad
3 Hassan Mortada
4 Saad Alkassem
5 Zaher Fahmi

معلومة تقنية

العامود الذي يتم تعيينه كمفتاح رئيسي لا يسمح بأن يتم تخزين قيم مكررة فيه.
بمعنى آخر, جميع قيم المفتاح الرئيسي تعتبر موحدة ( UNIQUE ) حتى و إن لم نعرفها كذلك.


تعيين المفتاح الرئيسي

في أغلب الأوقات, تحتاج وضع عامود واحد في الجدول ليكون بمثابة المفتاح الرئيسي للوصول لكل سطر فيه و هذا الأمر تطرقنا له كثيراً في الدروس السابقة حيث كنا نضع عامود واحد إسمه id كمفتاح رئيسي في أي جدول ننشئه, و لكن في بعض الحالات قد تجد أنك بحاجة إلى تكوين مفتاح رئيسي في الجدول من عامودين أو أكثر و ليس من عامود واحد فقط.

تعيين العامود كمفتاح رئيسي عند إنشاء الجدول

إذا كنت تنوي تعيين عامود كمفتاح رئيسي في الجدول, يمكنك إضافة الخاصية PRIMARY KEY إلى تعريف العامود فقط.
عندها سيصبح العامود لا يقبل أن تخزن فيه قيم موحدة و سيجبرك على إدخال قيم بنفسك له.


المثال الأول تعيين العامود كمفتاح رئيسي عند إنشاء الجدول

في المثال التالي, عند إنشاء جدول جديد قمنا بإنشاء عامود نوعه INT و تعيينه كمفتاح رئيسي في الجدول.

الإستعلام

        CREATE TABLE users (
        id         INT PRIMARY KEY,   -- users كمفتاح رئيسي للجدول id هنا قمنا بتعريف العامود
        first_name VARCHAR(50),
        last_name  VARCHAR(50)
        );
      

المثال الثاني تعيين العامود كمفتاح رئيسي عند إنشاء الجدول

في المثال التالي, عند إنشاء جدول جديد قمنا بإنشاء عامود نوعه VARCHAR و تعيينه كمفتاح رئيسي في الجدول.

الإستعلام

        CREATE TABLE users (
        username   VARCHAR(50) PRIMARY KEY,   -- users كمفتاح رئيسي للجدول username هنا قمنا بتعريف العامود
        first_name VARCHAR(50),
        last_name  VARCHAR(50)
        );
      

تعيين أكثر من عامود كمفتاح رئيسي عند إنشاء الجدول

لتعيين أكثر من عامود كمفتاح رئيسي في الجدول, يجب إضافة قيد ( CONSTRAINT ) على الجدول نحدد فيه كل الأعمدة التي نريدها أن تكون بمثابة مفتاح رئيسي.
إنتبه لنقطة مهمة جداً, و هي أنه عند تعيين عامودين كمفتاح رئيسيي, فإن كل عامود منهم يعتبر عامود عادي جداً و لكنهما مع بعض يشكلان مفتاح واحد.


الشكل العام لإضافة قيد خاص بتعيين المفتاح الرئيسي

لإضافة CONSTRAINT خاص بتحديد الأعمدة التي نريد تعيينها بمثابة PRIMARY KEY واحد يجب كتابتة الإستعلام كالتالي.

      CREATE TABLE table_name (
      column1 datatype NOT NULL,
      column2 datatype NOT NULL,
      ...,
      CONSTRAINT PK_table_name PRIMARY KEY (column1, column2)
      );
    

مثال

في المثال التالي, عند إنشاء جدول جديد قمنا بإنشاء عامود نوعه INT و عامود نوعه VARCHAR و تعيينهما كمفتاح رئيسي في الجدول.

لتعيين هذين العامودين كمفتاح واحد فعلنا التالي:

  • العامود id قمنا بتعريفه INT NOT NULL لأننا نريد إدخال قيمة رقمية و أن لا يسمح بترك الحقل فارغاً.

  • العامود username قمنا بتعريفه VARCHAR(50) NOT NULL لأننا نريد إدخال قيم نصية لا يتعدى طولها 50 حرف و أن لا يسمح بترك الحقل فارغاً.

  • قمنا بإضافة CONSTRAINT نوعه PRIMARY KEY بإسم pk_users ليكون مشابهاً لإسم الجدول فقط.

  • ذكرنا فيه إسم العامودين اللذين نريد وضعهما كمفتاح رئيسي في الجدول بين أقواس القيد PRIMARY KEY.


الإستعلام

        CREATE TABLE users (
        id         INT NOT NULL,
        username   VARCHAR(50) NOT NULL,
        first_name VARCHAR(50),
        last_name  VARCHAR(50),
        CONSTRAINT pk_users PRIMARY KEY (id, username)
        );
      

الآن, لاحظ أن العامود username يمكنه أن يحتوي على قيم مكررة و العامود id كذلك أيضاً.
السببب الذي يجعل ذلك لا يشكل مشكلة هو أن كل عامود منهما لا يشكل مفتاح رئيسي لوحده, بل قيمتهما مع بعضهما هي ما تشكل المفتاح الرئيسي في كل سطر.

id username first_name last_name
1 ahmad Ahmad Eid
2 ahmad Ahmad Rashad
2 hassan Hassan Mortada
3 saad Saad Alkassem

  • 1ahmad هو مفتاح السطر الأول.

  • 2ahmad هو مفتاح السطر الثاني.

  • 2hassan هو مفتاح السطر الثالث.

  • 3saad هو مفتاح السطر الرابع.


إذاً في حال كان المفتاح الرئيسي يتألف من أكثر من عامود, يمكننا جعل الأعمدة تقبل أن يكون فيها قيم مكررة و لكن لا يمكننا تكرار نفس قيم الأعمدة الموجودة على نفس السطر في سطر آخر.

في الجدول السابق ممنوع تكرار نفس الـ id و الـ username في أكثر من سطر. أي ممنوع إدخال قيم مكررة كالتالي.

id username first_name last_name
1 ahmad Ahmad Eid
1 ahmad Ahmad Rashad

تعيين المفتاح الرئيسي بعد إنشاء الجدول

إذا كنت تنوي تعيين المفتاح الرئيسي بعد إنشاء الجدول, يجب أن تتأكد أولاً أن العامود أو الأعمدة التي تنوي وضعها كمفتاح رئيسي لا تحتوي على حقول فارغة.


في حال كنت تنوي تعيين عامود واحد كمفتاح رئيسي, يمكنك استخدام الأمر ALTER لتعيينه كالتالي.

      ALTER TABLE table_name
      ADD PRIMARY KEY (column_name);
    

في حال كنت تنوي تعيين أكثر من عامود واحد كمفتاح رئيسي, يجب إضافة CONSTRAINT لتعيينه كالتالي.

      ALTER TABLE table_name
      ADD CONSTRAINT pk_constraint_name PRIMARY KEY (column1, column2, ..); 
    


المثال الأول تعيين المفتاح الرئيسي بعد إنشاء الجدول

الإستعلام التالي يقوم بتعيين العامود id الموجود في الجدول users كمفتاح رئيسي.

الإستعلام

        ALTER TABLE users
        ADD PRIMARY KEY (id);
      

المثال الثاني تعيين المفتاح الرئيسي بعد إنشاء الجدول

الإستعلام التالي يقوم بتعيين العامودين id و username الموجودين في الجدول users كمفتاح رئيسي.

الإستعلام

        ALTER TABLE users
        ADD CONSTRAINT pk_users PRIMARY KEY (id, username); 
      

حذف المفتاح الرئيسي

في حال أردت إلغاء المفتاح الرئيسي الذي قمت بتعيينه سابقاً. يمكنك تعديل العامود بواسطة الأمر ALTER مع الإشارة إلى أن طريقة فعل ذلك تختلف من قاعدة بيانات لأخرى.


في قواعد بيانات MySQL

      ALTER TABLE table_name
      DROP PRIMARY KEY;
    

الإستعلام التالي يقوم بإلغاء تعيين المفتاح الرئيسي الموضوع في الجدول users.
هنا لا داعي لذكر إسم المفتاح الرئيسي الذي تنوي إلغاؤه.

مثال

        ALTER TABLE users
        DROP PRIMARY KEY;
      


في قواعد بيانات SQL Server / Oracle / Access

      ALTER TABLE table_name
      DROP CONSTRAINT pk_constraint_name;
    

الإستعلام التالي يقوم بحذف القيد pk_users الذي تم وضعه سابقاً لتعيين المفتاح الرئيسي في الجدول users.
هنا يجب ذكر إسم القيد الذي يمثل المفتاح الرئيسي الذي تنوي إلغاؤه.

مثال

        ALTER TABLE users
        DROP CONSTRAINT pk_users;
      

المفتاح الأجنبي ( Foreign Key )

المفتاح الأجنبي هو العامود الذي نضع فيه قيم مفتاح رئيسي موجود في جدول آخر بهدف ربطهما مع بعض.
فمثلاً في الجدول users, العامود country_id يعتبر مفتاح أجنبي لأن قيمه تشير لقيم العامود id في الجدول countries.

نلاحظ أن قيم العامود country_id يجب أن تشير لقيم العامود id في الجدول countries لتخزين قيم صحيحة.

لضمان أن لا يتم تخزين أرقام خاطئة في العامود country_id يجب تعيين هذا العامود كمفتاح أجنبي بالنسبة للعامود id الموجود في الجدول countries.
عندها في حال تمرير قيمة للحقل country_id غير موجودة في الجدول countries, لن يتم قبولها و سيظهر لك خطأ يخبرك أنه لا يمكنك ذلك.

تعيين المفتاح الرئيسي و المفتاح الأجنبي عند إنشاء الجدول

في الدرس السابق شاهدنا أنه يوجد طريقتين لتحديد المفتاح الرئيسي, الأولى يمكن استخدامها في حال كان المفتاح الرئيسي عبارة عن عامود واحد, و الثانية إستخدامها إجباري في حال كان المفتاح الرئيسي يتكون من أكثر من عامود.

المفتاح الأجنبي كذلك الأمر, حيث يمكن تعيين واحد أو أكثر كمفتاح أجنبي في الجدول.


لتعيين عامود واحد أو أكثر كمفتاح أجنبي في الجدول, يمكننا وضع قيد ( CONSTRAINT ) لتحديده.
هذه الطريقة متاحة في جميع قواعد البيانات و استخدامها سهل لأننا نقوم بتعريف الأعمدة و في النهاية نحدد مفاتيح الجدول.
المميز في هذه الطريقة, أنك في حال قررت إلغاء المفتاح الأجنبي لاحقاً, يمكنك فعل ذلك بكل سهولة لأنك ستعتمد على إسم القيد الذي وضعته بنفسك من أجل إلغائه.

مثال

        CREATE TABLE users (
        id         INT NOT NULL,
        first_name VARCHAR(50),
        last_name  VARCHAR(50),
        country_id INT NOT NULL,
        -- users كمفتاح رئيسي في الجدول id هنا قمنا بتعيين العامود
        PRIMARY KEY (id),
        -- countries الموجود في الجدول id هو مفتاح ثانوي بالنسبة للعامود country_id يحدد أن العامود fk_country_user هنا قمنا بوضع قيد بإسم
        CONSTRAINT fk_country_user FOREIGN KEY (country_id) REFERENCES countries(id)
        );
      


تعيين المفاتيح في قواعد بيانات MySQL

يمكنك اتباع الطريقة التالية لتعريف المفاتيح مع الإشارة إلى أنه يفضل وضع قيد بشكل يدوي لأن التعامل معه مستقبلاً سيكون أسهل عليك إن أردت ذلك.

مثال

        CREATE TABLE users (
        id         INT NOT NULL,
        first_name VARCHAR(50),
        last_name  VARCHAR(50),
        country_id INT NOT NULL,
        -- users كمفتاح رئيسي في الجدول id هنا قمنا بتعيين العامود
        PRIMARY KEY (id),
        -- countries الموجود في الجدول id هو مفتاح ثانوي بالنسبة للعامود country_id هنا قمنا بوضع قيد يحدد أن العامود
        FOREIGN KEY (country_id) REFERENCES countries(id)
        );
      


تعيين المفاتيح في قواعد بيانات SQL Server / Oracle / Access

يمكنك اتباع الطريقة التالية لتعريف المفاتيح مع الإشارة إلى أنه يفضل وضع قيد بشكل يدوي لأن التعامل معه مستقبلاً سيكون أسهل عليك إن أردت ذلك.

مثال

        CREATE TABLE users (
        id         INT NOT NULL PRIMARY KEY,                 -- users كمفتاح رئيسي في الجدول id هنا قمنا بتعيين العامود
        first_name VARCHAR(50),
        last_name  VARCHAR(50),
        country_id INT FOREIGN KEY REFERENCES countries(id)  -- countries الموجود في الجدول id هو مفتاح ثانوي بالنسبة للعامود country_id يحدد أن العامود fk_country_user هنا قمنا بوضع قيد بإسم
        );
      

تعيين المفتاح الأجنبي بعد إنشاء الجدول

في حال كنت تنوي تعيين عامود واحد كمفتاح أجنبي, يمكنك استخدام الأمر ALTER لتعيينه.
في حال كنت تنوي تعيين أكثر من عامود كمفتاح أجنبي, يجب إضافة CONSTRAINT لتعيينه.

ملاحظة: إضافة CONSTRAINT أمر يحتاج كتابة إستعلام أطول قليلاً و لكن التعامل معه مستقبلاً سيكون أسهل عليك إن أردت ذلك.


المثال الأول

الإستعلام التالي يقوم بتعيين العامود country_id الموجود في الجدول users كمفتاح أجنبي بالنسبة للعامود id الموجود في الجدول countries.

الإستعلام

        ALTER TABLE users                                       -- users هنا قمنا بتحديد أننا نريد إجراء تعديل على بنية الجدول
        ADD FOREIGN KEY (contry_id) REFERENCES countries(id);   -- countries الموجود في الجدول id هو عامود أجنبي بالنسبة للعامود country_id هنا قمنا بتحديد أن العامود
      

المثال الثاني

الإستعلام التالي يقوم بتعيين العامود country_id الموجود في الجدول users كمفتاح أجنبي بالنسبة للعامود id الموجود في الجدول countries.

الإستعلام

        ALTER TABLE users                                     -- users هنا قمنا بتحديد أننا نريد إجراء تعديل على بنية الجدول
        ADD CONSTRAINT fk_country_user                        -- users و countries للإشارة إلى أنه مفتاح أجنبي بين الجدولين fk_country_user هنا قمنا بوضع قيد إسمه
        FOREIGN KEY (country_id) REFERENCES countries(id);    -- countries الموجود في الجدول id يستخدم قيم العامود country_id هنا قمنا بتحديد أن العامود
      

حذف قيد المفتاح الأجنبي

طريقة إلغاء قيد ( CONSTRAINT ) المفتاح الأجنبي الذي قمت بتعيينه سابقاً, تختلف من قاعدة بيانات لأخرى.


في قواعد بيانات MySQL

      ALTER TABLE table_name
      DROP FOREIGN KEY fk_constraint_name;
    

الإستعلام التالي يقوم بإلغاء قيد مفتاح أجنبي إسمه fk_country_user إفترضنا أنه موضوع في الجدول users.

مثال

        ALTER TABLE users
        DROP FOREIGN KEY fk_country_user;
      


في قواعد بيانات SQL Server / Oracle / Access

      ALTER TABLE table_name
      DROP CONSTRAINT fk_constraint_name;
    

الإستعلام التالي يقوم بإلغاء قيد مفتاح أجنبي إسمه fk_country_user إفترضنا أنه موضوع في الجدول users.

مثال

        ALTER TABLE users
        DROP CONSTRAINT fk_country_user;
      

مفهوم وضع شروط لتخزين القيم

عند تخزين البيانات في أي جدول, نلاحظ أنه علينا فقط إحترام نوع البيانات التي يجب تخزينها في كل حقل. فمثلاً العامود الذي نوعه INT يمكن أن نخزن فيه أعداد صحيحة, و العامود الذي نوعه VARCHAR يمكن أن نخزن فيه نصوص و هكذا.

ماذا لو أردت تحديد القيم التي يسمح بتخزينها, فمثلاً كنت تريد تخزين أعداد صحيحة و لكنك تريد أن تكون هذه الأعداد بين 0 و 100 فقط.
في هذه الحالة يمكنك إضافة قيد ( CONSTRAINT ) نوعه CHECK تضع فيه شرط قبول أي قيمة سيتم تخزينها في العامود.

وضع شرط على العامود عند إنشاء الجدول

لوضع شرط على القيم التي يمكن تخزينها في العامود, يمكننا وضع قيد ( CONSTRAINT ) لتحديده.
هذه الطريقة متاحة في جميع قواعد البيانات و استخدامها سهل لأننا نقوم بتعريف الأعمدة و في النهاية نضيف القيد.
المميز في هذه الطريقة, أنك في حال قررت إلغاء القيد لاحقاً, يمكنك فعل ذلك بكل سهولة لأنك ستعتمد على إسم القيد الذي وضعته بنفسك من أجل إلغائه.

مثال

        CREATE TABLE books (
        id    INT NOT NULL,
        title VARCHAR(255),
        pages INT,
        price DECIMAL(5,2),
        -- يجب أن تكون قيمته أقل من 50 price يجب أن تكون قيمته أكبر من 0, و العامود pages يحدد أن العامود chk_book_pages_and_price هنا قمنا بوضع قيد بإسم
        CONSTRAINT chk_book_pages_and_price CHECK (pages > 0 AND price < 50)
        );
      


طريقة متاحة في قواعد بيانات MySQL

يمكنك اتباع الطريقة التالية لوضع الشروط مع الإشارة إلى أنه يفضل وضع CONSTRAINT و إعطاؤه إسم لأن التعامل معه مستقبلاً سيكون أسهل عليك إن أردت ذلك.

مثال

        CREATE TABLE books (
        id    INT NOT NULL,
        title VARCHAR(255),
        pages INT,
        price DECIMAL(5,2),
        -- يجب أن تكون قيمته يجب أن تكون أقل من 50 price يجب أن تكون قيمته أكبر من 0, و العامود pages هنا قمنا بوضع قيد يحدد أن العامود
        CHECK (pages > 0 AND price <= 50)
        );
      


طريقة متاحة في قواعد بيانات SQL Server / Oracle / Access

يمكنك اتباع الطريقة التالية لوضع الشروط مع الإشارة إلى أنه يفضل وضع CONSTRAINT و إعطاؤه إسم لأن التعامل معه مستقبلاً سيكون أسهل عليك إن أردت ذلك.

مثال

        CREATE TABLE books (
        id    INT NOT NULL,
        title VARCHAR(255),
        pages INT CHECK (pages  > 0),           -- قيمته يجب أن تكون أكبر من 0 pages هنا قمنا بوضع قيد يحدد أن العامود
        price DECIMAL(5,2) CHECK (price < 50)   -- قيمته يجب أن تكون أقل من 50 price هنا قمنا بوضع قيد يحدد أن العامود
        );
      

وضع شرط على العامود بعد إنشاء الجدول

في حال كنت تنوي وضع شروط على القيم التي سيتم تخزينها في عامود واحد, يمكنك استخدام الأمر ALTER لوضعها كالتالي.

      ALTER TABLE table_name
      ADD CHECK (conditions);
    

في حال كنت تنوي وضع شروط على القيم التي سيتم تخزينها في أكثر من عامود, يجب إضافة CONSTRAINT لوضعها كالتالي.
ملاحظة: إضافة CONSTRAINT أمر يحتاج كتابة إستعلام أطول قليلاً و لكن التعامل معه مستقبلاً سيكون أسهل عليك إن أردت ذلك.

      ALTER TABLE table_name
      ADD CONSTRAINT check_constraint_name
      CHECK (conditions);
    


المثال الأول

الإستعلام التالي يقوم بوضع شرط على القيم التي يمكن تخزينها في العامود price و يحدد أن القيم التي تضاف فيه يجب أن تكون أقل من 50.

الإستعلام

        ALTER TABLE books          -- books هنا قمنا بتحديد أننا نريد إجراء تعديل على بنية الجدول
        ADD CHECK (price < 50);    -- قيمته يجب أن تكون أقل من 50 price هنا قمنا بوضع قيد يحدد أن العامود
      

المثال الثاني

الإستعلام التالي يقوم بوضع شرط على القيم التي يمكن تخزينها في العامود price يحدد أن القيم التي تضاف فيه يجب أن تكون أصغر من 0, و شرط آخر على العامود price يحدد أن القيم التي تضاف فيه يجب أن تكون أقل من 50.

الإستعلام

        ALTER TABLE books                          -- books هنا قمنا بتحديد أننا نريد إجراء تعديل على بنية الجدول
        ADD CONSTRAINT chk_book_pages_and_price    -- chk_book_pages_and_price هنا قمنا بوضع قيد بإسم
        CHECK (pages > 0 AND price < 50)           -- يجب أن تكون قيمته أقل من 50 price يجب أن تكون قيمته أكبر من 0, و العامود pages يحدد أن العامود
      

حذف قيد الشرط الموضوع على العامود

طريقة إلغاء قيد ( CONSTRAINT ) الشرط الموضوع على القيم التي يمكن تخزينها في العامود تختلف من قاعدة بيانات لأخرى.


في قواعد بيانات MySQL

      ALTER TABLE table_name
      DROP CHECK chk_constraint_name;
    

الإستعلام التالي يقوم بإلغاء قيد إسمه chk_book_pages.

مثال

        ALTER TABLE books             -- books هنا قمنا بتحديد أننا نريد إجراء تعديل على بنية الجدول
        DROP CHECK chk_book_pages;    -- الموضوع على أحد أعمدته chk_book_pages هنا قمنا بتحديد أننا نريد إلغاء القيد
      


في قواعد بيانات SQL Server / Oracle / Access

      ALTER TABLE table_name
      DROP CONSTRAINT chk_constraint_name;
    

الإستعلام التالي يقوم بإلغاء قيد إسمه chk_book_pages.

مثال

        ALTER TABLE books                  -- books هنا قمنا بتحديد أننا نريد إجراء تعديل على بنية الجدول
        DROP CONSTRAINT chk_book_pages;    -- الموضوع على أحد أعمدته chk_book_pages هنا قمنا بتحديد أننا نريد إلغاء القيد
      

SQL مفهوم الـ Transaction

كلمة Transaction يقصد بها مجموعة من الأوامر إما أن تتنفذ جميعها بنجاح و إما لا يتم تنفيذها.

هناك حالات كثيرة تكون فيها مضطر فيها إلى تنفيذ مجموعة أوامر أو التراجع عن تنفيذها خاصةً إذا كنت تطور قاعدة بيانات لمشروع فيه معاملات مالية.
فمثلاً إذا أنك تنوي شراء برنامج من أي موقع إلكتروني, في هذه الحالة سيطلب منك الموقع إدخال معلومات بطاقتك المصرفية التي ستدفع من خلالها, بعد أن تدخل معلومات البطاقة, سيتم التأكد من أنك تملك المال المطلوب في البطاقة, إذا كنت تملك ثمن شراء البرنامج في بطاقتك, سيقوم الموقع بتسجيل أنه تم بيع نسخة من البرنامج لك, و ستجد أن الموقع أرسل لك كود تفعيل للبرنامج خاص بك, و ستجد أن بطاقتك المصرفية تم إقتطاع المبلغ الذي دفعته منها.

في حالة الشراء أونلاين قد تقع في مشاكل كارثية ما لم يتم تنفيذ جميع الخطوات بنجاح. فمثلاً في حال قمت بتحويل المال للموقع الإلكتروني و فجأةً تعطل الموقع قبل أن يسجل في قاعدة البيانات أنك قمت بشراء نسخة البرنامج و قبل أن يرسل لك كود تفعيل البرنامج الذي دفعت ثمنه.

كخلاصة للموضوع, Transaction يقصد بها محاولة تنفيذ جميع الأوامر و في حال فشل أي أمر موضوع موضوع يجب إلغاء تنفيذ كل الأوامر التي تم تنفيذها بنجاح.
قواعد بيانات MySQL - SQL Server - Access - Oracle - SQLite جميعها تتيح لك إمكانية وضع الكود في Transaction.

طريقة وضع الكود بداخل Transaction

في البداية, إذا كنت تتعامل مع قواعد بيانات MySQL يجب أولاً إعلام قاعدة البيانات بأنك تريد إيقاف الحفظ التلقائي و هذه الخطوة تحتاج أن تفعلها مرة واحدة فقط و ليس عند إرسال كل إستعلام.

لإعلام قاعدة البيانات بأنك تريد إيقاف الحفظ التلقائي, يجب أن تنفذ الإستعلام التالي.

      SET autocommit = OFF;
    


الآن, عليك معرفة أن طريقة وضع الكود بداخل Transaction تختلف قليلاً من قاعدة بيانات لأخرى و لكن الفكرة هي نفسها تماماً.


في قواعد بيانات MySQL / Oracle / SQLite

      BEGIN;
      -- statements
      COMMIT;
    

  • الكلمة BEGIN نضعها لتحديد أين تبدأ الـ Transaction.

  • مكان الكلمة -- statements نضع كل الأوامر التي نريدها إما أن تتنفذ جميعها بنجاح و إما لا يتم تنفيذها.

  • الكلمة COMMIT نضعها لتحديد أين تنتهي الـ Transaction مما يعني أنه عند الوصول لها بدون أي مشاكل سيتم الموافقة على حفظ كل العمليات التي تم إجراءها.


ملاحظة

إذا كنت تستخدم قواعد بيانات SQL Server أو Access فكل ما عليك فعله هو تبديل كلمة BEGIN; بكلمة BEGIN TRANSACTION;.


مثال

في الإستعلام التالي, إفترضنا أننا نقوم بتخزين عملية شراء لمنتج.

  • المنتج الذي تم بيعه إفترضنا أنه يملك id يساوي 33.

  • الشخص الذي قام بشراء المنتج إفترضنا أنه يملك يملك id يساوي 1.

إذا تمت عملية البيع بنجاح, يجب إنقاص كمية المنتج الذي تم بيعه في جدول المنتجات products واحداً.
أيضاً, يجب إضافة id الشخص الذي قام بالشراء و id المنتج الذي تم بيعه في جدول الطلبيات orders.

بما أنه يجب تنفيذ العمليتين سويةً أو عدم تنفيذهما من الأساس في حال حدث خطأ ما, سنقوم بوضع العملية بداخل Transaction.

الإستعلام

        -- Transaction هنا قمنا ببدء
        BEGIN;

        -- يساوي 33 بهدف إنقاصها واحداً id للمنتج الذي يملك available_quantity هنا إفترضنا أننا نريد تحديث قيمة الحقل
        UPDATE products SET available_quantity = 9 WHERE id = 33;

        -- المنتج في جدول الطلبيات id المشتري و id سيتم إضافة أن available_quantity إذا لم يحدث مشكلة عند محاولة إنقاص 1 من قيمة الحقل
        INSERT INTO orders (user_id, product_id) values (1, 33);

        -- سيتم حفظ التغيرات في قاعدة البيانات Transaction إذا لم يحدث أي خطأ في الأوامر الموضوعة في الـ
        COMMIT;
      

التراجع عن تنفيذ أوامر الـ Transaction

لحفظ كل التغيرات التي تم إجراءها بداخل Transaction قلنا أننا يجب أن ننفذ الأمر COMMIT; لإعلام قاعدة البيانات أننا نريد حفظ كل العمليات التي تم إجراءها.
الآن للتراجع عن كل التغيرات التي تم إجراءها على قاعدة البيانات, يمكننا أن ننفذ الأمر ROLLBACK; ما لم يتم تنفيذ الأمر COMMIT; لأنه لا يمكننا التراجع عن تنفيذ الأوامر في حال تم تنفيذها.


مثال

في الإستعلام التالي, إفترضنا أننا نقوم بتخزين عملية شراء لمنتج و لكننا في النهاية قررنا إلغاء العملية.

  • المنتج الذي تم بيعه إفترضنا أنه يملك id يساوي 33.

  • الشخص الذي قام بشراء المنتج إفترضنا أنه يملك يملك id يساوي 1.

إذا تمت عملية البيع بنجاح, يجب إنقاص كمية المنتج الذي تم بيعه في جدول المنتجات products واحداً.
أيضاً, يجب إضافة id الشخص الذي قام بالشراء و id المنتج الذي تم بيعه في جدول الطلبيات orders.

بما أنه يجب تنفيذ العمليتين سويةً أو عدم تنفيذهما من الأساس في حال حدث خطأ ما, سنقوم بوضع العملية بداخل Transaction.
في النهاية, بما أننا سنقوم بإلغاء تنفيذ العمليتين في النهاية, سنستدعي الأمر ROLLBACK; بدل استدعاء الأمر COMMIT;.

الإستعلام

        -- Transaction هنا قمنا ببدء
        BEGIN;

        -- يساوي 33 بهدف إنقاصها واحداً id للمنتج الذي يملك available_quantity هنا إفترضنا أننا نريد تحديث قيمة الحقل
        UPDATE products SET available_quantity = 9 WHERE id = 33;

        -- المنتج في جدول الطلبيات id المشتري و id سيتم إضافة أن available_quantity إذا لم يحدث مشكلة عند محاولة إنقاص 1 من قيمة الحقل
        INSERT INTO orders (user_id, product_id) values (1, 33);

        -- INSERT و UPDATE للتراجع عن تنفيذ الأمرين ROLLBACK هنا قمنا باستدعاء الأمر
        ROLLBACK;
      

مفهوم الفهارس و أنواعها

أي كتاب ورقي تقرؤه تجد في بدايته أو في نهايته فهرس يساعدك على البحث فيه بشكل سريع. فمثلاً في أغلب كتب العلوم تجد في أول الكتاب فهرس يخبرك في أي صفحة يبدأ كل درس, و في القرآن الكريم أيضاً تجد في نهايته فهرس يمكنك من خلاله معرفة في أي صفحة تبدأ كل سورة بسهولة.

فكرة الفهارس في قواعد البيانات هي نفسها فكرة الفهارس في الكتب الورقية و لكنها تختلف في التطبيق, فمثلاً في قواعد البيانات أنت تقوم بإعداد الفهارس لجعل قاعدة البيانات قادرة على البحث و إيجاد المعلومات بشكل أسرع و ليس لكي تقرأ محتوى الفهرس بنفسك.

لتسريع عملية البحث عن المعلومات عليك أولاً تحديد الأعمدة التي تنوي البحث من خلالها, فمثلاً إذا كنت تتعامل مع جدول يحفظ معلومات المستخدمين, و تريد إتاحة إمكانية البحث عن المستخدمين من خلال أسماء المستخدمين, فهنا سيكون عليك إضافة فهرس خاص لأسماء المستخدمين.

في قواعد البيانات يوجد نوعين أساسيين من الفهارس هما Clustered Indexes و Non Clustered Indexes و الإثنين فكرتهما تسهيل إيجاد المعلومات.


مفهوم الـ Clustered Indexes

الفهرس الذي من النوع Clustered يقوم بحفظ المعلومات من الأساس بشكل مرتب, فمثلاً عندما تقوم بتعيين أي عامود كمفتاح رئيسي ( Primary Key ) في الجدول, تقوم قاعدة البيانات بشكل تلقائي بإضافة فهرس له نوعه Clustered لهذا السبب كنا نجد id المستخدمين يتم إرجاعها بنفس الترتيب الذي تم فيه إضافتهم كالتالي.

id username first_name last_name
1 ahmad Ahmad Eid
2 ramez Ramez Morad
3 hassan Hassan Mortada
4 saad Saad Alkassem
5 zaher Zaher Fahmi


مفهوم الـ Non Clustered Indexes

الفهرس الذي من النوع Non Clustered فكرته وضع فهرس خارجي لأي عامود في الجدول يتم فيه الإشارة إلى بيانات الجدول الأساسي بشكل مرتب.
للدقة أكثر, الفهرس الخارجي يقوم بالإشارة إلى عنوان السطر في القرص الصلب ( Physical Address in Hard Drive ) الذي يحتوي على البيانات في الجدول.

كمثال بسيط, إذا أردنا وضع فهرس Non Clustered خاص للعامود username فهذا الفهرس سيحفظ قيم هذا العامود بشكل أبجدي مرتب و سيضع عنوان كل سطر في القرص الصلب تم منه إحضار القيم.
الطريقة التي يتم ترتيب القيم فيها لا يمكنك رؤيتها بعينك في قواعد البيانات و لكن يمكنك تخيلها كالتالي.

username address
ahmad AB123FD123142
hassan AB87393F83AD4
ramez F83AB8D47393A
saad 80DDC62384AFB
zaher BC01234ABDFE7

الآن في حال أردت البحث عن المستخدم من خلال الـ username الخاص به فإن هذه العملية ستكون سريعة جداً حتى لو كانت قاعدة البيانات تضم مليون مستخدم, لأن قاعدة البيانات ستتمكن من إيجاد إسم المستخدم بكل سهولة في الفهرس و من بعدها ستوجهك للسطر أو الأسطر التي تملك القيمة التي بحثت عنها في الجدول الحقيقي.


متى يجب إضافة فهرس؟

الفهرس تضيفه فقط في حال كنت تنوي البحث في الجدول من خلال قيم أعمدة محددة, عندها يمكنك أن تضيف فهرس لكل عامود تنوي البحث من خلاله.
فمثلاً تريد البحث من خلال إسم المستخدم يمكنك وضع فهرس على إسمه, تريد البحث من خلال بريد المستخدم الإلكتروني يمكنك وضع فهرس على بريده الإلكتروني, تريد البحث عن المستخدمين نسبةً لروابتهم, تريد البحث عن المستخدمين نسبةً لبلدانهم يمكنك وضع فهرس على بلدان المستخدمين و هكذا..

فائدة الفهارس لن تظهر لك إن كنت تتعامل مع قاعدة بيانات صغيرة لأنك لن تشعر بفرق السرعة, و لكنها ستظهر لك حين تتعامل مع قاعدة بيانات فيها آلاف و ملايين الأسطر عندها ستلاحظ فرق كبير في الأداء و سرعة ممتازة في جلب البيانات.


هل إضافة فهرس لها تأثير سلبي على الأداء؟

إضافة الفهرس تجعل إسترجاع المعلومات أسرع و لكنها تسبب بطئ عند إضافة أي معلومات جديدة في الجدول و سبب ذلك أنه كلما تم إجراء تعديل على قيمة موجودة في الجدول سيتم إعادة تحديث قيم كل فهرس مرتبط بالجدول.

طريقة إضافة فهرس

لإضافة فهرس جديد سواء لعامود واحد أو لأكثر من عامود في ذات الوقت, نستخدم الأمر CREATE INDEX لفعل ذلك.


الشكل العام لإضافة فهرس

      CREATE INDEX index_name
      ON table_name (colums); 
    

  • مكان الكلمة index_name نضع الإسم الذي نريد وضعه للفهرس.

  • مكان الكلمة table_name نضع إسم الجدول الذي سنضع الفهرس لأعمدته.

  • مكان الكلمة columns نضع إسم العامود الذي سيتم إنشاء الفهرس من أجله, و في حال أردت وضع أكثر من عامود يجب أن تضع فاصلة بين كل عامودين.


المثال الأول

الإستعلام التالي ينشئ فهرس إسمه idx_username خاص للعامود username الموجود في الجدول users.

الإستعلام

        CREATE INDEX idx_username
        ON users (username); 
      

المثال الثاني

الإستعلام التالي ينشئ فهرس إسمه idx_full_name خاص للعامودين first_name و last_name الموجودين في الجدول users.

الإستعلام

        CREATE INDEX idx_full_name
        ON users (first_name, last_name); 
      

طريقة إضافة فهرس يحتوي على قيم موحدة

لجعل العامود يقبل قيم موحدة يوجد ثلاث طرق يمكنك إتباع أحدها:

  • جعل نوع العامود من الأساس UNIQUE.

  • إضافة CONSTRAINT للعامود نوعه UNIQUE.

  • إضافة INDEX للعامود نوعه UNIQUE و هذا ما سنتعلمه الآن.


لإضافة فهرس جديد سواء لعامود واحد أو لأكثر من عامود في ذات الوقت و جعلهم يقبلوا قيم موحدة فقط, نستخدم الأمر CREATE UNIQUE INDEX لفعل ذلك.


الشكل العام لإضافة فهرس يحتوي على قيم موحدة فقط

      CREATE UNIQUE INDEX index_name
      ON table_name (colums); 
    

  • مكان الكلمة index_name نضع الإسم الذي نريد وضعه للفهرس.

  • مكان الكلمة table_name نضع إسم الجدول الذي سنضع الفهرس لأعمدته.

  • مكان الكلمة columns نضع إسم العامود الذي سيتم إنشاء الفهرس من أجله, و في حال أردت وضع أكثر من عامود يجب أن تضع فاصلة بين كل عامودين.


المثال الأول

الإستعلام التالي ينشئ فهرس قيمه موحدة, إسمه idx_username و هو خاص للعامود username الموجود في الجدول users.

الإستعلام

        CREATE UNIQUE INDEX idx_username
        ON users (username); 
      

المثال الثاني

الإستعلام التالي ينشئ فهرس قيمه موحدة, إسمه idx_full_name و هو خاص للعامودين first_name و last_name الموجودين في الجدول users.

الإستعلام

        CREATE UNIQUE INDEX idx_full_name
        ON users (first_name, last_name); 
      

طريقة حذف الفهرس

طريقة حذف الفهرس تختلف من قاعدة بيانات لأخرى و لكن الفكرة هي نفسها تماماً.


في قواعد بيانات MySQL

      ALTER TABLE table_name
      DROP INDEX index_name;
    

الإستعلام التالي يقوم بإلغاء قيد إسمه idx_username.

مثال

        ALTER TABLE users
        DROP INDEX idx_username; 
      

في قواعد بيانات SQL Server

      DROP INDEX table_name.index_name; 
    

الإستعلام التالي يقوم بإلغاء قيد إسمه idx_username.

مثال

        DROP INDEX users.idx_username; 
      

في قواعد بيانات Oracle / SQLite

      DROP INDEX index_name;
    

الإستعلام التالي يقوم بإلغاء قيد إسمه idx_username.

مثال

        DROP INDEX idx_username;
      

في قواعد بيانات Access

      DROP INDEX index_name ON table_name;
    

الإستعلام التالي يقوم بإلغاء قيد إسمه idx_username.

مثال

        DROP INDEX idx_username ON users;
      

العامل LIKE

العامل LIKE يستخدم عند الحاجة لتخصيص البحث في العامود الذي يحتوي على قيم نصية.
كمثال بسيط, في حال أراد المستخدم البحث عن كل كتاب عنوانه يتضمن كلمة 'Hero' يمكنك استخدام العامل LIKE مع الأمر SELECT لتحدد ذلك.


الشكل العام لإستخدام العامل LIKE

      SELECT column1, column2, ...
      FROM table_name
      WHERE column_name LIKE pattern;
    


  • مكان الكلمة column_name نضع إسم العامود الذي يحتوي على قيم نصية و التي نريد تخصيص البحص فيها.

  • مكان الكلمة pattern نضع نص يقال له نمط ( Pattern ) لأنه يحتوي على رموز تمثل الطريقة التي سيتم فيها البحث.

الأحرف و الرموز التي تستخدم لكتابة الأنماط

الأحرف و الرموز التي نضعها في أي نمط يقال لها Wildcards.
قد تختلف قليلاً الأحرف و الرموز التي نضعها في الأنماط من قاعدة بيانات لأخرى و لكن أغلبها متشابه.

في الجدول التالي وضعنا الأحرف و الرموز التي تستخدم في كتابة الأنماط مع تحديد قواعد البيانات التي تستخدم فيها.

الرمز إستخدامه
% يستخدم في حال عدم الإكتراث ما إذا كان يوجد شيء أم لا.
بالنسبة لقواعد البيانات التي يمكن استخدامه فيها فهي MySQL / SQL Server / Oracle.

مثال: 'Hello*'
هذا النمط يقصد منه أننا نريد أن يكون الحقل النصي يبدأ بكلمة Hello و لا يهم إن كان يوجد شيء بعدها أم لا.
* يستخدم في حال عدم الإكتراث ما إذا كان يوجد شيء أم لا.
بالنسبة لقواعد البيانات التي يمكن استخدامه فيها فهي Access.

مثال: 'Hello*'
هذا النمط يقصد منه أننا نريد أن يكون الحقل النصي يبدأ بكلمة Hello و لا يهم إن كان يوجد شيء بعدها أم لا.
_ يستخدم للإشارة إلى وجوب وجود أي حرف في المكان الذي يتم وضعه.
بالنسبة لقواعد البيانات التي يمكن استخدامه فيها فهي MySQL / SQL Server / Oracle.

مثال: 'm_n'
هذا النمط يقصد منه أننا نريد أن يكون الحقل النصي يبدأ بحرف m و يوجد حرفين بعده فقط مثل men - man - msn.
? يستخدم للإشارة إلى وجوب وجود أي حرف في المكان الذي يتم وضعه.
بالنسبة لقواعد البيانات التي يمكن استخدامه فيها فهي Access.

مثال: 'm?n'
هذا النمط يقصد منه أننا نريد أن يكون الحقل النصي يبدأ بحرف m و يوجد حرفين بعده فقط مثل men - man - msn.

تجهيز قاعدة البيانات التي سنطبق عليها

قم بتنفيذ الإستعلام التالي حتى تنشئ قاعدة بيانات جديدة إسمها harmash و تنشئ فيها جدول إسمه users يحتوي على بيانات 10 مستخدمين.

الإستعلام

        -- سيتم حذفها harmash في حال كان يوجد بالأساس قاعدة بيانات إسمها
        DROP DATABASE IF EXISTS harmash;

        -- harmash هنا قمنا بإنشاء قاعدة بيانات جديدة إسمها
        CREATE DATABASE harmash;

        -- harmash هنا قمنا بتحديد أن أي إستعلام جديد سيتم تنفيذه على قاعدة البيانات
        USE harmash;

        -- يتألف من 4 أعمدة users هنا قمنا بإنشاء جدول جديد إسمه
        -- لأننا قمنا بتحديدها قبل إستدعاء هذا الأمر harmash سيتم إنشاء هذا الجدول بداخل قاعدة البيانات
        CREATE TABLE users (
        id         INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
        first_name VARCHAR(50),
        last_name  VARCHAR(50),
        country    VARCHAR(50)
        );

        -- هنا قمنا بإضافة 10 أسطر في الجدول, أي أضفنا معلومات 10 مستخدمين
        -- لاحظ أننا لم نحدد أسماء الأعمدة التي سنضع فيها البيانات لأننا قمنا بملئ جميع المعلومات
        -- في البداية هو حتى يكون عدد القيم الموضوعة يساوي عدد أعمدة الجدول null سبب وضع الكلمة
        -- ستقوم قاعدة البيانات بوضع رقم تعرفة مختلف لكل سطر null مكان الكلمة
        INSERT INTO users VALUES (null, "Rami", "Masri", "Lebanon");
        INSERT INTO users VALUES (null, "Ahmad", "Naji", "Syria");
        INSERT INTO users VALUES (null, "Hanan", "Mostafa", "KSA");
        INSERT INTO users VALUES (null, "Saly", "Jawhar", "Lebanon");
        INSERT INTO users VALUES (null, "Mhamad", "Harmush", "Lebanon");
        INSERT INTO users VALUES (null, "Hamad", "Akel", "Syria");
        INSERT INTO users VALUES (null, "Abdullah", "Helmi", "Egypt");
        INSERT INTO users VALUES (null, "Rashed", "Masri", "Oman");
        INSERT INTO users VALUES (null, "Majed", "Alali", "Yaman");
        INSERT INTO users VALUES (null, "Rayan", "Kasabi", "KSA");
      

بعد تنفيذ الإستعلام السابق في phpMyAdmin سيتم إنشاء قاعدة البيانات harmash و إنشاء الجدول users بداخلها.
قم بالنقر على إسم قاعدة البيانات harmash من القائمة اليسرى حتى تبدأ بالتعامل معها و تطبيق ما ستتعلمه في هذا الدرس.


البيانات التي قمنا بإضافتها بشكل إفتراضي في الجدول users.

id first_name last_name country
1 Rami Masri Lebanon
2 Ahmad Naji Syria
3 Hanan Mostafa KSA
4 Saly Jawhar Lebanon
5 Mhamad Harmush Lebanon
6 Hamad Akel Syria
7 Abdullah Helmi Egypt
8 Rashed Masri Oman
9 Majed Alali Yaman
10 Rayan Kasabi KSA

أمثلة شاملة على كتابة الأنماط


المثال الأول

الإستعلام التالي يجلب جميع الأسطر التي فيها الحقل first_name يبدأ بحرف A.

الإستعلام

        SELECT * FROM users             -- users هنا قمنا بعرض بيانات جميع الأسطر الموجودة في الجدول
        WHERE first_name LIKE 'A%';     -- A يبدأ بالحرف first_name التي فيها الحقل
      

سنحصل على النتيجة التالية عند تنفيذ الإستعلام.

id first_name last_name country
2 Ahmad Naji Syria
7 Abdullah Helmi Egypt


المثال الثاني

الإستعلام التالي يجلب جميع الأسطر التي فيها الحقل first_name ينتهي الحرفين ad.

الإستعلام

        SELECT * FROM users             -- users هنا قمنا بعرض بيانات جميع الأسطر الموجودة في الجدول
        WHERE first_name LIKE '%ad';    -- ad ينتهي بالحرفين first_name التي فيها الحقل
      

سنحصل على النتيجة التالية عند تنفيذ الإستعلام.

id first_name last_name country
2 Ahmad Naji Syria
5 Mhamad Harmush Lebanon
6 Hamad Akel Syria


المثال الثالث

الإستعلام التالي يجلب جميع الأسطر التي فيها الحقل first_name يبدأ بحرف M و ينتهي بحرف D.

الإستعلام

        SELECT * FROM users              -- users هنا قمنا بعرض بيانات جميع الأسطر الموجودة في الجدول
        WHERE first_name LIKE 'M%D';     -- D و ينتهي بالحرف M يبدأ بالحرف first_name التي فيها الحقل
      

سنحصل على النتيجة التالية عند تنفيذ الإستعلام.

id first_name last_name country
5 Mhamad Harmush Lebanon
9 Majed Alali Yaman


المثال الرابع

الإستعلام التالي يجلب جميع الأسطر التي فيها الحقل first_name يتضمن الحرفين am و لا يهم إن كانا موجودين في البداية أو في الوسط أو في النهاية.

الإستعلام

        SELECT * FROM users               -- users هنا قمنا بعرض بيانات جميع الأسطر الموجودة في الجدول
        WHERE first_name LIKE '%am%';     -- و لا يهم إن كانا موجودين في البداية أو في الوسط أو في النهاية am يتضمن الحرفين first_name التي فيها الحقل
      

سنحصل على النتيجة التالية عند تنفيذ الإستعلام.

id first_name last_name country
1 Rami Masri Lebanon
5 Mhamad Harmush Lebanon
6 Hamad Akel Syria


المثال الخامس

الإستعلام التالي يجلب جميع الأسطر التي فيها الحقل first_name يبدأ بحرف A أو R.

الإستعلام

        SELECT * FROM users                                      -- users هنا قمنا بعرض بيانات جميع الأسطر الموجودة في الجدول
        WHERE (first_name LIKE 'A%' OR first_name LIKE 'R%');    -- R أو الحرف A يبدأ بالحرف first_name التي فيها الحقل
      

سنحصل على النتيجة التالية عند تنفيذ الإستعلام.

id first_name last_name country
1 Rami Masri Lebanon
2 Ahmad Naji Syria
7 Abdullah Helmi Egypt
8 Rashed Masri Oman
10 Rayan Kasabi KSA


المثال السادس

الإستعلام التالي يجلب جميع الأسطر التي فيها الحقل first_name لا يبدأ بالحرف A.

الإستعلام

        SELECT * FROM users                -- users هنا قمنا بعرض بيانات جميع الأسطر الموجودة في الجدول
        WHERE first_name NOT LIKE 'A%';    -- A لا يبدأ بالحرف first_name التي فيها الحقل
      

سنحصل على النتيجة التالية عند تنفيذ الإستعلام.

id first_name last_name country
1 Rami Masri Lebanon
3 Hanan Mostafa KSA
4 Saly Jawhar Lebanon
5 Mhamad Harmush Lebanon
6 Hamad Akel Syria
8 Rashed Masri Oman
9 Majed Alali Yaman
10 Rayan Kasabi KSA

مفهوم الإستعلامات المتدخلة

الإستعلام العادي الذي لا يحتوي على أوامر متداخلة يقال له Query فقط.
بينما الإستعلام الذي يحتوي على أوامر متداخلة مثل أمر SELECT بداخله أمر SELECT فهنا يقال لهذا الإستعلام Sub Query أو Nested Query أو Inner Query.

الإستعلامات المتداخلة مهمة جداً و هي منطق في العمل نطبقه عند الحاجة لإجراء إحصاءات محددة على أساسسها نقوم بجلب القيم, تعديلها, أو حذفها.
في هذا الدرس لن تتعلم أوامر جديدة في SQL بل ستتعلم المنطق الذي نتبعه لكتابة إستعلامات متداخلة خطوة خطوة.

كتابة إستعلامات متداخلة يعتبر أصعب شيء قد تتعلمه في قواعد البيانات و قد تحتاج وقت حتى تعتاد عليه و تفهم كيف تفعله بنفسك.
لذا حاول متابعة الدرس بهدوء و تركيز تام و شاهد الفيديوهات الموضوعة ضمن الأمثلة لأنها ستساعدك كثيراً في فهم الطريقة التي تم فيها كتابة الإستعلامات.

أولوية تنفيذ الإستعلامات المتداخلة

عند وضع الإستعلامات بداخل بعضها البعض, يتم تنفيذ الإستعلامات الداخلية و من ثم الإستعلامات الخارجية.

كمثال بسيط, تخيل أن الإستعلام مكتوب على هذا النحو.

      SELECT * FROM users
      WHERE salary = (SELECT MAX(salary) FROM users);
    

هنا سيتم حساب ناتج الإستعلام الداخلي و يتم وضع جوابه مكانه. فمثلاً في حال كان ناتج الإستعلام الداخلي هو 1500 فإن الإستعلام سيصبح كأنه كالتالي بالضبط.

      SELECT * FROM users
      WHERE salary = 1500;
    

الآن بعد أن أصبح الإستعلام لا يحتوي على أي أوامر متداخلة, سيتم إعطاءك الجواب النهائي.


ملاحظة

أي إستعلام داخلي يجب أن تضعه بين قوسين ( ) لأن أولية التنفيذ تقررها قاعدة البيانات نسبةً للأقواس.
و بالطبع في حال كان يوجد أكثر من إستعلام داخلي, سيتم تنفيذ أعمق إستعلام بينهم في البداية رجوعاً للإستعلام الأساسي الذي ينتظر نتائج كل الإستعلام الموضوعة فيه حتى يتنفذ كما في المال السابق.

تجهيز قاعدة البيانات التي سنطبق عليها

قم بتنفيذ الإستعلام التالي حتى تنشئ قاعدة بيانات جديدة إسمها harmash و تنشئ فيها جدول إسمه scientists يحتوي على بيانات 10 علماء.

الإستعلام

        -- سيتم حذفها harmash في حال كان يوجد بالأساس قاعدة بيانات إسمها
        DROP DATABASE IF EXISTS harmash;

        -- harmash هنا قمنا بإنشاء قاعدة بيانات جديدة إسمها
        CREATE DATABASE harmash;

        -- harmash هنا قمنا بتحديد أن أي إستعلام جديد سيتم تنفيذه على قاعدة البيانات
        USE harmash;

        -- يتألف من 5 أعمدة scientists هنا قمنا بإنشاء جدول جديد إسمه
        -- لأننا قمنا بتحديدها قبل إستدعاء هذا الأمر harmash سيتم إنشاء هذا الجدول بداخل قاعدة البيانات
        CREATE TABLE scientists (
        id        INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
        name      VARCHAR(50),
        salary    DECIMAL(7,2),
        country   VARCHAR(50),
        continent VARCHAR(50)
        );

        -- هنا قمنا بإضافة 10 أسطر في الجدول, أي أضفنا معلومات 10 علماء
        -- لاحظ أننا لم نحدد أسماء الأعمدة التي سنضع فيها البيانات لأننا قمنا بملئ جميع المعلومات
        -- في البداية هو حتى يكون عدد القيم الموضوعة يساوي عدد أعمدة الجدول null سبب وضع الكلمة
        -- ستقوم قاعدة البيانات بوضع رقم تعرفة مختلف لكل سطر null مكان الكلمة
        INSERT INTO scientists VALUES (null, "Ahmad", 4200, "KSA", "Asia");
        INSERT INTO scientists VALUES (null, "Rami", 2750, "Egypt", "Africa");
        INSERT INTO scientists VALUES (null, "Said", 3900, "Lebanon", "Asia");
        INSERT INTO scientists VALUES (null, "Noura", 3150, "Algeria", "Africa");
        INSERT INTO scientists VALUES (null, "Amani", 2950, "Morocco", "Africa");
        INSERT INTO scientists VALUES (null, "Mhamad", 3700, "Lebanon", "Asia");
        INSERT INTO scientists VALUES (null, "Wissam", 3550, "Oman", "Asia");
        INSERT INTO scientists VALUES (null, "Mostafa", 4120, "Qatar", "Asia");
        INSERT INTO scientists VALUES (null, "Houssam", 6800, "Germany", "Europe");
        INSERT INTO scientists VALUES (null, "Jana", 7500, "Canada", "North America");
      

بعد تنفيذ الإستعلام السابق في phpMyAdmin سيتم إنشاء قاعدة البيانات harmash و إنشاء الجدول scientists بداخلها.
قم بالنقر على إسم قاعدة البيانات harmash من القائمة اليسرى حتى تبدأ بالتعامل معها و تطبيق ما ستتعلمه في هذا الدرس.


البيانات التي قمنا بإضافتها بشكل إفتراضي في الجدول scientists.

id name salary country continent
1 Ahmad 4200.00 KSA Asia
2 Rami 2750.00 Egypt Africa
3 Said 3900.00 Lebanon Asia
4 Noura 3150.00 Algeria Africa
5 Amani 2950.00 Morocco Africa
6 Mhamad 3700.00 Lebanon Asia
7 Wissam 3550.00 Oman Asia
8 Mostafa 4120.00 Qatar Asia
9 Houssam 6800.00 Germany Europe
10 Jana 7500.00 Canada North America

أمثلة شاملة على كتابة إستعلامات متداخلة


المثال الأول

الإستعلام التالي يعرض جميع معلومات الموظف الذي ينال أعلى أجر.
هنا يجب معرفة أكبر راتب يتم إعطاؤه للموظفين, بعد معرفته يمكننا إحضار معلومات كل موظف يملك نفس هذا الراتب.

الإستعلام

        SELECT * FROM scientists                             -- scientists هنا قمنا بعرض بيانات جميع الأسطر الموجودة في الجدول
        WHERE salary = (SELECT MAX(salary) FROM scientists)  -- (scientists موجود في الجدول salary أعلى) يساوي salary التي تملك
      

الفيديو التالي يشرح كود الإستعلام بدقة و يشرح كيف حصلنا على النتيجة.

سنحصل على النتيجة التالية عند تنفيذ الإستعلام.

id name salary country continent
10 Jana 7500.00 Canada North America


المثال الثاني

الإستعلام التالي يعرض أسماء و رواتب العلماء الذين ينالون راتب لا يتجاوز متوسط الرواتب التي يتم إعطاءها للموظفين.
هنا يجب معرفة متوسط الرواتب التي يتم إعطاءها للموظفين, بعد معرفته يمكننا إحضار أسماء و رواتب العلماء الذين يملكون راتب أقل منه.

الإستعلام

        SELECT name, salary FROM scientists                  -- scientists هنا قمنا بعرض إسم و راتب كل عالم في الجدول
        WHERE salary < (SELECT AVG(salary) FROM scientists)  -- (scientists موجود في الجدول salary متوسط كل) أقل من salary يملك
      

الفيديو التالي يشرح كود الإستعلام بدقة و يشرح كيف حصلنا على النتيجة.

سنحصل على النتيجة التالية عند تنفيذ الإستعلام.

name salary
Ahmad 4200.00
Rami 2750.00
Said 3900.00
Noura 3150.00
Amani 2950.00
Mhamad 3700.00
Wissam 3550.00
Mostafa 4120.00


المثال الثالث

الإستعلام التالي يعرض إسم أكثر قارة تم إحضار علماء منها.
هنا يجب معرفة عدد العلماء الموجودين في كل قارة.
بعدها يجب معرفة أكبر عدد علماء تم إيجاده في قارة.
بعدها يجب طباعة إسم و عدد علماء كل قارة عدد علمائها يساوي عدد العلماء الأكبر الذي عرفناه.

الإستعلام

الطريقة التالية تعمل مع جميع قواعد البيانات.

        SELECT continent, COUNT(id) AS total
        FROM scientists
        GROUP BY continent
        HAVING total = (SELECT MAX(t.total) AS max_count
        FROM (SELECT continent, COUNT(id) AS total
        FROM scientists
        GROUP BY continent) AS t)
      

الفيديو التالي يشرح كود الإستعلام بدقة و يشرح كيف حصلنا على النتيجة.


إذا كنت تستخدم قواعد بيانات MySQL يمكنك كتابة الإستعلام نفسه كالتالي.

        SELECT continent, COUNT(id) AS total
        FROM scientists
        GROUP BY continent
        HAVING total = (SELECT COUNT(id) AS max_count
        FROM scientists
        GROUP BY continent
        ORDER BY max_count DESC
        LIMIT 1);
      

إذا كنت تستخدم قواعد بيانات SQL Server / Access يمكنك كتابة الإستعلام نفسه كالتالي.

        SELECT continent, COUNT(id) AS total
        FROM scientists
        GROUP BY continent
        HAVING total = (SELECT TOP 1 COUNT(id) AS max_count
        FROM scientists
        GROUP BY continent
        ORDER BY max_count DESC);
      

إذا كنت تستخدم قواعد بيانات Oracle يمكنك كتابة الإستعلام نفسه كالتالي.

        SELECT continent, COUNT(id) AS total
        FROM scientists
        GROUP BY continent
        HAVING total = (SELECT COUNT(id) AS max_count
        FROM scientists
        WHERE ROWNUM <= 1
        GROUP BY continent
        ORDER BY max_count DESC);
      

سنحصل على النتيجة التالية عند تنفيذ الإستعلام.

continent total
Asia 5

مفهوم الإستعلامات المتدخلة

الإستعلام العادي الذي لا يحتوي على أوامر متداخلة يقال له Query فقط.
بينما الإستعلام الذي يحتوي على أوامر متداخلة مثل أمر SELECT بداخله أمر SELECT فهنا يقال لهذا الإستعلام Sub Query أو Nested Query أو Inner Query.

الإستعلامات المتداخلة مهمة جداً و هي منطق في العمل نطبقه عند الحاجة لإجراء إحصاءات محددة على أساسسها نقوم بجلب القيم, تعديلها, أو حذفها.
في هذا الدرس لن تتعلم أوامر جديدة في SQL بل ستتعلم المنطق الذي نتبعه لكتابة إستعلامات متداخلة خطوة خطوة.

كتابة إستعلامات متداخلة يعتبر أصعب شيء قد تتعلمه في قواعد البيانات و قد تحتاج وقت حتى تعتاد عليه و تفهم كيف تفعله بنفسك.
لذا حاول متابعة الدرس بهدوء و تركيز تام و شاهد الفيديوهات الموضوعة ضمن الأمثلة لأنها ستساعدك كثيراً في فهم الطريقة التي تم فيها كتابة الإستعلامات.

أولوية تنفيذ الإستعلامات المتداخلة

عند وضع الإستعلامات بداخل بعضها البعض, يتم تنفيذ الإستعلامات الداخلية و من ثم الإستعلامات الخارجية.

كمثال بسيط, تخيل أن الإستعلام مكتوب على هذا النحو.

      SELECT * FROM users
      WHERE salary = (SELECT MAX(salary) FROM users);
    

هنا سيتم حساب ناتج الإستعلام الداخلي و يتم وضع جوابه مكانه. فمثلاً في حال كان ناتج الإستعلام الداخلي هو 1500 فإن الإستعلام سيصبح كأنه كالتالي بالضبط.

      SELECT * FROM users
      WHERE salary = 1500;
    

الآن بعد أن أصبح الإستعلام لا يحتوي على أي أوامر متداخلة, سيتم إعطاءك الجواب النهائي.


ملاحظة

أي إستعلام داخلي يجب أن تضعه بين قوسين ( ) لأن أولية التنفيذ تقررها قاعدة البيانات نسبةً للأقواس.
و بالطبع في حال كان يوجد أكثر من إستعلام داخلي, سيتم تنفيذ أعمق إستعلام بينهم في البداية رجوعاً للإستعلام الأساسي الذي ينتظر نتائج كل الإستعلام الموضوعة فيه حتى يتنفذ كما في المال السابق.

تجهيز قاعدة البيانات التي سنطبق عليها

قم بتنفيذ الإستعلام التالي حتى تنشئ قاعدة بيانات جديدة إسمها harmash و تنشئ فيها جدول إسمه scientists يحتوي على بيانات 10 علماء.

الإستعلام

        -- سيتم حذفها harmash في حال كان يوجد بالأساس قاعدة بيانات إسمها
        DROP DATABASE IF EXISTS harmash;

        -- harmash هنا قمنا بإنشاء قاعدة بيانات جديدة إسمها
        CREATE DATABASE harmash;

        -- harmash هنا قمنا بتحديد أن أي إستعلام جديد سيتم تنفيذه على قاعدة البيانات
        USE harmash;

        -- يتألف من 5 أعمدة scientists هنا قمنا بإنشاء جدول جديد إسمه
        -- لأننا قمنا بتحديدها قبل إستدعاء هذا الأمر harmash سيتم إنشاء هذا الجدول بداخل قاعدة البيانات
        CREATE TABLE scientists (
        id        INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
        name      VARCHAR(50),
        salary    DECIMAL(7,2),
        country   VARCHAR(50),
        continent VARCHAR(50)
        );

        -- هنا قمنا بإضافة 10 أسطر في الجدول, أي أضفنا معلومات 10 علماء
        -- لاحظ أننا لم نحدد أسماء الأعمدة التي سنضع فيها البيانات لأننا قمنا بملئ جميع المعلومات
        -- في البداية هو حتى يكون عدد القيم الموضوعة يساوي عدد أعمدة الجدول null سبب وضع الكلمة
        -- ستقوم قاعدة البيانات بوضع رقم تعرفة مختلف لكل سطر null مكان الكلمة
        INSERT INTO scientists VALUES (null, "Ahmad", 4200, "KSA", "Asia");
        INSERT INTO scientists VALUES (null, "Rami", 2750, "Egypt", "Africa");
        INSERT INTO scientists VALUES (null, "Said", 3900, "Lebanon", "Asia");
        INSERT INTO scientists VALUES (null, "Noura", 3150, "Algeria", "Africa");
        INSERT INTO scientists VALUES (null, "Amani", 2950, "Morocco", "Africa");
        INSERT INTO scientists VALUES (null, "Mhamad", 3700, "Lebanon", "Asia");
        INSERT INTO scientists VALUES (null, "Wissam", 3550, "Oman", "Asia");
        INSERT INTO scientists VALUES (null, "Mostafa", 4120, "Qatar", "Asia");
        INSERT INTO scientists VALUES (null, "Houssam", 6800, "Germany", "Europe");
        INSERT INTO scientists VALUES (null, "Jana", 7500, "Canada", "North America");
      

بعد تنفيذ الإستعلام السابق في phpMyAdmin سيتم إنشاء قاعدة البيانات harmash و إنشاء الجدول scientists بداخلها.
قم بالنقر على إسم قاعدة البيانات harmash من القائمة اليسرى حتى تبدأ بالتعامل معها و تطبيق ما ستتعلمه في هذا الدرس.


البيانات التي قمنا بإضافتها بشكل إفتراضي في الجدول scientists.

id name salary country continent
1 Ahmad 4200.00 KSA Asia
2 Rami 2750.00 Egypt Africa
3 Said 3900.00 Lebanon Asia
4 Noura 3150.00 Algeria Africa
5 Amani 2950.00 Morocco Africa
6 Mhamad 3700.00 Lebanon Asia
7 Wissam 3550.00 Oman Asia
8 Mostafa 4120.00 Qatar Asia
9 Houssam 6800.00 Germany Europe
10 Jana 7500.00 Canada North America

أمثلة شاملة على كتابة إستعلامات متداخلة


المثال الأول

الإستعلام التالي يعرض جميع معلومات الموظف الذي ينال أعلى أجر.
هنا يجب معرفة أكبر راتب يتم إعطاؤه للموظفين, بعد معرفته يمكننا إحضار معلومات كل موظف يملك نفس هذا الراتب.

الإستعلام

        SELECT * FROM scientists                             -- scientists هنا قمنا بعرض بيانات جميع الأسطر الموجودة في الجدول
        WHERE salary = (SELECT MAX(salary) FROM scientists)  -- (scientists موجود في الجدول salary أعلى) يساوي salary التي تملك
      

الفيديو التالي يشرح كود الإستعلام بدقة و يشرح كيف حصلنا على النتيجة.

سنحصل على النتيجة التالية عند تنفيذ الإستعلام.

id name salary country continent
10 Jana 7500.00 Canada North America


المثال الثاني

الإستعلام التالي يعرض أسماء و رواتب العلماء الذين ينالون راتب لا يتجاوز متوسط الرواتب التي يتم إعطاءها للموظفين.
هنا يجب معرفة متوسط الرواتب التي يتم إعطاءها للموظفين, بعد معرفته يمكننا إحضار أسماء و رواتب العلماء الذين يملكون راتب أقل منه.

الإستعلام

        SELECT name, salary FROM scientists                  -- scientists هنا قمنا بعرض إسم و راتب كل عالم في الجدول
        WHERE salary < (SELECT AVG(salary) FROM scientists)  -- (scientists موجود في الجدول salary متوسط كل) أقل من salary يملك
      

الفيديو التالي يشرح كود الإستعلام بدقة و يشرح كيف حصلنا على النتيجة.

سنحصل على النتيجة التالية عند تنفيذ الإستعلام.

name salary
Ahmad 4200.00
Rami 2750.00
Said 3900.00
Noura 3150.00
Amani 2950.00
Mhamad 3700.00
Wissam 3550.00
Mostafa 4120.00


المثال الثالث

الإستعلام التالي يعرض إسم أكثر قارة تم إحضار علماء منها.
هنا يجب معرفة عدد العلماء الموجودين في كل قارة.
بعدها يجب معرفة أكبر عدد علماء تم إيجاده في قارة.
بعدها يجب طباعة إسم و عدد علماء كل قارة عدد علمائها يساوي عدد العلماء الأكبر الذي عرفناه.

الإستعلام

الطريقة التالية تعمل مع جميع قواعد البيانات.

        SELECT continent, COUNT(id) AS total
        FROM scientists
        GROUP BY continent
        HAVING total = (SELECT MAX(t.total) AS max_count
        FROM (SELECT continent, COUNT(id) AS total
        FROM scientists
        GROUP BY continent) AS t)
      

الفيديو التالي يشرح كود الإستعلام بدقة و يشرح كيف حصلنا على النتيجة.


إذا كنت تستخدم قواعد بيانات MySQL يمكنك كتابة الإستعلام نفسه كالتالي.

        SELECT continent, COUNT(id) AS total
        FROM scientists
        GROUP BY continent
        HAVING total = (SELECT COUNT(id) AS max_count
        FROM scientists
        GROUP BY continent
        ORDER BY max_count DESC
        LIMIT 1);
      

إذا كنت تستخدم قواعد بيانات SQL Server / Access يمكنك كتابة الإستعلام نفسه كالتالي.

        SELECT continent, COUNT(id) AS total
        FROM scientists
        GROUP BY continent
        HAVING total = (SELECT TOP 1 COUNT(id) AS max_count
        FROM scientists
        GROUP BY continent
        ORDER BY max_count DESC);
      

إذا كنت تستخدم قواعد بيانات Oracle يمكنك كتابة الإستعلام نفسه كالتالي.

        SELECT continent, COUNT(id) AS total
        FROM scientists
        GROUP BY continent
        HAVING total = (SELECT COUNT(id) AS max_count
        FROM scientists
        WHERE ROWNUM <= 1
        GROUP BY continent
        ORDER BY max_count DESC);
      

سنحصل على النتيجة التالية عند تنفيذ الإستعلام.

continent total
Asia 5

المستخدم root في MySQL

في هذه الدورة كنا نتعامل مع قواعد بيانات MySQL بشكل مباشر من خلال phpMyAdmin حيث لم نكن نضطر لكتابة إسم مستخدم و كلمة مرور حتى يسمح لنا خادم قاعدة البيانات ( Database Server ) بالتعامل مع قواعد البيانات.

سبب قدرتك على التعامل بشكل مباشر مع قواعد البيانات هو أن MySQL ينشئ لك مستخدم إفتراضي إسمه root لا يملك كلمة مرور و يملك كل الصلاحيات التي تخوله لإجراء كافة أنواع العمليات على قواعد البيانات. المستخدم root يتم الدخول بواستطه بشكل تلقائي حين تحاول فتح صفحة phpMyAdmin في المتفصح لهذا لم يكن يطلب منك إدخال إسم مستخدم و كلمة مرور عند فتح الصفحة و كان يسمح لك بفعل ما تريد.

رؤية معلومات المستخدمين في phpMyAdmin

لمشاهدة معلومات المستخدمين لقواعد البيانات و صلاحياتهم في phpMyAdmin تأكد أنك متصل بالخادم فقط و من ثم قم بالنقر على كلمة User Accounts لفتح القسم الخاص بالمستخدمين و صلاحياتهم.


الصفحة التي ستفتح تتيح لك مشاهدة أسماء جميع المستخدمين و صلاحياتهم, تعديل صلاحية المستخدمين, إضافة مستخدم جديد و حذف مستخدم.

لاحظ أنه يوجد مستخدم إسمه root لم يتم وضع كلمة مرور له و عرفنا ذلك لأنه يظهر في العامود Password الكلمة No مما يعني أنه لم يتم وضع كلمة مرور. بالإضافة إلى ذلك فقد تم منحه جميع الصلاحيات و عرفنا ذلك لأنه يظهر في العامود Global Privileges جملة ALL PRIVILEGES و يظهر في العامود Grant كلمة Yes مما يعني أن جميع الصلاحيات ممنوحة له.


معلومة

سبب تكرار إسم المستخدم root ثلاث مرات و كل مرة تم إعطاؤه Host Name هو أن قواعد بيانات MySQL تتيح لك وضع صلاحيات للمستخدم على حسب الطريقة التي سيدخل منها و لكن لا تشغل بالك بهذه التفاصيل.

تعديل معلومات و صلاحيات المستخدمين في phpMyAdmin

في البداية و قبل البدء عليك معرفة أنه عند إجراء أي تعديل على معلومات المستخدم أو صلاحياته من خلال هذه الصفحة يجب أن تنقر على الزر Go الذي ستجده في أسفل كل صفحة حتى تحفظ التغييرات التي قمت بإجرائها.

الآن لتعديل معلومات دخول أو صلاحيات أي مستخدم, أنقر على Edit Previleges التي تظهر بجانب إسمه كالتالي.


  1. لتعديل صلاحيات المستخدم, يمكنك الدخول للقسم Global و فيه تقوم بتحديد أو إلغاء تحديد أي صلاحية تريد و من بعدها لا تنسى الضغط على الزر Go لحفظ التغيرات. و هنا لاحظ أن المستخدم root يملك جميع الصلاحيات كما سبق و قلنا.


  1. لإعطاء المستخدم صلاحيات محددة لكل قاعدة بيانات على حدا, يمكنك الدخول للقسم Database و فيه تقوم بتحديد إسم قاعدة البيانات سواء تكتبه بنفسك أو تختاره من القائمة التي تظهر فيها قواعد البيانات و من بعدها لا تنسى الضغط على الزر Go حتى تبدأ بتحديد صلاحيات المستخدم فيها.


  1. لتغيير كلمة مرور المستخدم, يمكنك الدخول للقسم Change Password و فيه يطلب منك إدخال كلمة المرور القديمة و من ثم إدخال كلمة المرور الجديدة. و من بعدها لا تنسى الضغط على الزر Go حتى تحفظ كلمة المرور الجديدة.


  1. لتعديل معلومات دخول المستخدم, يمكنك الدخول للقسم Login Information و فيه تستطيع تغيير أي معلومة متعلقة بتسجيل دخول المستخدم و من بعدها لا تنسى الضغط على الزر Go لحفظ أي تغيرات تجريها.

لغات البرمجة و قواعد البيانات

عندما يربط المبرمج مشروعه بقاعدة بيانات سواء كان هذا المشروع عبارة عن موقع, تطبيق أو لعبة سيكون عليه أولاً تحديد مكان وجود قاعدة البيانات التي سيتصل بها و من ثم تحديد إسم المستخدم الذي سيتعامل من خلاله مع قاعدة البيانات إذا كانت قاعدة البيانات تجبرك على الدخول من خلال مستخدم خاص.

قواعد البيانات التي تعمل بشكل مباشر بدون الحاجة للدخول بواسطة مستخدم هي قواعد بيانات Access و SQLite.
قواعد البيانات الأخرى مثل MySQL و SQL Server و Oracle لا بد لك من تسجيل الدخول بواسطة مستخدم حتى تتمكن من التعامل معهم.

في دورة تعلم لغة جافا و دورة تعلم لغة بايثون شرحنا طريقة ربط المشاريع بقواعد بيانات MySQL خطوة خطوة.

الإتصال بقاعدة البيانات في جافا » الإتصال بقاعدة البيانات في بايثون »