أعجوبة

البرمجيات الحُرة والمفتوحة المصدر

أدوات المستخدم

أدوات الموقع


docs:sql_basics


بدهيات استعلامات SQL مع قواعد بيانات Sqlite

هذه الصفحة لا تزال قيد الإنشاء

مقدمة

إن SQL وتعني لغة الاستعلامات الهيكلية Structured Query Language وهي من وضع IBM (في نظام لهم اسمه النظام R يعود للسبعينات من القرن الماضي) وهي لغة عيارية منذ 1986 م حيث تبنتها ANSI ثم ISO في معايير متلاحقة.

يجدر بنا الإشارة إلى أن SQL ليست لغة برمجة وإنما لغة للتحدث مع قواعد البيانات العلائقية RDBMS.

ويوجد العديد من قواعد البيانات التي يمكنك مخاطبتها بهذه اللغة سواء الحرة مثل MySql و PostgreSQL أو المملوكة مثل الشهيرة Oracle وهناك قواعد بيانات تدعم SQL كميزة إضافية غير أساسية.

بعكس ما قد يخطر ببالك ليس بالضرورة أن تكون قواعد البيانات برمجيات عبر شبكة فهناك منها ما هي هيئة ملفات تستخدمها تماما كما تكتب قيم بينها فواصل CSV في ملف لكنها تكون أسرع وأكثر كفاءة وفعالية. منها قواعد بيانات SQLite التي أطلقت عام 2001 م بعد قيام مؤلفها بأبحاث لصالح البحرية للتحكم بالمدمرات الصاروخية.

اخترنا SQLite لعدة أسباب

  • لا تحتاج التعقيدات المرتبطة بنموذج الخادم والعميل client/server فهي في النهاية مجرد ملف.
  • مع أنها صممت كي تكون منضدة إلا أنه يمكنها التعامل مع كم كبير من البيانات
  • تدعم معايير SQL-92 بشكل كبير.

كل ما يلزمنا في هذه الوثيقة برنامج صغير اسمه sqlite3 (في فيدورا/أعجوبة تجده في حزمة sqlite) وهناك إضافة متصفح الإنترنت لثعلب النار firefox تسمح لك بالتعامل رسوميا مع ملفات SQLite.

الأساسيات

تتألف قاعدة البيانات من عدد من الجداول كل منها له مخطط ثابت schema يحدد ما يحتويه كل جدول من حقول (أعمدة).

أنواع الحقول

في SQL القياسية يجب تعريف أنواع الحقول بشكل مسبق والالتزام بها.

  • الحقول النصية
    • حرة الطول ذات حد أعلى VARCHAR ونتبعها بعدد صحيح بين قوسين هو الحد الأعلى لطولها.
    • ثابت الطول CHAR متبوع هو الآخر بعدد بين قوسين توسّد محتوياته بمسافات إن قل طولها عنه.
    • ومثلهما حقلي NVARCHAR و NCHAR لكنهما مع إضافة دعم المحارف العالمية.
  • الحقول العددية
    • الصحيحة وهي INTEGER و SMALLINT
    • النسبية ذات الفاصلة العائمة FLOAT و REAL و DOUBLE PRECISION
    • عدد من البتات BIT و VARBIT
  • حقول منطقية BOOL و BOOLEAN
  • حقول زمنية DATETIME و TIME و DATE و TIMESTAMP
تسمح لك SQlite بعدم الالتزام بنوع الحقل المعرف في المخطط Schema كأن تضع نص في حقل عددي وهو ما يعرف بميزة dynamic typing.

من مرونة SQlite فإنها توفر الأنواع الإضافية التالية:

  • حقل نصي عالمي حر الطول TEXT وهو يدعم unicode دون الحاجة لتحديد أي طول. (هذا النوع متوفر في MySQL و PostgreSQL)
  • حقل ثنائي BLOB لحفظ سلسلة من البيانات (ليس بالضرورة أن تكون نصا كحفظ محتويات ملف .jpg مثلا)
  • حقل عددي صحيح INTEGER
  • وحقل عددي نسبي REAL

لا توفر SQLite دعما حقيقيا لحفظ التاريخ والوقت لكن يمكنك محاكاة ذلك عبر استعمال INTEGER وحفظ عدد الثواني بعد ما يعرف ببزوغ فجر يونكس Unix epoch.

يمكنك استعمال أنواع SQL القياسية لكن اعلم أن SQLite ستدير الحجم ولن تحترم النوع أو الحد الذي تطلبه.

يفضل البعض استخدام حقول من قوى 2 مثل VARCHAR(128) إلا أن هذا في SQLite ليس منه فائدة لأنها كلها من نوع TEXT

إنشاء قاعدة بيانات

تختلف الطريقة من قاعدة بيانات إلى أخرى.

في SQLite

في sqlite يكفي تنفيذ sqlite3 متبوعا باسم الملف المطلوب إنشاؤه ليكون قاعدة بيانات

sqlite3 my_db_file.db

ولأغراض الاختبار يمكنك استعمال جدول في الذاكرة المتطايرة RAM وذلك بتمرير :memory: عوضا عن اسم الملف هكذا

sqlite3 :memory:

يتوفر واجهة رسومية تتعامل مع ملفات sqlite وهي متوفرة بصيغتين أسهلهما إضافة لمتصفح الإنترنت firefox (داخل قائمة أدوات tools) أو برنامج مستقل (يتم تنفيذه عبر xulrunner)

واجهة أداة إدارة sqlite من firefox

في MySQL

في mysql نتصل مع خادم قاعدة البيانات مثلا بالأمر

