جدول المحتويات
بدهيات استعلامات 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 فإنها توفر الأنواع الإضافية التالية:
- حقل نصي عالمي حر الطول TEXT وهو يدعم unicode دون الحاجة لتحديد أي طول. (هذا النوع متوفر في MySQL و PostgreSQL)
- حقل ثنائي BLOB لحفظ سلسلة من البيانات (ليس بالضرورة أن تكون نصا كحفظ محتويات ملف .jpg مثلا)
- حقل عددي صحيح INTEGER
- وحقل عددي نسبي REAL
لا توفر SQLite دعما حقيقيا لحفظ التاريخ والوقت لكن يمكنك محاكاة ذلك عبر استعمال INTEGER وحفظ عدد الثواني بعد ما يعرف ببزوغ فجر يونكس Unix epoch.
يمكنك استعمال أنواع SQL القياسية لكن اعلم أن SQLite ستدير الحجم ولن تحترم النوع أو الحد الذي تطلبه.
إنشاء قاعدة بيانات
تختلف الطريقة من قاعدة بيانات إلى أخرى.
في SQLite
في sqlite يكفي تنفيذ sqlite3 متبوعا باسم الملف المطلوب إنشاؤه ليكون قاعدة بيانات
sqlite3 my_db_file.db
ولأغراض الاختبار يمكنك استعمال جدول في الذاكرة المتطايرة RAM وذلك بتمرير :memory: عوضا عن اسم الملف هكذا
sqlite3 :memory:
يتوفر واجهة رسومية تتعامل مع ملفات sqlite وهي متوفرة بصيغتين أسهلهما إضافة لمتصفح الإنترنت firefox (داخل قائمة أدوات tools) أو برنامج مستقل (يتم تنفيذه عبر xulrunner)
في MySQL
في mysql نتصل مع خادم قاعدة البيانات مثلا بالأمر
mysql --default-character-set=utf8 -u root
أضف الخيار p حتى يسألك عن كلمة السر. يمكنك تحديد مستخدم غير root.
ثم ننشئ قاعدة البيانات بالأمر
CREATE DATABASE testdb;
ولعرض بقواعد البيانات المتوفرة نستعمل 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) );
يمكنك وصف عمود ما بأنه معرّف أساسي 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" );
إضافة الصفوف
يمكننا إضافة الصفوف إلى الجدول عبر 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 | 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 بعد إنشاء الجدول.
لاحظ هذا المثال
لكن لو أردنا أن نعمل فهرسا فريدا للاسم الأول وآخر للاسم الثاني
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 مهما كان الاسم الثاني أيضا يكون سريعا
المفتاح الرئيسي 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) );
الاستعلام
الاستعلام يكون عبر 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;
ربط الجداول 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);
وسنجد أن المقابلة الداخلية لم تشمل الصفوف الجديدة لأنها خالية
الربط الخارجي 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
تجميع الصفوف 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
الإضافة من استعلام
يمكنك أخذ نواتج عملية الاستعلام (بكل مزاياها) وتمريرها إلى 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)
نقاش
ما الوقت المحدد للانتهاء من كتابة المقال/الدرس؟
لا يوجد وقت. هي بحسب فراغي. من يريد المساهمة فأهلا وسلها.
يارب مساعدة
ملاحظة : عنوان المقال حول SQLITE
بينما الأمثلة حول MySQL
سأشرح SQL وليس SQLITE ولا MySQL لكنني سأطبق على SQLITE في الغالب وسأتطرق للفروق إن لزم الأمر
شكرا و بارك الله فيك على هذا الطرح أود أن تطلعنا عن مميزات و سلبيات sqlite ان أمكن و مدى فعاليته ان قارناها ب MySql
هما أمران مختلفان لكن يتحدثان لغة واحدة هي SQL فقواعد sqlite هي ملفات أما MySQL فهي خادم Server تمتاز الأولى بالسرعة لكنها تعمل قفل lock على مستوى قاعدة البيانات كاملة وليس على مستوى الجدول أو حتى الصف عند الكتابة مما يسبب البطئ عند وضعها في بيئة تحتاج كتابة متوازية
اذن أخي أفهم بأن SqLite لا يمكن استعمالها كقاعدة بيانات لتطبيق مشترك في الادخال على سبيل المثال لو استخدمناها في قاعدة لموقع و كان عدد المتعاملين مع القاعدة كثير سيسبب بطأ لأن القاعدة تشغل من طرف شخص اخر حتى يتم تحريرها
بارك الله فيك
بارك الله فيك وجزاك الله كل خيرررررررررر
جزاك الله خير
السلام عليكم ..
شكراً لك على المقال القيم ..
بس عندي استفسار بخصوص أسعار إنشاء أي داتا بيس يعني إذا طلب مني تصميم قاعدة بينات لموقع أو منتدى كيف أقدر أعرف قيمة قاعدة البيانات التي صممتها
وشكراً لك مقدماً ..
سؤالى
اذا محتاج اطرح قيمتين فى حقل واحد ولكن بتواريخ مختلفه مثال
التاريخ الرقم
2015/12/1 1250
2015/12/5 1000
محتاج الناتج يكون هكذا
التاريخ الرقم الرقم الجديد
2015/12/1 1250 250
2015/12/5 1000 250
وشكرا لك مقدما
بعتقد على حد ما فهمت من سؤال حضرت
محتاج تطرح مبلغ كان على العميل بتاريخ معين وقام بدفع جزء منه بتاريخ جديد
وإن كان كذلك فأليك مثال بسيط عليك بتصميم شاشة البيانات التى تقم بعمل الحسبه هذه ولنفرض
حضرتك هتصمم المشروع باستخدام 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,);
}
جرب وإبعت لى الرد من هنا
ماذا يعني با الكود في نظام sql عندي برزن تيشن هذه الاسبوع وكتبت المشروع والدكتورة قالت نقص كودات ارجو المساعدة بالرد واذا تحب اشوفك البحث على الايميل الخاص بك وتساعدني ولك الاجر والثواب ان شاء الله
لماذا لا نرى مثل هذه المواقع في المقدمة لدى المستخدمين العرب ؟
لعل من أهم الأسباب اندماج الكثير من الخصائص في موقع واحد مما يصعب الحصول على المعلومة الفردية لدى أغلب الجمهور.
بارك الله فيك أخ مؤيد.
يار يحفظ امثلك من الناس الذى تحب ان تساعد الناس
هل من الممكن معرفة السكى بى الخاص بيك للتعرف عليك ؟
والله نفسى اتعلم منك حجات كتير انا اتعلمت الجزء الخاص بالبحث هذا الذى يفيدنى فى عملى
..مثل ctrl+z في نظام الويندوز
لدي استفسار حول امكانية تحديث حقول قواعد البيانات sqlserver من خلال ملفات ال query .
اعمل على مشروع منظومة وتم تشغيلها بجهاز وهو حاليا يعمل وبه بيانات تم ادخالها وهي مخزنة على القاعدة
وفي كل مرحلة اقوم فيها بتطوير النظام باضافة حقول وجداول وصفوف واعمدة .... الخ آخره .
وتبقى اماي عقبة اعادة ادخال الحقول على القاعدة باضافتها يدويا بالمرور على كافة الجداول وانشاء الجداول المطلوبة في كل مرة
يحدث فيها تطوير للنظام ... تعلمت العمل على كيفية اخذ ملف qery وادراجه بالقاعدة ولكنه فقط يعمل على كل ملف على حدة
فهل من سبيل لتطوير وتبديل اسماء الحقول وانشاء جداول بحقولها من خلال query ??? ولك منا اجزل الشكر والتقدير والاحترام
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.
invali object name 'pub'
ومع اني عامل كونيكت لقاعدة البيانات
والتعليمة جدا بسيطة
select *
from pubs
ياشباب لوسمحتم
عندي مشكلة في انشاء الجدوال في قواعد البيانات
علي سبيل المتال
انشاء الجدوال باسم userss
العمود uers 1
المطلوب تعديل العمود من uers الي users
قم بالنقر على مسار الأرتباط يظهر لك حذف الأرتباط ومن ثم خصائص propareties
انقر فوق propareties
في خالة insert and update
قم بالنقر على Cascade اذا اردت تتالي الحذف
واذا اردت الغاء تتالي الحذف او التعديل يمكنك النقر فوق خانة No Action
كنت كتبت كود لحساب الجمعه والسبت فقط
ممكن مساعدة
بارك الله بكم ونفع بعلمكم
لدي اسؤال احبتي في الله
انا انشئة برنامج صغير في البايثون ووظيفته تعبئة حقول قاعدة البيانات من قبل المستخدم وفيه 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))
كيف يمكنني اضافة الغه العربيه للفاليو وشكر لكم
انا درست قواعد البيانات المتقدمه (sq) في بلدي ولكن بعد حدوث الحرب اضطررت السفر لدوله ثانيه وانا الان في المانيا
وطلبوا مني تطبيق لكن انا درست قبل 5 سنوات المشكله هي لا اعرف كيف عملهم وليس لديه خبره فيها كل مااريد ان اقوله
ممكن تساعدوني في انجاز هذه المهمه فهذه فرصه للعمل هنا
من اين ابدء ؟
<tr>
<td></td>
<td><input type="submit" name="ok_add" value="حفظ البيانات"</td>
</tr>
اريد كود جلب البيانات بشكل افقي صورة المنتج وتحته سعره وعند اضافة منتج اخر يصبح بجنبه بشكل افقي اريد استخدم بوتسراب واقسم الصفحة الى اربع اعمدة في سطر واحد ووضع المنتجات بجانب بعضها
ولكم جزيل الشكر
سوالي هوا نقوم بتغير نظم العمل بي مصرف من المنظومة الي منظومة اخره
نظم العمل بي اوركل توجد عندي مشكله في تكرر الحسابات الزبائن المطلوب هوا sqlيقوم هدا امر بتصنيف الحسابات وعدم تكراره بحيت .
متال رقم زبون يوجد بي الفرع رقم 12 يتكرر معي في اكتر من فرع هيل يوجد امر خاص يعمل علي فصل الحسابات دون تكرر وشكرا
فضلا إذا بالإمكان تبعث لي دروس مفصلة عن sql server