mysql --default-character-set=utf8 -u root

أضف الخيار p حتى يسألك عن كلمة السر. يمكنك تحديد مستخدم غير root.

ثم ننشئ قاعدة البيانات بالأمر

CREATE DATABASE testdb;
جرت العادة أن تكتب الكلمات المحجوزة في SQL بأحرف كبيرة.
تتبع كل عبارات SQL بفاصلة منقوطة ;

ولعرض بقواعد البيانات المتوفرة نستعمل SHOW DATABASES حيث جدول بها هكذا

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| testdb             |
+--------------------+
3 rows in set (0.00 sec)

mysql>

لاختيار قاعدة بيانات نستعمل USE متبوعا باسم قاعدة البيانات ويمكن عمل هذا الاختيار عند الاتصال بخادم mysql بكتابة اسم قاعدة البيانات في نهاية الأمر

mysql --default-character-set=utf8 -u root -p testdb

إنشاء الجداول

لإنشاء جدول نستخدم CREATE TABLE ويمكن إنشاء جدول مؤقت متطاير المحتوى عبر CREATE TEMPORARY TABLE وهو جدول حذف تلقائيا عند إنهاء الاتصال (إغلاق البرنامج).

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

 CREATE TABLE addressbook (
	name VARCHAR(30), 
	email VARCHAR(30), 
	phone VARCHAR(30)
 );
تعتبر لغة SQL من اللغات حرة التنسيق Free form لهذا يجوز أن تكتب العبارة الواحدة على عدة أسطر.

يمكنك وصف عمود ما بأنه معرّف أساسي PRIMARY KEY أو بأنه فريد UNIQUE أو مفهرس KEY أو أنه لا ينبغي أن يكون خاليا NOT NULL كما يمكنك أن تعطيه قيمة أولية كذلك يمكن جعله ذاتي الزيادة AUTOINCREMENT (أو كما في mysql يكتب AUTO_INCREMENT بعلامة تحتية _) وسيأتي تفصيل ذلك.

 CREATE TABLE addressbook (
	id INTEGER PRIMARY KEY, 
	name VARCHAR(30) UNIQUE, 
	email VARCHAR(30) NOT NULL, 
	phone VARCHAR(30) DEFAULT "NA"
 );
يسمى الأمر السابق باسم Data Definition Language أو DDL اختصارا.

إضافة الصفوف

يمكننا إضافة الصفوف إلى الجدول عبر INSERT INTO متبوعة باسم الجدول ثم VALUES ثم القيم بين قوسين

INSERT INTO addressbook VALUES (1, 'omar','omar@gmail.com', '+962-6-54321000');

لكن إن كنت تريد تحديد قيم لأعمدة بعينها وترك الباقية للقيم التلقائية (مثل المعرف ذاتي الزيادة) يمكنك تحديد الأعمدة التي تريدها بين قوسين بعد اسم الجدول هكذا

INSERT INTO addressbook (name, email, phone) VALUES ('ahmad','ahmad@gmail.com', '+962-6-54321000');
INSERT INTO addressbook (name, email) VALUES ('ali','ali@gmail.com');

عرض كل الصفوف

لعرض كل الصفوف من جدول ما (مثل جدول addressbook من المثال السابق) يمكننا كتابة SELECT متبوعة بأسماء الأعمدة أو * لاختيار كل الأعمدة متبوعة بكلمة FROM متبوعة باسم الجدول هكذا

SELECT * FROM addressbook;

والتي تعطي النتيجة التالية

id name email phone
1 omar omar@gmail.com +962-6-54321000
2 ahmad ahmad@gmail.com +962-6-54321000
3 ali ali@gmail.com NA

فإن حددنا الأعمدة مثلا عمودي name و phone

SELECT name, phone FROM addressbook;

والتي تعطي النتيجة التالية

name phone
omar +962-6-54321000
ahmad +962-6-54321000
ali NA

استخراج dump من قاعدة البيانات

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

في SQLite يكون ذلك عبر الأمر الخاص .dump (أي نقطة متبوعة بكلمة dump) اكتبه في محث sqlite3 ثم اضغط ENTER دون وضع فاصلة منقوطة.

ويعمل أيضا من سطر الأوامر مثلا في سطر أوامر نظام التشغيل (وليس SQLite) اكتب

sqlite3 my_db_file.db .dump

عرض بنية قاعدة البيانات

إن كنت تحتاج عرض مخطط قاعدة البيانات بكل جداولها لكن دون الصفوف اكتب في محث sqlite3 الأمر الخاص .schema (أي نقطة متبوعة بكلمة schema متبوعة بالضغط على ENTER)

حذف جدول

لحذف جدول (الصفوف والمخطط) نستخدم الأمر DROP TABLE هكذا

DROP TABLE addressbook;

إنشاء الجداول وفهرستها

الفهارس

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

يكون إنشاء الفهرس عبر استخدام CREATE INDEX أو CREATE UNIQUE INDEX ثم اسم الفهرس (اسم فريد ضمن نفس الجدول يجوز أن يكون اسم الحقل) ثم كلمة ON ثم بين قوسين الحقول التي تعمل الفهرسة عليها مثلا:

CREATE TABLE tags (
	name VARCHAR(30) UNIQUE, 
	n VARCHAR(30) NOT NULL,
);
CREATE INDEX n ON tags (n);

هنا عملنا فهرسا فريدا ضمنيا على حقل الاسم لأننا استعملنا كلمة UNIQUE عند انشاء الجدول. وعملنا فهرسا غير فريد على حقل التكرار n عبر CREATE INDEX بعد إنشاء الجدول.

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

لاحظ هذا المثال

لكن لو أردنا أن نعمل فهرسا فريدا للاسم الأول وآخر للاسم الثاني

CREATE TABLE users (
	first_name VARCHAR(30),
	last_name VARCHAR(30),
	UNIQUE (first_name),
	UNIQUE (last_name)
);
CREATE UNIQUE INDEX first_name ON users (first_name)
CREATE UNIQUE INDEX last_name ON users (last_name);

والذي يكافئ:

CREATE TABLE users (
	first_name VARCHAR(30),
	last_name VARCHAR(30),
	UNIQUE (first_name),
	UNIQUE (last_name)
);

لكن هذا ليس ما نريده فهو يمنع تكرار الاسم الأول حتى لو اختلف الاسم الثاني. لكن المثال التالي:

CREATE TABLE users (
	first_name VARCHAR(30),
	last_name VARCHAR(30)
);
CREATE UNIQUE INDEX first_last_name ON users (first_name, last_name);

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

CREATE TABLE users (
	first_name VARCHAR(30),
	last_name VARCHAR(30),
	UNIQUE (first_name, last_name)
);

هنا الحصول على الصف الذي فيه الاسم الأول omar والثاني ahmad يكون سريعا كذلك الوصول لكل الصفوف التي فيها الاسم الأول ali مهما كان الاسم الثاني أيضا يكون سريعا

ترتيب الحقول مهم في الفهرس فإن كان عندك فهرس واحد على ثلاث حقول هي a ثم b ثم c فإنك تستطيع البحث أو الترتيب بسرعة على السوابق أي قيم a دون تحديد b ولا c بسرعة لكن ليس b دون تحديد a.

المفتاح الرئيسي PRIMARY KEY

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

هناك صيغتين لتعريفه بعد الحقل مباشرة هكذا

CREATE TABLE addressbook (
	uid INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 
	name VARCHAR(30), 
	email VARCHAR(30), 
	phone VARCHAR(30)
);

أو بعد الحقول بطريقة تشبه تعريف الفهارس ويمكن أن تكون مركبة بذكر أكثر من حقل

CREATE TABLE addressbook (
	uid INTEGER NOT NULL AUTOINCREMENT, 
	name VARCHAR(30), 
	email VARCHAR(30), 
	phone VARCHAR(30),
	PRIMARY KEY (uid)
);
نذكر بأن الحقل ذاتي الزيادة في MYSQL يكتب AUTO_INCREMENT بعلامة تحتية _

الاستعلام

الاستعلام يكون عبر SELECT

تطبيق بعض الحسابات

يمكننا استخدام الاستعلام بالأمر SELECT من أجل القيام بحسابات هكذا

SELECT 1+1;
SELECT MAX(1,2,3,-1);
SELECT LENGTH("ojuba");

عرض حقول مخصوصة

يمكن عرض كل الحقول عبر * ويمكن تحديد الحقول المطلوبة عبر سردها مفصولة بعلامة ,

SELECT first_name, last_name FROM users;

يمكن عمل كنية لكل حقل عبر AS هكذا

SELECT first_name AS n1, last_name AS n2 FROM users;

الترتيب

يمكنك ترتيب الصفوف عبر ORDER BY متبوعة باسم الحقل ثم ASC (وهو التلقائي) أو DESC للتصاعدي أو التنازلي على الترتيب.

SELECT * FROM users ORDER BY last_name ASC;

يجوز الترتيب على أكثر من حقل

SELECT * FROM users ORDER BY first_name ASC, last_name ASC;

في هذا المثال نرتب على الاسم الأول فإن تساوى على الأخير.

الحد وتقليب الصفحات

يمكن وضع حد أعلى لعدد الصفوف المجلوبة عبر عبارة LIMIT مثلا لاستخراج صف واحد يمكنك كتابة

SELECT * FROM addressbook LIMIT 1

لاستخراج أول 5 صفوف عند التريب حسب الاسم الأخير أبجديا

SELECT * FROM addressbook ORDER BY last_name LIMIT 5

يمكن تخطي عدد من الصفوف بواسطة عبارة الإزاحة OFFSET متبوعة بعدد الصفوف المتخطاة. مثلا العبارة السابقة تكافئ

SELECT * FROM addressbook ORDER BY last_name LIMIT 5 OFFSET 0

أما الصفحة الثالثة فتكون بتخطي 10 صفوف (5 في كل صفحة) لعرض الصفوف حتى الخامس عشر.

SELECT * FROM addressbook ORDER BY last_name LIMIT 5 OFFSET 10

ويمكن وضع فاصلة , مكان عبارة OFFSET لكن ترتيب التعبيرين مقلوب (الإزاحة أولا ثم الحد)

SELECT * FROM addressbook ORDER BY last_name LIMIT 10, 5

لعرض رقم آخر صف يمكن استخدام الترتيب العكسي مع وضع حد أعلى صف واحد

SELECT rowid FROM addressbook ORDER BY rowid DESC LIMIT 1

الشرط WHERE

يمكن عرض الصفوف التي تطابق شرط معين عبر عبارة WHERE متبوعة بالشرط أو الشروط المركبة عبر AND أو OR ويمكن استخدام الأقواس.

مثلا لعرض الصفوف التي يكون الاسم الأخير فيها omar نكتب الاستعلام

SELECT * FROM addressbook WHERE last_name = 'omar';

هناك الكثير من العبارات المرنة يمكن أن نستعملها بعد WHERE مثل:

  • فحص المساواة =
  • المقارنة < أو >
  • IS NULL
  • IS NOT NULL
    • مطابقة الأنماط عبر الشبه LIKE حيث نستعمل % بمعنى أي شيء من أي طول

مثلا لعرض الأسماء التي تبدأ ب A

SELECT * FROM addressbook WHERE last_name LIKE 'A%' ORDER BY last_name;
مطابقة نمط السوابق (كما في المثال السابق) يستفيد من الفهارس أما اللواحق أو التوسط فلا يستفيد.
قبل أن تتندر على كود NULL=NULL تعطي FALSE عليك تخيل أن NULL تمثل قيمة مجهولة خالية. فإن القيمة المجهولة لا تساوي القيمة المجهولة الأخرى. لهذا نستعمل أداة IS NULL

ربط الجداول JOIN

يمكن مقابلة جدولين أو أكثر كل صف في الأول مع كل صفوف الآخر كما في الضرب الديكارتي. لنأخذ المثال التالي:

CREATE TABLE items (name VARCHAR(20) PRIMARY KEY, price DOUBLE, in_stock INTEGER);
CREATE TABLE cart (item_name VARCHAR(20), NUMBER INTEGER);
INSERT INTO items VALUES ('milk', 2.5, 1000);
INSERT INTO items VALUES ('chicken', 5.1, 3000);
INSERT INTO cart VALUES ('milk', 3);
INSERT INTO cart VALUES ('milk', 1);
INSERT INTO cart VALUES ('chicken', 2);
جدول الأصناف items
name price in_stock
milk 2.5 1000
chicken 5.1 3000
جدول عربة التسوق cart
item_name number
milk 3
milk 1
chicken 2

مقابلة الجدولين تكون هكذا

SELECT *, price*NUMBER AS total_price FROM items JOIN cart;

والتي تعطي

name price in_stock item_name number total_price
milk 2.5 1000 milk 3 7.5
milk 2.5 1000 milk 1 2.5
milk 2.5 1000 chicken 2 5.0
chicken 5.1 3000 milk 3 15.3
chicken 5.1 3000 milk 1 5.1
chicken 5.1 3000 chicken 2 10.2

لكن هناك صفوف لا نريدها فلماذا نضرب عدد الدجاجات في سعر الحليب!

أول ما يخطر بالبال هو استعمال WHERE لمطابقة اسم الصنف

SELECT *, price*NUMBER AS total_price FROM items JOIN cart WHERE name=item_name;

حيث سنحصل على

name price in_stock item_name number total_price
milk 2.5 1000 milk 3 7.5
milk 2.5 1000 milk 1 2.5
chicken 5.1 3000 chicken 2 10.2

لكن لاداع لذلك فكل عبارة JOIN تأخذ بعدها مباشرة عبارة ON متبوعا بشرط مطابقة الصفوف بين الجدولين يمين ويسار عبارة JOIN كما في الأمر التالي والذي يكافئ السابق:

SELECT *, price*NUMBER AS total_price FROM items JOIN cart ON name=item_name;

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

يمكنك أن تعطي الجدول أو الحقل كنية وذلك بذكر الكنية بعد عبارة AS كما فعلنا في حاصل ضرب العدد في سعر price*number وسميناه السعر الإجمالي total_price

أنواع الربط

الربط الداخلي INNER JOIN (النوع التلقائي)

لنضف حقول غير متقابلة في الجدولين

INSERT INTO items VALUES ('beans', 0.5, 2000);
INSERT INTO cart VALUES ('cheese', 1);

الآن لننفذ أمر الربط السابق والذي يكافئ تحديد نوع الربط على أنه INNER JOIN لأنه التلقائي

SELECT *, price*NUMBER AS total_price FROM items INNER JOIN cart ON name=item_name;

نلاحظ أن النتيجة هي نفسها كما كانت قبل إضافة الحقول غير المتقابلة ولم نجد صنف beans لأنه لم يظهر في عربة التسوق ولا وجدنا الجبن الذي ظهر في العربة ولم يظهر في الأضناف

لنقم الآن بإضافة صفوف متقابلة لكن بقيمة خالية NULL في حقول التقابل

INSERT INTO items VALUES (NULL, 0.75, 8500);
INSERT INTO cart VALUES (NULL, 1);

وسنجد أن المقابلة الداخلية لم تشمل الصفوف الجديدة لأنها خالية

تذكر أن الخالي لا يفحص بالتساوي NULL=NULL لأنه يعطي FALSE بل يفحص ب IS NULL

الربط الخارجي OUTER JOIN

وهو على 3 أنواع هي

  • التام FULL OUTER JOIN
  • الأيسر LEFT OUTER JOIN أو LEFT JOIN اختصارا وهو الوحيد المدعوم في SQLite
  • الأيمن RIGHT OUTER JOIN أو RIGHT JOIN اختصارا

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

SELECT *, price*NUMBER AS total_price FROM items LEFT JOIN cart ON name=item_name;
name price in_stock item_name number total_price
milk 2.5 1000 milk 1 2.5
milk 2.5 1000 milk 3 7.5
chicken 5.1 3000 chicken 2 10.2
beans 0.5 2000 NULL NULL NULL
NULL 0.75 8500 NULL NULL NULL

وهنا علينا ملاحظة ما يلي:

  • لاحظ أن الجبن لم يكن بين النتائج لأنه في الطرف الأيمن ولا مقابل له في الطرف الأيسر.
  • الصف الأيسر الذي حقل المقابلة في خال NULL موجود لكنه لم يتم مقابلته مع الأيمن الذي حقل المقابلة فيه NULL بل كان ما يقابله حقول كلها خالية ذلك أن NULL لا تساوي NULL بل تفحصل ب IS NULL
يمكن محاكة الربط الأيمن بقلب الترتيب واستخادم الأيسر.
لاحظ أن الحليب موجود مرتين ذلك أن اسم الصنف في جدول items مفتاح أساسي فريد أما في جدول عربة التسوق فهو حقل عادي غير فريد وهذا يقودنا لاحقا لعمل علاقات واحد إلى أكثر One to Many أما إن كان فريدا فتكون العلاقة واحد لواحد One to One ويسمى اسم الصنف item_name في جدول العربة المفتاح الخارجي Foreign Key

تجميع الصفوف GROUP BY

لاحظ أن الحليب موجود مرتين في العربة يمكننا جمهما حيث تتيح لنا عبارة GROUP BY تحديد عدد من الحقول إن تطابقت في أكثر من صف جمعا معا في صف واحد وذلك باستخدام الحقل المشترك وأي عدد من دوال التجميع aggregation functions مثل المجموع SUM والمتوسط AVG وغيرها.

SELECT item_name, SUM(NUMBER) AS total_number FROM cart GROUP BY item_name;

والتي تعطي (حيث جمع مدخلتا الحليب في مدخلة واحدة عوضا عن 1 و 3 أصبحت مدخلة واحدة عددها 4)

item_name total_number
NULL 1
cheese 1
chicken 2
milk 4
الحقول غير المذكورة في التجميع يجب أن تجلب عبر دالة تجميع وإلا فإنها ستكون واحد عشوائية من بين الصفوف المجمعة.

إن استخدام أي دالة تجميع مثل SUM دون ذكر التجميع يؤدي إلى عملية تجميع لكل الصفوف. لذا يمكننا طباعة السعر الإجمالي للفاتورة كاملة بالاستعلام التالي:

SELECT SUM(price*NUMBER) AS total FROM items JOIN cart ON name=item_name;

الاشتراط بعد التجميع عبر HAVING

لا يجوز استخدام أي من دوال التجميع مثل SUM في عبارة WHERE لأنها تنفذ قبل التجميع GROUP BY وهنا جاءت الحاجة لعبارة HAVING والتي تذكر بعد GROUP BY

ترتيب الشروط هو ON التي تستخدم مقرونة مع JOIN ثم WHERE والتي تحدث قبل GROUP BY وأخيرا تنفذ HAVING

الإضافة من استعلام

يمكنك أخذ نواتج عملية الاستعلام (بكل مزاياها) وتمريرها إلى INSERT INTO وذلك بوضع عبارة SELECT مكان VALUES وذلك جائز سواء من نفس الجدول أو من جدول آخر أو من عملية دمج JOIN …إلخ

INSERT INTO t1 (col1, col2) SELECT t2.c1, t2.c2 FROM t2 WHERE t2.c1>5;

ترحيل الحركات دفعة واحدة transactions

لنتخيل أن لدينا مجموعة من الاستعلامات نريد تنفيذها دفعة واحدة (نسميها حركة Transaction) وذلك بوضع BEGIN في قبل أول عملية و COMMIT بعد آخر واحدة هذا يجعلها أسرع بشكل كبير جدا

CREATE TABLE t1 (col1 TEXT);
BEGIN;
INSERT INTO t1 VALUES ('cell1');
INSERT INTO t1 VALUES ('cell2');
INSERT INTO t1 VALUES ('cell3');
INSERT INTO t1 VALUES ('cell1');
INSERT INTO t1 VALUES ('cell5');
COMMIT;

لكن السرعة ليست هي الفائدة الوحيدة. مثلا فلتكن إضافة 5 صفوف لكن إضافة الصف الرابع فشلت لسبب أو لآخر (كأن يكون حقل فريد مكرر) إن كنا نريد ضمان دخول الخمسة بحركة واحدة إما كلها وإما لا شيء فإننا نستعمل ROLLBACK كي نعود إلى الحالة التي كان عليها قبل الحركة.

لاحظ أن الاستعلام في آخر المثال التالي لم يعد أي نتائج.

sqlite> DROP TABLE t1;CREATE TABLE t1 (col1 TEXT UNIQUE);
sqlite> BEGIN;
sqlite> INSERT INTO t1 VALUES ('cell1');
sqlite> INSERT INTO t1 VALUES ('cell2');
sqlite> INSERT INTO t1 VALUES ('cell3');
sqlite> INSERT INTO t1 VALUES ('cell1');
Error: COLUMN col1 IS NOT UNIQUE
sqlite> ROLLBACK;
sqlite> SELECT * FROM t1;

هناك 3 أنواع من الحركات

  • المؤجل deferred وهو النوع التلقائي وهنا لا تففل قاعدة البيانات.
  • الفوري immediate هذا النوع يقفل عمليات الكتابة ولا يقفل القراءة
  • الحصري exclusive هذا النوع يقفل كل شيء

العلاقات

  • علاقة واحد إلى واحد One to One
  • علاقة واحد إلى العديد One to Many
  • علاقة العديد إلى واحد Many to One
  • علاقة العديد إلى العديد Many to Many

دعاة اسقاطها NoSQL

ظهرت الكثير من الدعوات باسم NoSQL تدعو إلى التخلي عن SQL وهذا من أسوأ المصطلحات في عالم الحاسوب فهي في الواقع لا تنتقد اللغة في حد ذاتها بل تنتقد الصفة العلائقية فيها relational ويقترح البعض تسميتها حركات NoREL.

قواعد البيانات العلاقية RDBMS والتي نتخاطب معها بلغة SQL مبنية على مبدأ ACID أي

  • atomicity
  • consistency
  • isolation
  • durability

أما NoSQL فهي على مبدأ BASE أي:

  • Basically Available
  • Soft state
  • Eventual consistency

من أشهر العبارات التي تستخدم للدعوة إلى NoSQL

SELECT fun, profit FROM real_world WHERE relational=FALSE;

أحد أصدقائي محبي SQL رد عليهم

Query OK, 0 ROWS selected (0.00 sec)

نقاش

password, 2010/07/17 01:16

ما الوقت المحدد للانتهاء من كتابة المقال/الدرس؟

مؤيد السعدي, 2010/07/17 13:50

لا يوجد وقت. هي بحسب فراغي. من يريد المساهمة فأهلا وسلها.

عمر, 2010/12/10 15:52

يارب مساعدة

أبو حفصة, 2010/12/13 15:07

ملاحظة : عنوان المقال حول SQLITE

بينما الأمثلة حول MySQL

مؤيد السعدي, 2010/12/16 11:24

سأشرح SQL وليس SQLITE ولا MySQL لكنني سأطبق على SQLITE في الغالب وسأتطرق للفروق إن لزم الأمر

بن عبد الله يوسف, 2011/03/03 16:00

شكرا و بارك الله فيك على هذا الطرح أود أن تطلعنا عن مميزات و سلبيات sqlite ان أمكن و مدى فعاليته ان قارناها ب MySql

مؤيد السعدي, 2011/03/03 17:32

هما أمران مختلفان لكن يتحدثان لغة واحدة هي SQL فقواعد sqlite هي ملفات أما MySQL فهي خادم Server تمتاز الأولى بالسرعة لكنها تعمل قفل lock على مستوى قاعدة البيانات كاملة وليس على مستوى الجدول أو حتى الصف عند الكتابة مما يسبب البطئ عند وضعها في بيئة تحتاج كتابة متوازية

بن عبد الله يوسف, 2011/03/03 19:04

اذن أخي أفهم بأن SqLite لا يمكن استعمالها كقاعدة بيانات لتطبيق مشترك في الادخال على سبيل المثال لو استخدمناها في قاعدة لموقع و كان عدد المتعاملين مع القاعدة كثير سيسبب بطأ لأن القاعدة تشغل من طرف شخص اخر حتى يتم تحريرها

mr diet, 2013/05/18 18:52

بارك الله فيك

fisher man, 2013/06/04 15:29

بارك الله فيك وجزاك الله كل خيرررررررررر

marouf, 2015/07/08 07:52

جزاك الله خير

سارة محمد, 2015/10/19 07:34

السلام عليكم ..

شكراً لك على المقال القيم ..

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

وشكراً لك مقدماً ..

خالد الحاج, 2015/12/07 15:46
السلام عليكم
سؤالى
اذا محتاج اطرح قيمتين فى حقل واحد ولكن بتواريخ مختلفه مثال
التاريخ الرقم
2015/12/1 1250
2015/12/5 1000
محتاج الناتج يكون هكذا
التاريخ الرقم الرقم الجديد
2015/12/1 1250 250
2015/12/5 1000 250
وشكرا لك مقدما
أيمن النشار, 2015/12/23 01:33
السلام عليكم
بعتقد على حد ما فهمت من سؤال حضرت

محتاج تطرح مبلغ كان على العميل بتاريخ معين وقام بدفع جزء منه بتاريخ جديد
وإن كان كذلك فأليك مثال بسيط عليك بتصميم شاشة البيانات التى تقم بعمل الحسبه هذه ولنفرض
حضرتك هتصمم المشروع باستخدام VB.NET 2012 وضعت الادوات الاتيه:
تضع على الفورم مفتاحين جديد وحفظ
dateTimePicker1 <-------- DTime1 أداة التاريخ السابق
dateTimePicker2 <-------- DTime2 أداة التاريخ الحالى

TextBox <-------- txt_debt أداة نص الدَّيْن
TextBox <-------- txt_Payed أداة نص الدفوع
TextBox <-------- txt_Remained أداة نص المتبقى
وأنشئ جدول Product به ستة حقول من خلال SQL
ID حقل مفتاح أساس هتحتاجه بالبحث وهو رقمى Int غير متكرر ولا تنسى تجعل قيمة Idintety Spysification =Yes
DT1 حقل التاريخ السابق
DT2 حقل التاريخ الحالى
Debt حقل الدّين
Payed حقل المدفوع
Remained حقل المتبقى

ثم أنشئ كلاس Retrive_Class وضع به هذا الكود
public struct Debt
{
#endregion
// -- Save Data Product
CMD.CommandText =
"Insert into Product values(@DT1,@DT2,@Debt,@Payed,@Remained)";
SqlParameter[] p = new SqlParameter[5];
p[0] = new SqlParameter("@DT1", DT1);
p[1] = new SqlParameter("@DT2", DT2);
p[2] = new SqlParameter("@Debt", Debt);
p[3] = new SqlParameter("@Payed", Payed);
p[4] = new SqlParameter("@Remained", Remained);
CMD.Parameters.AddRange(p);
int aff = CMD.ExecuteNonQuery();
if (aff > 0)
{
MessageBox.Show ("تم الحفظ بنجاح");
}
CMD.Parameters.Clear();
MessageBox.Show ("عفوا لم يتم الحفظ");
}
#endregion
}
ثم فى حدث تغير قيمة نص المدفوع TextChanged وهو اللى حضرتك تقريبا تقصده اكتب هذا الكود:
private void txt_Remained_TextChanged(object sender, EventArgs e)
{
txt_Remained.Text = (double.Parse(txt_debt.Text) - double.Parse(txt_Payed.Text) )).ToString();
}
كود جديد:سهل يمكنك فعله وهو كل ما فيه هتهيأ صناديق النص لادخال القيم الجديده يعنى هتمسح أى مدخلات سابقه
private void button1_Click(object sender, EventArgs e)
{
txt_debt.Text = "";
txt_Payed.Text = "";
txt_Remained.Text = "";
}

كود حفظ:
private void button2_Click(object sender, EventArgs e)
{
Retrive_Class.StoreSales.Debt(DT1.Value.Date.ToString(), DT2.Value.Date.ToString(),
txt_debt.Text, txt_Payed.Text, txt_Remained.Text,);
}
جرب وإبعت لى الرد من هنا
haider, 2016/01/18 13:54
السلام عليكم ورحمة الله وبركاته
ماذا يعني با الكود في نظام sql عندي برزن تيشن هذه الاسبوع وكتبت المشروع والدكتورة قالت نقص كودات ارجو المساعدة بالرد واذا تحب اشوفك البحث على الايميل الخاص بك وتساعدني ولك الاجر والثواب ان شاء الله
عبد الله, 2016/03/03 23:58
شرح جميل ومنسق ورائع ،،، اين تذهب مثل هذه المواقع عن واجهة الويب العربي ؟

لماذا لا نرى مثل هذه المواقع في المقدمة لدى المستخدمين العرب ؟

لعل من أهم الأسباب اندماج الكثير من الخصائص في موقع واحد مما يصعب الحصول على المعلومة الفردية لدى أغلب الجمهور.

بارك الله فيك أخ مؤيد.
بدر محمد , 2016/03/24 01:40
بسم الله ماشاء الله تسلم ايدك وعقلك
يار يحفظ امثلك من الناس الذى تحب ان تساعد الناس
هل من الممكن معرفة السكى بى الخاص بيك للتعرف عليك ؟
والله نفسى اتعلم منك حجات كتير انا اتعلمت الجزء الخاص بالبحث هذا الذى يفيدنى فى عملى
رحاب, 2016/03/30 00:03
طيب ممكن شرح لجملة التعديل ALTER في إضاقة FOREIGN KEY للجدول
عبدالفتاح امين ودالنجوم, 2016/04/15 16:51
شكرا على المقال سؤالي هو ما هي استخدامات الامر Roll back
اسامة سيد, 2016/05/13 21:51
الامر roll back مهمته يعمل تراجع خطواء للوراء
..مثل ctrl+z في نظام الويندوز
عثمان السماني, 2016/05/30 12:52
مقال ممتاز ومشكور ومأجور انشاء الله .
لدي استفسار حول امكانية تحديث حقول قواعد البيانات sqlserver من خلال ملفات ال query .
اعمل على مشروع منظومة وتم تشغيلها بجهاز وهو حاليا يعمل وبه بيانات تم ادخالها وهي مخزنة على القاعدة
وفي كل مرحلة اقوم فيها بتطوير النظام باضافة حقول وجداول وصفوف واعمدة .... الخ آخره .
وتبقى اماي عقبة اعادة ادخال الحقول على القاعدة باضافتها يدويا بالمرور على كافة الجداول وانشاء الجداول المطلوبة في كل مرة
يحدث فيها تطوير للنظام ... تعلمت العمل على كيفية اخذ ملف qery وادراجه بالقاعدة ولكنه فقط يعمل على كل ملف على حدة
فهل من سبيل لتطوير وتبديل اسماء الحقول وانشاء جداول بحقولها من خلال query ??? ولك منا اجزل الشكر والتقدير والاحترام
خالد المشهداني, 2016/06/08 18:54
اين الخطأ في هذا الكود
Dim sqlcon As New SqlClient.SqlConnection("data source=(local);initial catalog=kah_training;integrated security=true;")
Dim SQLAdap As New SqlClient.SqlDataAdapter
Dim Comm As New SqlClient.SqlCommand
Dim comBild As New SqlClient.SqlCommandBuilder
Dim ds As New DataSet()
sqlcon.Open()

Dim X As Integer
Dim y As String
y = txt_share_name.Text


Comm = New SqlClient.SqlCommand("select * from employees where emp_full_name =N'" & txt_share_name.Text & "'", sqlcon)
SQLAdap.SelectCommand = Comm
SQLAdap.Fill(ds, "ds_emp")

X = ds.Tables("ds_emp").Rows(0).Item("emp_id")


Comm = New SqlClient.SqlCommand("select * from emp_courses where emp_id =" & X & "AND course_id=" & Request.QueryString("course_id"), sqlcon)
SQLAdap.SelectCommand = Comm
SQLAdap.Fill(ds, "ds_emp_courses")

Dim dr As DataRow
dr = ds.Tables("ds_emp_courses").NewRow


dr.Item("emp_id") = X
dr.Item("course_id") = Request.QueryString("course_id")
dr.Item("result") = txt_result.Text
dr.Item("arragment") = txt_arrag.Text
dr.Item("note") = txt_note.Text

ds.Tables("ds_emp_courses").Rows.Add(dr)


comBild.GetUpdateCommand()**رسالة الخطأ هنا
SQLAdap.Update(ds, "ds_emp_courses")

sqlcon.Close()

Response.Redirect("courses.aspx")
رسالة الخطأ هي:The DataAdapter.SelectCommand property needs to be initialized.
Abdulkader, 2016/06/16 16:02
انا مبتدأ على الSQL Server أعطيه اي استعلام ويرجع لي Msg 208
invali object name 'pub'
ومع اني عامل كونيكت لقاعدة البيانات
والتعليمة جدا بسيطة
select *
from pubs
عبدالملك الدروبي, 2016/08/17 22:50
السلام عليكم ورحمة الله
ياشباب لوسمحتم
عندي مشكلة في انشاء الجدوال في قواعد البيانات
علي سبيل المتال
انشاء الجدوال باسم userss
العمود uers 1
المطلوب تعديل العمود من uers الي users
وصال, 2016/11/16 20:46
عاشت ايدك
سلوى سعيد, 2016/12/03 15:11
ازا ممكن استفسار بسيط كيفية ربط شريحة واحدة من بوربوينتازا الى قاعدة البينات بواسطة استعلام sqlو شكرًا للمساعدةثقق
ahmad alkel, 2016/12/17 15:28
ما الاجرائية التي من شئنها حذف سجل من جدول مع حذف الارتباطات المتعلقة به
محمد طه, 2017/09/16 03:41
ادخل على قائمة الأرتباطات Database Diagram

قم بالنقر على مسار الأرتباط يظهر لك حذف الأرتباط ومن ثم خصائص propareties

انقر فوق propareties

في خالة insert and update

قم بالنقر على Cascade اذا اردت تتالي الحذف

واذا اردت الغاء تتالي الحذف او التعديل يمكنك النقر فوق خانة No Action
احمد, 2016/12/23 09:49
ممتاز
sawsan samy, 2016/12/27 16:35
من فضلك عايزة احسب الاجازات الرسمية خلال الشهر
كنت كتبت كود لحساب الجمعه والسبت فقط
ممكن مساعدة
محمد رمضان عطية, 2017/01/16 09:57
اريد معرفة كيف اقوم بجعل حقل فى جدول عدم تكرار البيانات به فى قاعدة بيانات sql داخل phpmyadmin
محمود سلطان, 2017/03/03 21:38
السلام عليكم ورحمة الله
بارك الله بكم ونفع بعلمكم
لدي اسؤال احبتي في الله
انا انشئة برنامج صغير في البايثون ووظيفته تعبئة حقول قاعدة البيانات من قبل المستخدم وفيه 6براميترات
1- رقم الموظف
2- الاسم
3-رقم الجوال
4-التخصص
5-القسم
6-الرتبه العسكريه
المشكله التي واجهتني هي ترميز الغه العربيه في قاعدة البيانات
كمثال
لو انشئنا متغير باسم x
x='كلمه'
واريد اضافتها الى VALUES
لاتعمل الا من داخل متغير التنفيذ
مثال
Q.execute(''' INSERT INTO Soldiers(id,Name,Phone,GradeMILITARY,Specialty,Section)
VALUES(?,?,?,?,?,?)''',(id,Name,Phone,GradeMILITARY,Specialty,Section))
كيف يمكنني اضافة الغه العربيه للفاليو وشكر لكم
احمد شهاب, 2017/06/18 13:02
السلام عليكم

انا درست قواعد البيانات المتقدمه (sq) في بلدي ولكن بعد حدوث الحرب اضطررت السفر لدوله ثانيه وانا الان في المانيا
وطلبوا مني تطبيق لكن انا درست قبل 5 سنوات المشكله هي لا اعرف كيف عملهم وليس لديه خبره فيها كل مااريد ان اقوله
ممكن تساعدوني في انجاز هذه المهمه فهذه فرصه للعمل هنا
من اين ابدء ؟
ناصر عوض للاجهزة الصوتية والالكترونيات, 2017/08/22 18:29
انا بصمم موقع شبه موقع مدارس اريد عند ادخال معلومات يجب ان احفظ العمل لا يقبل الحفظ ارجو منكم المساعدة هاذا كود الحفظ عندي
<tr>
<td></td>
<td><input type="submit" name="ok_add" value="حفظ البيانات"</td>
</tr>
خالد ناصر, 2021/06/02 16:32
السلام عليكم
اريد كود جلب البيانات بشكل افقي صورة المنتج وتحته سعره وعند اضافة منتج اخر يصبح بجنبه بشكل افقي اريد استخدم بوتسراب واقسم الصفحة الى اربع اعمدة في سطر واحد ووضع المنتجات بجانب بعضها

ولكم جزيل الشكر
Rihab, 2021/06/27 05:22
Hkkkncg
طارق ابراهيم, 2022/03/29 10:43
شكرا علي كل ماتقدمه من خدمات وربي يحفظك
سوالي هوا نقوم بتغير نظم العمل بي مصرف من المنظومة الي منظومة اخره
نظم العمل بي اوركل توجد عندي مشكله في تكرر الحسابات الزبائن المطلوب هوا sqlيقوم هدا امر بتصنيف الحسابات وعدم تكراره بحيت .
متال رقم زبون يوجد بي الفرع رقم 12 يتكرر معي في اكتر من فرع هيل يوجد امر خاص يعمل علي فصل الحسابات دون تكرر وشكرا
ناجي بن عمر , 2023/04/19 13:45
جازاك الله خيرا على كل ما قدمته ...

فضلا إذا بالإمكان تبعث لي دروس مفصلة عن sql server
إدخال التعليق من هنا:
T A F H Z
 
docs/sql_basics.txt · آخر تعديل: 2015/04/23 03:20 بواسطة 127.0.0.1

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki