Skip to content

16. استخدام SGBD MySQL

Image

16.1. تثبيت SGBD و MySQL

للحصول على SGBD و MySQL، سنقوم بتثبيت برنامج Laragon.

16.1.1. تثبيت برنامج Laragon

Laragon عبارة عن حزمة تضم عدة برامج:

  • خادم ويب Apache. سنستخدمه لكتابة نصوص برمجية ويب بلغة Python؛
  • برنامج SGBD و MySQL؛
  • لغة البرمجة النصية PHP التي لن نستخدمها؛
  • خادم Redis الذي يوفر ذاكرة تخزين مؤقتة لتطبيقات الويب. لن نستخدمه؛

يمكن تنزيل Laragon (فبراير 2020) من العنوان التالي:

https://laragon.org/download/

Image

Image

  • يؤدي التثبيت [1-5] إلى إنشاء الشجرة التالية:

Image

  • في [6]، مجلد التثبيت الخاص بـ PHP (غير مستخدم في هذا المستند)؛

يؤدي تشغيل [Laragon] إلى عرض النافذة التالية:

Image

  • [1]: القائمة الرئيسية لـ Laragon؛
  • [2]: الزر [Start All] يقوم بتشغيل خادم الويب Apache و SGBD MySQL؛
  • [3]: الزر [WEB] يعرض صفحة الويب [http://localhost
  • [4]: يتيح الزر [Database] إدارة SGBD و MySQL باستخدام الأداة [phpMyAdmin]. يجب تثبيت هذه الأداة مسبقًا؛
  • [5]: يفتح الزر [Terminal] نافذة أوامر؛
  • [6]: يفتح الزر [Root] مستكشف Windows موجهًا إلى المجلد [<laragon>/www] الذي يمثل الجذر لموقع الويب [http://localhost]. وهذا هو المكان الذي يجب وضع التطبيقات الويب الثابتة التي يديرها خادم Apache الخاص بـ Laragon فيه؛

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

سنوضح الآن كيفية إنشاء قاعدة بيانات ومستخدم MySQL باستخدام أداة Laragon.

Image

  • بمجرد تشغيله، يمكن إدارة Laragon [1] من خلال قائمة [2]؛
  • في [3-5]، نقوم بتثبيت أداة إدارة [phpMyAdmin] الخاصة بـ MySQL إذا لم تكن مثبتة بالفعل؛

Image

  • في [6]، يتم تشغيل خادم الويب Apache بالإضافة إلى SGBD وMySQL؛
  • في [7]، يتم تشغيل خادم Apache؛
  • في [8]، يتم تشغيل SGBD وMySQL؛

Image

  • في [8-10]، يتم إنشاء قاعدة بيانات تسمى [dbpersonnes] [11]. سنقوم بإنشاء قاعدة بيانات للأشخاص؛

Image

  • في [11]، سنقوم بإدارة قاعدة البيانات التي أنشأناها للتو؛

Image

  • تقوم العملية [Bases de données] بإرسال طلب ويب إلى URL و[http://localhost/phpmyadmin] و[12]. ويقوم خادم الويب Apache الخاص بـ Laragon بالرد على هذا الطلب. URL و[http://localhost/phpmyadmin] هما URL والأداة المساعدة [phpMyAdmin] التي قمنا بتثبيتها سابقًا [5]. تسمح هذه الأداة بإدارة قواعد البيانات MySQL؛
  • بشكل افتراضي، بيانات تسجيل دخول مسؤول قاعدة البيانات هي: root [13] بدون كلمة مرور [14]؛

Image

  • في [16]، قاعدة البيانات التي أنشأناها سابقًا؛

Image

  • لدينا حالياً قاعدة بيانات [dbpersonnes] و [17] وهي فارغة [18]؛

نقوم بإنشاء مستخدم [admpersonnes] بكلمة مرور [nobody]، والذي سيتمتع بجميع الصلاحيات على قاعدة البيانات [dbpersonnes]:

Image

  • في [19]، ننتقل إلى قاعدة البيانات [dbpersonnes]؛
  • في [20]، نختار علامة التبويب [Privileges]؛
  • في [21-22]، نلاحظ أن المستخدم [root] يتمتع بجميع الصلاحيات على قاعدة البيانات [dbpersonnes]؛
  • في [23]، يتم إنشاء مستخدم جديد؛

Image

  • في [25-26]، سيكون معرّف المستخدم هو [admdbpersonnes]؛
  • في [27-29]، ستكون كلمة مروره هي [nobody]؛
  • في حالة [30]، يشير phpMyAdmin إلى أن كلمة المرور ضعيفة جدًّا (يسهل اختراقها). في بيئة الإنتاج، يُفضل إنشاء كلمة مرور قوية باستخدام [31]؛
  • في [32]، يُشار إلى أن المستخدم [admdbpersonnes] يجب أن يتمتع بجميع الصلاحيات على قاعدة البيانات [dbpersonnes]؛
  • في [33]، يتم التحقق من صحة المعلومات المقدمة؛

Image

  • في [35]، يشير phpMyAdmin إلى أن المستخدم قد تم إنشاؤه؛
  • في [36]، الأمر SQL الذي تم إصداره على أساس؛
  • في [37]، يتمتع المستخدم [admpersonnes] بجميع الحقوق على قاعدة البيانات [dbpersonnes]؛

والآن لدينا:

  • قاعدة بيانات MySQL و [dbpersonnes]؛
  • مستخدم [admpersonnes/nobody] الذي يمتلك جميع الحقوق على قاعدة البيانات هذه؛

16.2. تثبيت الحزمة [mysql-connector-python]

سنقوم بكتابة نصوص برمجية بلغة Python لاستغلال قاعدة البيانات التي تم إنشاؤها مسبقًا بالبنية التالية:

Image

يُستخدم الموصل لعزل كود Python عن جهاز SGBD قيد التشغيل. توجد موصلات لمختلف أجهزة SGBD، وتلتزم جميعها بنفس الواجهة. لذلك، عندما يتم استبدال SGBD وMySQL بـ SGBD وPostgreSQL في المثال أعلاه، تصبح البنية كما يلي:

Image

نظرًا لأن جميع موصلات SGBD تتوافق جميعها مع نفس الواجهة، فلا داعي عادةً لتعديل البرنامج النصي Python. وفي الواقع، فإن معظم موصلات SGBD لها موصل SQL خاص بها:

  • وهي تتوافق مع معيار SQL (لغة الاستعلام الهيكلية)؛
  • ولكنها توسع نطاقها، نظرًا لعدم كفايتها، بإضافات خاصة باللغة؛

لذلك، غالبًا ما يتطلب تغيير قاعدة البيانات SGBD إجراء تعديلات على النصوص البرمجية المتعلقة بـ SQL.

لا توفر لغة Python أصلاً إمكانية إدارة قاعدة بيانات MySQL. ولذلك، يجب تنزيل حزمة برمجية. وهناك العديد من الحزم المتاحة. سنستخدم هنا حزمة [mysql-connector-python] التي تُعد الموصل الرسمي لشركة Oracle، وهي الشركة المالكة لـ MySQL.

سيتم تثبيت الحزمة في نافذة [Terminal] في Pycharm:

Image

  • المجلد [2] ليس له أهمية فيما سيأتي بعد ذلك؛

في المحطة الطرفية، نكتب الأمر [pip search MySQL]:

  • [pip] (Package Installer for Python) هي أداة تثبيت حزم Python. تتصل أداة [pip] بمستودع الحزم الذي يحتوي على حزم Python؛
  • [search MySQL]: يطلب قائمة الحزم التي تحتوي على المصطلح [MySQL] (لا يهم استخدام الأحرف الكبيرة أو الصغيرة) في أسمائها؛

نتائج الأمر هي كما يلي:


mysql (0.0.2)                                                   - Virtual package for MySQL-python
jx-mysql (3.49.20042)                                           - jx-mysql - JSON Expressions for MySQL
weibo-mysql (0.1)                                               - insert mysql
bits-mysql (1.0.3)                                              - BITS MySQL
MySQL-python (1.2.5)                                            - Python interface to MySQL
deployfish-mysql (0.2.13)                                       - Deployfish MySQL plugin
mtstat-mysql (0.7.3.3)                                          - MySQL Plugins for mtstat
bottle-mysql (0.3.1)                                            - MySQL integration for Bottle.
WintxDriver-MySQL (2.0.0-1)                                     - MySQL support for Wintx
py-mysql (1.0)                                                  - Operating Mysql for Python.
mysql-utilities (1.4.3)                                         - MySQL Utilities 1.4.3 (part of MySQL Workbench Distribution 6.0.0)
….                                        - Tool to move slices of data from one MySQL store to another
mysql-tracer (2.0.2)                                            - A MySQL client to run queries, write execution reports and export results
mysql-utils (0.0.2)                                             - A simple MySQL library including a set of utility APIs for Python database programming
mysql-connector-repackaged (0.3.1)                              - MySQL driver written in Python
dffml-source-mysql (0.0.5)                                      - DFFML Source for MySQL Protocol
mysql-connector-python (8.0.19)                                 - MySQL driver written in Python
  INSTALLED: 8.0.19 (latest)
prometheus-mysql-exporter (0.2.0)                               - MySQL query Prometheus exporter
backwork-backup-mysql (0.3.0)                                   - Backwork plug-in for MySQL backups.
django-mysql-manager (0.1.4)                                    - django-mysql-manager is a Django based management interface for MySQL users and databases.
….                                              - mysql operate

C:\Data\st-2020\dev\python\cours-2020\v-01>

تم سرد جميع الوحدات التي تحتوي أسماؤها أو أوصافها على الكلمة المفتاحية MySQL. الوحدة التي سنستخدمها (فبراير 2020) هي [mysql-connector-python]، السطر 17. لتثبيتها، نكتب في المحطة الطرفية الأمر [pip install -U mysql-connector-python]:


C:\Data\st-2020\dev\python\cours-2020\v-01>pip install -U mysql-connector-python
Collecting mysql-connector-python
  Using cached mysql_connector_python-8.0.19-py2.py3-none-any.whl (355 kB)
Requirement already satisfied, skipping upgrade: protobuf==3.6.1 in c:\myprograms\python38\lib\site-packages (from mysql-connector-python) (3.6.1)
Requirement already satisfied, skipping upgrade: dnspython==1.16.0 in c:\myprograms\python38\lib\site-packages (from mysql-connector-python) (1.16.0)
Requirement already satisfied, skipping upgrade: six>=1.9 in c:\users\serge\appdata\roaming\python\python38\site-packages (from protobuf==3.6.1->mysql-connector-python) (1.14.0)
Requirement already satisfied, skipping upgrade: setuptools in c:\myprograms\python38\lib\site-packages (from protobuf==3.6.1->mysql-connector-python) (41.2.0)
Installing collected packages: mysql-connector-python
Successfully installed mysql-connector-python-8.0.19
  • السطر 1: الخيار [install -U] (U=upgrade) يطلب أحدث إصدار من الحزم المختلفة المرتبطة بالحزمة [mysql-connector-python]؛

لمعرفة الحزم المثبتة في بيئة Python على جهازنا، نكتب الأمر [pip list]:


C:\Data\st-2020\dev\python\cours-2020\v-01>pip list
Package                Version
---------------------- ----------
asgiref                3.2.3
astroid                2.3.3
atomicwrites           1.3.0
attrs                  19.3.0
certifi                2019.11.28

MarkupSafe             1.1.1
mccabe                 0.6.1
more-itertools         8.1.0
mysql-connector-python 8.0.19
mysqlclient            1.4.6
packaging              20.0
pip                    20.0.1
pipenv                 2018.11.26

  • السطر 13: نجد بالفعل الحزمة [mysql-connector-python]؛

لمعرفة كيفية استخدام الحزمة [mysql-connector-python] لإدارة قاعدة بيانات MySQL، سننتقل إلى موقع الحزمة |https://dev.mysql.com/doc/connector-python/en/|. فيما يلي سلسلة من الأمثلة.

16.3. النص البرمجي [mysql_01]: الاتصال بقاعدة بيانات MySQL - 1

يقدم البرنامج النصي [mysql_01] الخطوة الأولى لاستخدام قاعدة البيانات. سيسمح لنا هذا البرنامج النصي بالتحقق من قدرتنا على الاتصال بقاعدة البيانات [dbpersonnes] التي تم إنشاؤها مسبقًا.


# استيراد الوحدة النمطية mysql.connector
from mysql.connector import connect, DatabaseError, InterfaceError

# الاتصال بقاعدة بيانات MySql [dbpersonnes]
# هوية المستخدم هي (admpersonnes، nobody)
USER = "admpersonnes"
PWD = "nobody"
HOST = "localhost"
DATABASE = "dbpersonnes"

# ها نحن ذا
connexion = None
try:
    print("Connexion au SGBD MySQL en cours...")
    # تسجيل الدخول
    connexion = connect(host=HOST, user=USER, password=PWD, database=DATABASE)
    # المتابعة
    print(
        f"Connexion MySQL réussie à la base database={DATABASE}, host={HOST} sous l'identité user={USER}, passwd={PWD}")
except (InterfaceError, DatabaseError) as erreur:
    # يتم عرض الخطأ
    print(f"L'erreur suivante s'est produite : {erreur}")
finally:
    # إغلاق الاتصال إذا كان مفتوحًا
    if connexion:
        connexion.close()

ملاحظات

  • السطر 2: يتم استيراد بعض الوظائف والفئات من الوحدة النمطية [mysql.connector]؛
  • السطران 6 و7: معرّفات المستخدم الذي سيقوم بالاتصال؛
  • السطر 8: الجهاز الذي يستضيف قاعدة البيانات. في الواقع، يتيح الموصل MySQL العمل مع قاعدة بيانات بعيدة؛
  • السطر 9: اسم قاعدة البيانات التي نريد الاتصال بها؛
  • السطور 11-26: سيقوم البرنامج النصي (في السطر 16) بتوصيل المستخدم [admpersonnes / nobody] بقاعدة البيانات [dbpersonnes]؛
  • الأسطر 20-26: قد يفشل الاتصال. ولذلك يتم إجراؤه ضمن بنية try / except / finally؛
  • السطر 16: تقبل الطريقة connect في الوحدة النمطية [mysq.connector] معلمات مختلفة مسماة:
    • user: المستخدم المالك للاتصال [admpersonnes
    • password: كلمة مرور المستخدم [nobody
    • host: جهاز تابع لـ SGBD و MySQL و [localhost]؛
    • database: قاعدة البيانات التي يتم الاتصال بها. اختياري.
  • السطر 20: في حالة حدوث استثناء، يكون من النوع [DatabaseError] أو [InterfaceError]؛
  • الأسطر 23-26: في الجملة [finally]، يتم إغلاق الاتصال؛

النتائج

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/databases/mysql/mysql_01.py
Connexion au SGBD MySQL en cours...
Connexion MySQL réussie à la base database=dbpersonnes, host=localhost sous l'identité user=admpersonnes, passwd=nobody

Process finished with exit code 0

16.4. النص البرمجي [mysql_02]: الاتصال بقاعدة بيانات MySQL - 2

في هذا البرنامج النصي الجديد، تم عزل الاتصال بقاعدة البيانات في دالة:


# استيراد الوحدة النمطية mysql.connector
from mysql.connector import DatabaseError, InterfaceError, connect


# ---------------------------------------------------------------------------------
def connexion(host: str, database: str, login: str, pwd: str):
    # يُجري الاتصال ثم يقطع الاتصال (اسم المستخدم، كلمة المرور) بقاعدة البيانات [database] على الخادم [host]
    # يطلق الاستثناء DatabaseError في حالة وجود مشكلة
    connexion = None
    try:
        # الاتصال
        connexion = connect(host=host, user=login, password=pwd, database=database)
        print(
            f"Connexion réussie à la base database={database}, host={host} sous l'identité user={login}, passwd={pwd}")
    finally:
        # يتم إغلاق الاتصال إذا كان مفتوحًا
        if connexion:
            connexion.close()
            print("Déconnexion réussie\n")


# ---------------------------------------------- main
# بيانات تسجيل الدخول
USER = "admpersonnes"
PASSWD = "nobody"
HOST = "localhost"
DATABASE = "dbpersonnes"

# تسجيل دخول مستخدم موجود
try:
    connexion(host=HOST, login=USER, pwd=PASSWD, database=DATABASE)
except (InterfaceError, DatabaseError) as erreur:
    # يتم عرض رسالة الخطأ
    print(erreur)

# تسجيل دخول مستخدم غير موجود
try:
    connexion(host=HOST, login="xx", pwd="xx", database=DATABASE)
except (InterfaceError, DatabaseError) as erreur:
    # يتم عرض الخطأ
    print(erreur)

ملاحظات:

  • الأسطر 6-19: دالة [connexion] التي تحاول توصيل مستخدم بقاعدة البيانات [dbpersonnes] ثم فصله عنها. تعرض النتيجة؛
  • الأسطر 29-41: البرنامج الرئيسي – يستدعي مرتين الأسلوب connexion ويعرض أي استثناءات محتملة؛

النتائج

1
2
3
4
5
6
7
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/databases/mysql/mysql_02.py
Connexion MySQL réussie à la base database=dbpersonnes, host=localhost sous l'identité user=admpersonnes, passwd=nobody
Déconnexion MySQL réussie

1045 (28000): Access denied for user 'xx'@'localhost' (using password: YES)

Process finished with exit code 0

16.5. نص برمجي [mysql_03]: إنشاء جدول MySQL

الآن بعد أن تعلمنا كيفية إنشاء اتصال باستخدام SGBD و MySQL، سنبدأ في إصدار أوامر SQL عبر هذا الاتصال. للقيام بذلك، سنقوم بالاتصال بقاعدة البيانات التي تم إنشاؤها [dbpersonnes] واستخدام هذا الاتصال لإنشاء جدول داخل قاعدة البيانات.


# عمليات الاستيراد
import sys

from mysql.connector import DatabaseError, InterfaceError, connect
from mysql.connector.connection import MySQLConnection


# ---------------------------------------------------------------------------------
def execute_sql(connexion: MySQLConnection, update: str):
    # تنفيذ استعلام تحديث على عملية الاتصال
    curseur = None
    try:
        # يتم طلب مؤشر
        curseur = connexion.cursor()
        # تنفيذ استعلام التحديث على الاتصال
        curseur.execute(update)
    finally:
        # إغلاق المؤشر إذا تم الحصول عليه
        if curseur:
            curseur.close()


# ---------------------------------------------- main
# بيانات اعتماد الاتصال
# هوية المستخدم
ID = "admpersonnes"
PWD = "nobody"
# الجهاز المضيف لنظام إدارة قواعد البيانات
HOST = "localhost"
# هوية قاعدة البيانات
DATABASE = "dbpersonnes"

# لنبدأ خطوة بخطوة
try:
    # الاتصال
    connexion = connect(host=HOST, user=ID, password=PWD, database=DATABASE)
    # الوضع AUTOCOMMIT
    connexion.autocommit = True
except (InterfaceError, DatabaseError) as erreur:
    # عرض الخطأ
    print(f"L'erreur suivante s'est produite : {erreur}")
    # الخروج
    sys.exit()

# حذف جدول الأشخاص إذا كان موجودًا
# إذا لم تكن موجودة، فسيحدث خطأ - يتم تجاهله
requête = "drop table personnes"
try:
    execute_sql(connexion, requête)
except (InterfaceError, DatabaseError):
    pass

# إنشاء جدول «الأشخاص»
requête = "create table personnes (id int PRIMARY KEY, prenom varchar(30) NOT NULL, nom varchar(30) NOT NULL, age integer NOT NULL, " \
          "unique(nom,prenom)) "
try:
    # تنفيذ الاستعلام
    execute_sql(connexion, requête)
    # العرض
    print(f"{requête} : requête réussie")
except (InterfaceError, DatabaseError) as erreur:
    # يتم عرض الخطأ
    print(f"L'erreur suivante s'est produite : {erreur}")
finally:
    # يتم قطع الاتصال
    connexion.close()

ملاحظات:

  • السطر 9: تقوم الدالة execute_sql بتنفيذ استعلام SQL على اتصال مفتوح؛
  • السطر 14: تُجرى عمليات SQL على الاتصال من خلال كائن خاص يُسمى المؤشر؛
  • السطر 14: الحصول على مؤشر؛
  • السطر 16: تنفيذ الاستعلام SQL؛
  • الأسطر 17-20: سواء حدث خطأ أم لا، يتم إغلاق المؤشر. وهذا يحرر الموارد المرتبطة به. إذا حدث استثناء، فإنه لا يتم معالجته هنا. بل سيتم رفعه إلى الكود المستدعي؛
  • الأسطر 33-43: إنشاء اتصال بقاعدة البيانات؛
  • السطر 38: يعني وضع AUTOCOMMIT=True للاتصال أن كل عملية تنفيذ لاستعلام ما تتم ضمن معاملة تلقائية. الوضع الافتراضي هو AUTOCOMMIT=False، حيث يقع على عاتق المطور مسؤولية إدارة المعاملات. المعاملة هي آلية تشمل تنفيذ عدة استعلامات من 1 إلى n. إما أن تنجح جميعها، أو لا تنجح أي منها. وبالتالي، إذا نجحت الاستعلامات من 1 إلى i لكن الاستعلام i+1 فشل، فسيتم «إلغاء» الاستعلامات من 1 إلى i حتى تعود قاعدة البيانات إلى الحالة التي كانت عليها قبل تنفيذ الاستعلام 1؛
  • هنا، يوجد طلبان SQL (السطران 49 و58). سيتم تنفيذ كل منهما في معاملة. ولا يؤثر فشل الطلب الثاني بأي شكل على الطلب الأول؛
  • الأسطر 45-51: يتم تنفيذ الأمر SQL [drop table personnes]. وهو يحذف الجدول المسمى [personnes]. وإذا لم تكن هذه الجدولة موجودة، فقد يتم الإبلاغ عن خطأ. ويتم تجاهل هذا الخطأ (السطر 51)؛
  • الأسطر 53-55: أمر إنشاء الجدول [personnes]. يمكن النظر إلى الجدول على أنه مجموعة من الأسطر والأعمدة. يحدد أمر الإنشاء أسماء الأعمدة:
    • [id]: معرّف صحيح. سيكون فريدًا لكل شخص. وسيكون هذا هو المفتاح الأساسي (PRIMARY KEY). وهذا يعني أنه في الجدول، لا تتكرر نفس القيمة مرتين في هذا العمود، ويمكن استخدامه لتعريف شخص ما؛
    • [nom]: سلسلة تتكون من 30 حرفًا كحد أقصى؛
    • [prenom]: سلسلة لا تتجاوز 30 حرفًا؛
    • [age]: عدد صحيح؛
    • السمة [NOT NULL] لكل عمود من هذه الأعمدة تعني أنه في أي صف من الجدول، لا يجوز أن يكون أي من الأعمدة الثلاثة فارغًا؛
    • يُسمى المعلمة [unique(nom,prenom)] «قيدًا». هنا، يتمثل القيد المفروض على الأسطر في أن التوبلة (الاسم، الاسم الأول) في السطر يجب أن تكون فريدة في الجدول. وهذا يعني أنه يمكن تحديد هوية فرد ما بشكل فريد في الجدول إذا كان اسمه واسمه الأول معروفين؛
  • السطور 56-60: تنفيذ الأمر SQL؛
  • الأسطر 61-63: معالجة أي استثناء محتمل؛
  • الأسطر 64-66: قطع الاتصال بقاعدة البيانات؛

النتائج

1
2
3
4
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/databases/mysql/mysql_03.py
create table personnes (id int PRIMARY KEY, prenom varchar(30) NOT NULL, nom varchar(30) NOT NULL, age integer NOT NULL, unique(nom,prenom))  : requête réussie

Process finished with exit code 0

التحقق باستخدام [phpMyAdmin]:

Image

  • تحتوي قاعدة البيانات [dbpersonnes] [1] على جدول [personnes] [2] الذي له البنية [3-4]، والمفتاح الأساسي [5] وقيد التفرد [6]؛

16.6. النص البرمجي [mysql_04]: تنفيذ ملف الأوامر SQL

بعد إنشاء الجدول [personnes] سابقًا، نقوم الآن بملئه ثم استخدامه باستخدام الأوامر SQL.

نرغب في تنفيذ الأوامر SQL من ملف نصي:

Image

محتوى الملف [commandes.sql] هو كما يلي:


# حذف الجدول [personnes]
drop table personnes
# إنشاء جدول الأشخاص
create table personnes (prenom varchar(30) not null, nom varchar(30) not null, age integer not null, primary key (nom,prenom))
# إدراج شخصين
insert into personnes(prenom, nom, age) values('Paul','Langevin',48)
insert into personnes(prenom, nom, age) values ('Sylvie','Lefur',70)
# عرض الجدول
select prenom, nom, age from personnes
# خطأ متعمد
xx
# إدراج ثلاثة أشخاص
insert into personnes(prenom, nom, age) values ('Pierre','Nicazou',35)
insert into personnes(prenom, nom, age) values ('Geraldine','Colou',26)
insert into personnes(prenom, nom, age) values ('Paulette','Girond',56)
# عرض الجدول
select prenom, nom, age from personnes
# قائمة الأشخاص مرتبة أبجديًا حسب الأسماء، وفي حالة تساوي الأسماء مرتبة أبجديًا حسب الأسماء الأولى
select nom,prenom from personnes order by nom asc, prenom desc
# قائمة بالأشخاص الذين تتراوح أعمارهم بين [20,40] مرتبة حسب العمر من الأكبر إلى الأصغر
# ثم حسب الترتيب الأبجدي للأسماء في حالة تساوي الأعمار، وحسب الترتيب الأبجدي للأسماء الأولى في حالة تساوي الأسماء
select nom,prenom,age from personnes where age between 20 and 40 order by age desc, nom asc, prenom asc
# إضافة السيدة برونو
insert into personnes(prenom, nom, age) values('Josette','Bruneau',46)
# تحديث عمرها
update personnes set age=47 where nom='Bruneau'
# قائمة بالأشخاص الذين يحملون اسم «برونو»
select nom,prenom,age from personnes where nom='Bruneau'
# حذف السيدة برونو
delete from personnes where nom='Bruneau'
# قائمة الأشخاص الذين يحملون اسم «برونو»
select nom,prenom,age from personnes where nom='Bruneau'

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

Image

النص البرمجي [mysql_module] هو كما يلي:


# عمليات الاستيراد
from mysql.connector import DatabaseError, InterfaceError
from mysql.connector.connection import MySQLConnection
from mysql.connector.cursor import MySQLCursor


# ---------------------------------------------------------------------------------
def afficher_infos(curseur: MySQLCursor):
     # يعرض نتيجة أمر SQL
    


# ---------------------------------------------------------------------------------
def execute_list_of_commands(connexion: MySQLConnection, sql_commands: list,
                             suivi: bool = False, arrêt: bool = True, with_transaction: bool = True):
    # يستخدم الاتصال المفتوح [connexion]
    # يُنفذ عبر هذا الاتصال الأوامر SQL الواردة في القائمة [sql_commands]
    # هذا الملف هو ملف أوامر SQL يتم تنفيذها بمعدل أمر واحد في كل سطر
    # إذا كانت قيمة «المتابعة» = «صحيح»، فإن كل عملية تنفيذ لأمر SQL تُعرض مع إشارة إلى نجاحها أو فشلها
    # إذا كانت «إيقاف» = «صحيح»، تتوقف الوظيفة عند أول خطأ تصادفه، وإلا فإنها تنفذ جميع أوامر SQL
    # إذا كانت قيمة with_transaction = True، فإن أي خطأ يلغي جميع الأوامر SQL التي تم تنفيذها مسبقًا
    # إذا كانت قيمة with_transaction تساوي False، فإن أي خطأ لا يؤثر على الأوامر SQL التي تم تنفيذها مسبقًا
    # تُرجع الدالة قائمة [erreur1, erreur2, ...]

    ….


# ---------------------------------------------------------------------------------
def execute_file_of_commands(connexion: MySQLConnection, sql_filename: str,
                             suivi: bool = False, arrêt: bool = True, with_transaction: bool = True):
    # تستخدم الاتصال المفتوح [connexion]
    # تنفذ عبر هذا الاتصال الأوامر SQL الموجودة في الملف النصي sql_filename
    # هذا الملف هو ملف أوامر SQL يتم تنفيذها بمعدل أمر واحد في كل سطر
    # إذا كانت القيمة «متابعة» = «صحيح»، فإن كل عملية تنفيذ لأمر SQL تُعرض مع إشارة إلى نجاحها أو فشلها
    # إذا كانت «إيقاف» = «صحيح»، تتوقف الوظيفة عند أول خطأ تصادفه، وإلا فإنها تنفذ جميع أوامر SQL
    # إذا كان with_transaction=True، فإن أي خطأ يلغي جميع الأوامر SQL التي تم تنفيذها مسبقًا
    # إذا كانت قيمة with_transaction تساوي False، فإن أي خطأ لا يؤثر على الأوامر SQL التي تم تنفيذها مسبقًا
    # تُرجع الدالة قائمة [erreur1, erreur2, ...]

    # معالجة الملف SQL
    try:
        # فتح الملف للقراءة
        file = open(sql_filename, "r")
        # المعالجة
        return execute_list_of_commands(connexion, file.readlines(), suivi, arrêt, with_transaction)
    except BaseException as erreur:
        # يتم إرجاع مصفوفة أخطاء
        return [f"Le fichier {sql_filename} n'a pu être être exploité : {erreur}"]

ملاحظات:

  • السطر 29: تقوم الدالة [execute_file_of_commands] بتنفيذ الأوامر SQL الموجودة في الملف النصي المسمى [sql_filename]:
  • يرجى الاطلاع على التعليقات في الأسطر 31-38 لمعرفة معنى المعلمات؛
  • الأسطر 40-48: يتم استخدام الملف النصي [sql_filename]؛
  • السطر 43: فتح الملف؛
  • السطر 34: تنفيذ الدالة [execute_list_of_commands] التي تقوم بتنفيذ الأوامر SQL التي يتم تمريرها إليها في قائمة. تتكون هذه القائمة هنا من قائمة بجميع أسطر الملف النصي [file.readlines()] (السطر 45)؛

الدالة [execute_list_of_commands] هي كما يلي:


# ---------------------------------------------------------------------------------
def execute_list_of_commands(connexion: MySQLConnection, sql_commands: list,
                             suivi: bool = False, arrêt: bool = True, with_transaction: bool = True):
    # استخدام الاتصال المفتوح [connexion]
    # تنفيذ الأوامر SQL الواردة في القائمة [sql_commands] عبر هذا الاتصال
    # هذا الملف هو ملف أوامر SQL يتم تنفيذها بمعدل أمر واحد في كل سطر
    # إذا كانت قيمة «المتابعة» = «صحيح»، فإن كل عملية تنفيذ لأمر SQL تُعرض مع إشارة إلى نجاحها أو فشلها
    # إذا كانت «إيقاف» = «صحيح»، تتوقف الوظيفة عند أول خطأ تصادفه، وإلا فإنها تنفذ جميع أوامر SQL
    # إذا كان with_transaction=True، فإن أي خطأ يلغي جميع الأوامر SQL التي تم تنفيذها مسبقًا
    # إذا كانت قيمة with_transaction تساوي False، فإن أي خطأ لا يؤثر على الأوامر SQL التي تم تنفيذها مسبقًا
    # تُرجع الدالة قائمة [erreur1, erreur2, ...]

    # عمليات التهيئة
    curseur = None
    connexion.autocommit = not with_transaction
    erreurs = []
    try:
        # مطلوب شريط تمرير
        curseur = connexion.cursor()
        # تنفيذ sql_commands SQL الموجودة في sql_commands
        # يتم تنفيذها واحدة تلو الأخرى
        for command in sql_commands:
            # يتم حذف المسافات في بداية ونهاية الأمر الحالي
            command = command.strip()
            # هل لدينا أمر فارغ أم تعليق؟ إذا كان الجواب نعم، ننتقل إلى الأمر التالي
            if command == '' or command[0] == "#":
                continue
            # تنفيذ الأمر الحالي
            error = None
            try:
                curseur.execute(command)
            except (InterfaceError, DatabaseError) as erreur:
                error = erreur
            # هل حدث خطأ؟
            if error:
                # خطأ آخر
                msg = f"{command} : Erreur ({error})"
                erreurs.append(msg)
                # هل يتم عرض الشاشة أم لا؟
                if suivi:
                    print(msg)
                # هل نتوقف؟
                if with_transaction or arrêt:
                    # نعرض قائمة الأخطاء
                    return erreurs
            else:
                # لا توجد أخطاء
                if suivi:
                    print(f"[{command}] : Exécution réussie")
                # عرض نتيجة الأمر
                afficher_infos(curseur)
        # عرض جدول الأخطاء
        return erreurs
    finally:
        # إغلاق المؤشر
        if curseur:
            curseur.close()
        # تأكيد/إلغاء المعاملة إن وجدت
        if with_transaction:
            if erreurs:
                # الإلغاء
                connexion.rollback()
            else:
                # التصديق
                connexion.commit()

ملاحظات

  • السطر 2: تقوم الدالة [execute_list_of_commands] بتنفيذ الأوامر SQL الموجودة في القائمة [sql_commands]:
  • يرجى الاطلاع على التعليقات الواردة في الأسطر من 4 إلى 11 لمعرفة معنى المعلمات؛
  • السطر 2: الاتصال المستلم هو اتصال مفتوح بقاعدة بيانات؛
  • السطر 15: إذا أردنا أن يتم تنفيذ جميع الأوامر الموجودة في القائمة [sql_commands] ضمن معاملة، فيجب العمل في الوضع AUTOCOMMIT=False. وإلا، فسيتم العمل في الوضع AUTOCOMMIT=True، وعندها سيتم تنفيذ كل أمر من الأوامر الموجودة في القائمة [sqlCommands] ضمن معاملة تلقائية ولن تكون هناك معاملة شاملة؛
  • السطر 19: نطلب مؤشرًا لتنفيذ الأوامر المختلفة SQL؛
  • الأسطر 22-51: يتم تنفيذ الأوامر واحدة تلو الأخرى؛
  • السطران 26-27: يتم قبول الأسطر الفارغة وكذلك التعليقات في قائمة الأوامر SQL. في هذه الحالة، يتم تجاهل الأمر ببساطة؛
  • الأسطر 30-33: تنفيذ الاستعلام الحالي؛
  • الأسطر 35-45: معالجة حالة حدوث خطأ محتمل في تنفيذ الاستعلام الحالي؛
  • الأسطر 37-38: تتم إضافة الخطأ إلى جدول الأخطاء؛
  • الأسطر 40-41: إذا تم طلب التتبع، يتم عرض رسالة الخطأ؛
  • الأسطر 43-45: إذا طلبت الشفرة المستدعية التوقف بعد أول خطأ أو إذا طلبت استخدام معاملة، فيجب التوقف. يتم إرجاع مصفوفة الأخطاء؛
  • الأسطر 46-51: الحالة التي لم تحدث فيها أخطاء في تنفيذ الاستعلام الحالي؛
  • الأسطر 48-49: إذا تم طلب المتابعة، يتم عرض الاستعلام الذي تم تنفيذه مع الإشارة إلى أنه «ناجح»؛
  • الأسطر 50-51: يتم عرض نتيجة الاستعلام الذي تم تنفيذه. سنعود إلى الدالة [afficher_infos] لاحقًا؛
  • الأسطر 54-65: يتم تنفيذ الجملة [finally] في جميع الحالات، سواء حدث استثناء أم لا؛
  • السطران 56-57: إغلاق المؤشر. يؤدي ذلك إلى تحرير الموارد المخصصة له؛
  • الأسطر 59-65: يتم معالجة الحالة التي طلب فيها الكود المستدعي تنفيذ الأوامر SQL ضمن معاملة؛
  • السطر 60: يتم التحقق مما إذا كانت القائمة [erreurs] فارغة، مما يعني أنه لم تحدث أي استثناءات. في هذه الحالة، يتم تأكيد المعاملة (السطر 65)، وإلا يتم إلغاؤها (السطر 62)؛

تعرض الدالة [afficher_infos] نتيجة استعلام:


# ---------------------------------------------------------------------------------
def afficher_infos(curseur: MySQLCursor):
    print(type(curseur))
    # عرض نتيجة أمر SQL
    # هل كانت هذه عبارة عن أمر SELECT؟
    if curseur.description:
        # المؤشر له وصف - لذا فقد نفذ أمر SELECT
        # description[i] هو وصف العمود رقم i في أمر select
        # descriptionQZXW2HTMLBW2ldZQXQZXW2HTMLBWzBdZQX هو اسم العمود رقم i في أمر select
        # يتم عرض أسماء الحقول
        titre = ""
        for i in range(len(curseur.description)):
            titre += curseur.description[i][0] + ", "
        # يتم عرض قائمة الحقول بدون الفاصلة الختامية
        print(titre[0:len(titre) - 1])
        # خط فاصل
        print("*" * (len(titre) - 1))
        # السطر الحالي في قائمة الاختيار
        ligne = curseur.fetchone()
        while ligne:
            # يتم عرضها
            print(ligne)
            # السطر التالي في قائمة الاختيار
            ligne = curseur.fetchone()
        # سطر فاصل
        print("*" * (len(titre) - 1))
    else:
        # المؤشر لا يحتوي على حقل [description] - لذا فقد نفذ أمرًا SQL
        # أمر تحديث (إدراج، حذف، تحديث)
        print(f"nombre de lignes modifiées : {curseur.rowcount}")

ملاحظات

  • السطر 1: معلمة الدالة هي المؤشر الذي نفذ للتو أمر SQL. ووفقًا لما إذا كان هذا الأمر هو SELECT أو أمر تحديث INSERT أو UPDATE أو DELETE، فإن محتوى المؤشر يختلف؛
  • السطر 6: إذا كان المؤشر يحتوي على الحقل [description]، فهذا يعني أنه نفذ أمر SELECT، ويصف [description] الحقول المطلوبة في SELECT:
    • description[i] يصف الحقل رقم i المطلوب بواسطة SELECT. وهي قائمة؛
    • description[i][0] هو اسم الحقل رقم i؛
  • الأسطر 11-17: يتم عرض أسماء الحقول المطلوبة بواسطة SELECT؛
  • الأسطر 18-24: يتم استغلال نتيجة SELECT؛
  • السطور 20 و24: يتم استغلال نتيجة SELECT بشكل متسلسل. هذه النتيجة عبارة عن مجموعة من السطور. يتم الحصول على السطر الحالي بواسطة [curseur.fetchone()] (السطر 19). وبذلك نحصل على مجموعة؛
  • السطور 27-30: إذا لم يكن للمؤشر الحقل [description]، فهذا يعني أنه نفذ أوامر التحديث INSERT، UPDATE، DELETE. وبذلك يمكن معرفة عدد الأسطر التي تم تعديلها في الجدول نتيجة تنفيذ هذا الأمر؛
  • السطر 30: [curseur.rowcount] هو هذا الرقم؛

يستخدم البرنامج النصي الرئيسي [mysql-04] الوحدة النمطية [mysql_module] التي وصفناها للتو:

Image

يُهيئ الملف [config_04] سياق تنفيذ البرنامج النصي [mysql_04]:


def configure():
    import os

    # المسار المطلق لمجلد ملف التكوين
    script_dir = os.path.dirname(os.path.abspath(__file__))
    # تكوين مجلدات syspath
    absolute_dependencies = [
        # المجلدات المحلية
        f"{script_dir}/shared",
    ]

    # تثبيت مسار syspath
    from myutils import set_syspath
    set_syspath(absolute_dependencies)

    # تطبيق التكوين
    return {
        # ملف الأوامر SQL
        "commands_filename": f"{script_dir}/data/commandes.sql",
        # بيانات اعتماد الاتصال بقاعدة البيانات
        "host": "localhost",
        "database": "dbpersonnes",
        "user": "admpersonnes",
        "password": "nobody"
    }

النص البرمجي [mysql_04] هو التالي:


# استرداد تكوين التطبيق
import config_04

config = config_04.configure()

# تم تكوين syspath - يمكن الآن إجراء عمليات الاستيراد
import sys
from mysql_module import execute_file_of_commands
from mysql.connector import connect, DatabaseError, InterfaceError

# ---------------------------------------------- main
# التحقق من صحة صيغة الاستدعاء
# argv[0] صحيح / خطأ
args = sys.argv
erreur = len(args) != 2
if not erreur:
    with_transaction = args[1].lower()
    erreur = with_transaction != "true" and with_transaction != "false"
# خطأ؟
if erreur:
    print(f"syntaxe : {args[0]} true / false")
    sys.exit()

# حساب نص
with_transaction = with_transaction == "true"
if with_transaction:
    texte = "avec transaction"
else:
    texte = "sans transaction"

# سجلات الشاشة
print("--------------------------------------------------------------------")
print(f"Exécution du fichier SQL {config['commands_filename']} {texte}")
print("--------------------------------------------------------------------")

# تنفيذ الأوامر SQL من الملف
connexion = None
try:
    # الاتصال بقاعدة البيانات
    connexion = connect(host=config['host'], user=config['user'], password=config['password'],
                        database=config['database'])
    # تنفيذ ملف الأوامر SQL
    erreurs = execute_file_of_commands(connexion, config["commands_filename"], suivi=True, arrêt=False,
                                       with_transaction=with_transaction)
except (InterfaceError, DatabaseError) as erreur:
     # عرض الخطأ
    print(f"L'erreur fatale suivante s'est produite : {erreur}")
    # التوقف
    sys.exit()
finally:
    # إغلاق الاتصال إذا كان مفتوحًا
    if connexion:
        connexion.close()

# عرض عدد الأخطاء
print("--------------------------------------------------------------------")
print(f"Exécution terminée")
print("--------------------------------------------------------------------")
print(f"Il y a eu {len(erreurs)} erreur(s)")
# عرض الأخطاء
for erreur in erreurs:
    print(erreur)

ملاحظات

  • الأسطر 1-4: تكوين البرنامج النصي؛
  • السطر 8: استيراد الوحدة النمطية [mysql_module] الموضحة سابقًا:
  • الأسطر 12-22: ينتظر البرنامج النصي [mysql-04] معلمة يجب أن تكون إحدى قيم [true / false]. يحدد هذا المعامل ما إذا كان يجب تنفيذ ملف الأوامر SQL ضمن معاملة (true) أم لا (false
  • السطر 14: المعلمات التي يمررها المستخدم إلى البرنامج النصي موجودة في القائمة [sys.argv]؛
  • السطر 15: يلزم وجود معلمتين، على سبيل المثال [mysql-04 true]. يُعتبر اسم البرنامج النصي معلمة؛
  • السطران 17-18: إذا كان هناك معلمتان بالفعل، فيجب أن تكون المعلمة الثانية سلسلة أحرف بقيمة 'true' أو 'false
  • الأسطر 24-29: حساب النص المعروض في السطر 33؛
  • الأسطر 39-44: يتم تنفيذ الأوامر الموجودة في الملف [./data/commandes.sql]؛
  • الأسطر 45-49: في حالة حدوث خطأ في الاتصال (السطر 40) أو خطأ لا يديره البرنامج النصي [execute_file_of_commands]، يتم عرض الخطأ وإيقاف العملية بالكامل؛
  • الأسطر 55-62: في حالة نجاح التنفيذ، يتم عرض عدد الأخطاء التي واجهت تنفيذ الأوامر SQL؛

التنفيذ رقم 1

نقوم أولاً بالتنفيذ بدون معاملة. ولذلك، سنقوم بإنشاء تكوين للتنفيذ كما تم في الفقرة |تكوين سياق التنفيذ|:

Image

  • في [1-4]، نقوم بإنشاء تكوين تنفيذ بلغة Python؛

Image

  • [5]: اسم تكوين التنفيذ؛
  • [6]: مسار البرنامج النصي المراد تنفيذه؛
  • [7]: معلمات البرنامج النصي؛
  • [8]: مجلد التنفيذ؛

وبالتالي، فإن هذا التكوين يتوافق مع تنفيذ الملف SQL مع معاملة. استخدم الزر [Apply] لتأكيد التكوين.

ونقوم بنفس الطريقة بإنشاء إعدادات التشغيل [mysql mysql-04 without_transaction]:

Image

وبالتالي، فإن هذا التكوين يمثل تشغيل الملف SQL بدون معاملة. استخدم الزر [Apply] لتأكيد التكوين.

نقوم أولاً بتنفيذ الإصدار بدون معاملة:

Image

وتكون النتائج كما يلي:


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/databases/mysql/mysql_04.py false
--------------------------------------------------------------------
Exécution du fichier SQL C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\databases\mysql/data/commandes.sql sans transaction
--------------------------------------------------------------------
[drop table personnes] : Exécution réussie
nombre de lignes modifiées : 0
[create table personnes (id int primary key, prenom varchar(30) not null, nom varchar(30) not null, age integer not null, unique (nom,prenom))] : Exécution réussie
nombre de lignes modifiées : 0
[insert into personnes(id, prenom, nom, age) values(1, 'Paul','Langevin',48)] : Exécution réussie
nombre de lignes modifiées : 1
[insert into personnes(id, prenom, nom, age) values (2, 'Sylvie','Lefur',70)] : Exécution réussie
nombre de lignes modifiées : 1
[select prenom, nom, age from personnes] : Exécution réussie
prenom, nom, age,
*****************
('Paul', 'Langevin', 48)
('Sylvie', 'Lefur', 70)
*****************
xx : Erreur (1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'xx' at line 1)
[insert into personnes(id, prenom, nom, age) values (3, 'Pierre','Nicazou',35)] : Exécution réussie
nombre de lignes modifiées : 1
[insert into personnes(id, prenom, nom, age) values (4, 'Geraldine','Colou',26)] : Exécution réussie
nombre de lignes modifiées : 1
[insert into personnes(id, prenom, nom, age) values (5, 'Paulette','Girond',56)] : Exécution réussie
nombre de lignes modifiées : 1
[select prenom, nom, age from personnes] : Exécution réussie
prenom, nom, age,
*****************
('Paul', 'Langevin', 48)
('Sylvie', 'Lefur', 70)
('Pierre', 'Nicazou', 35)
('Geraldine', 'Colou', 26)
('Paulette', 'Girond', 56)
*****************
[select nom,prenom from personnes order by nom asc, prenom desc] : Exécution réussie
nom, prenom,
************
('Colou', 'Geraldine')
('Girond', 'Paulette')
('Langevin', 'Paul')
('Lefur', 'Sylvie')
('Nicazou', 'Pierre')
************
[select nom,prenom,age from personnes where age between 20 and 40 order by age desc, nom asc, prenom asc] : Exécution réussie
nom, prenom, age,
*****************
('Nicazou', 'Pierre', 35)
('Colou', 'Geraldine', 26)
*****************
[insert into personnes(id, prenom, nom, age) values(6, 'Josette','Bruneau',46)] : Exécution réussie
nombre de lignes modifiées : 1
[update personnes set age=47 where nom='Bruneau'] : Exécution réussie
nombre de lignes modifiées : 1
[select nom,prenom,age from personnes where nom='Bruneau'] : Exécution réussie
nom, prenom, age,
*****************
('Bruneau', 'Josette', 47)
*****************
[delete from personnes where nom='Bruneau'] : Exécution réussie
nombre de lignes modifiées : 1
[select nom,prenom,age from personnes where nom='Bruneau'] : Exécution réussie
nom, prenom, age,
*****************
*****************
--------------------------------------------------------------------
Exécution terminée
--------------------------------------------------------------------
Il y a eu 1 erreur(s)
xx : Erreur (1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'xx' at line 1)

Process finished with exit code 0

ملاحظات:

  • السطر 19: نلاحظ أنه بعد حدوث الخطأ، استمر تنفيذ الأوامر SQL، وذلك لأن التنفيذ تم بدون معاملة وباستخدام المعلمة [arrêt=False]. وبالتالي، تم تنفيذ جميع الأوامر SQL. لذا، من المفترض أن يكون لدينا جدول [personnes] يعكس هذا التنفيذ؛

التحقق باستخدام phpMyAdmin:

Image

التنفيذ رقم 2

نقوم الآن بتنفيذ التكوين [mysql mysql-04 with_transaction]. وكانت النتائج كما يلي:


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/databases/mysql/mysql_04.py true
--------------------------------------------------------------------
Exécution du fichier SQL C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\databases\mysql/data/commandes.sql avec transaction
--------------------------------------------------------------------
[drop table personnes] : Exécution réussie
nombre de lignes modifiées : 0
[create table personnes (id int primary key, prenom varchar(30) not null, nom varchar(30) not null, age integer not null, unique (nom,prenom))] : Exécution réussie
nombre de lignes modifiées : 0
[insert into personnes(id, prenom, nom, age) values(1, 'Paul','Langevin',48)] : Exécution réussie
nombre de lignes modifiées : 1
[insert into personnes(id, prenom, nom, age) values (2, 'Sylvie','Lefur',70)] : Exécution réussie
nombre de lignes modifiées : 1
[select prenom, nom, age from personnes] : Exécution réussie
prenom, nom, age,
*****************
('Paul', 'Langevin', 48)
('Sylvie', 'Lefur', 70)
*****************
xx : Erreur (1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'xx' at line 1)
--------------------------------------------------------------------
Exécution terminée
--------------------------------------------------------------------
Il y a eu 1 erreur(s)
xx : Erreur (1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'xx' at line 1)

Process finished with exit code 0

ملاحظات:

  • السطر 19: نلاحظ أنه بعد حدوث الخطأ لم يعد هناك تنفيذ لأوامر SQL، وذلك لأن التنفيذ تم ضمن معاملة، وعند أول خطأ واجهناه قمنا بإلغاء المعاملة وإيقاف تنفيذ أوامر SQL. وهذا يعني أن نتائج الأوامر الواردة في الأسطر 9 و11 و13 قد تم التراجع عنها. لذا، من المفترض أن تكون الجدولة [personnes] فارغة؛

التحقق باستخدام phpMyAdmin:

Image

  • في [5]، نلاحظ أن الجدول [personnes] و[2] فارغان؛

16.7. البرنامج النصي [mysql_05]: استخدام الاستعلامات المعلمة

يقدم البرنامج النصي [mysql_05] مفهوم الاستعلامات المعلمة:


# عمليات الاستيراد
from mysql.connector import connect, DatabaseError, InterfaceError

# هوية المستخدم
ID = "admpersonnes"
PWD = "nobody"
# الجهاز المضيف لنظام إدارة قواعد البيانات
HOST = "localhost"
# هوية قاعدة البيانات
BASE = "dbpersonnes"

# قائمة الأشخاص (الاسم، الاسم الأول، العمر)
personnes = []
for i in range(5):
    personnes.append((i, f"n0{i}", f"p0{i}", i + 10))
personnes.append((40, "d'Aboot", "Y'éna", 18))
# قائمة أخرى بالأشخاص
autresPersonnes = []
for i in range(5):
    autresPersonnes.append((i + 100, f"n1{i}", f"p1{i}", i + 20))
autresPersonnes.append((200, "d'Aboot", "F'ilhem", 34))

# الوصول إلى SGBD
connexion = None
try:
    # تسجيل الدخول
    connexion = connect(host=HOST, user=ID, password=PWD, database=BASE)
    # مؤشر
    curseur = connexion.cursor()
    # حذف السجلات الموجودة
    curseur.execute("delete from personnes")
    # الإدراج شخصًا تلو الآخر باستخدام استعلام مُعد مسبقًا
    for personne in personnes:
        curseur.execute("insert into personnes(id,nom,prenom,age) values(%s,%s,%s,%s)", personne)
    # إدراج قائمة بالأشخاص دفعة واحدة
    curseur.executemany("insert into personnes(id,nom,prenom,age) values(%s, %s,%s,%s)", autresPersonnes)
    # التحقق من صحة المعاملة
    connexion.commit()
except (DatabaseError, InterfaceError) as erreur:
    # عرض الخطأ
    print(f"L'erreur suivante s'est produite : {erreur}")
    # إلغاء المعاملة
    if connexion:
        connexion.rollback()
finally:
    # إنهاء الاتصال
    if connexion:
        connexion.close()

ملاحظات

  • الأسطر 12-21: يتم إنشاء قائمتين بالأشخاص المراد إدراجهم في قاعدة البيانات [dbpersonnes]؛
  • السطر 27: الاتصال بقاعدة البيانات؛
  • السطر 31: حذف محتوى الجدول [personnes]؛
  • الأسطر 33-34: إدراج الأشخاص باستخدام استعلام معلمَّ؛ السطر 34، المعلمة الأولى هي الأمر SQL المطلوب تنفيذه. وهذا الأمر غير مكتمل. فهو يحتوي على معلمات [%s] التي سيتم استبدالها واحدة تلو الأخرى وبالترتيب بقيم قائمة المعلمة الثانية؛
  • السطر 36: إدراج أشخاص باستخدام تعليمة واحدة فقط هذه المرة وهي [curseur.executemany]. المعلمة الثانية لـ [executemany] هي إذن قائمة من القوائم؛

تكمن فائدة الاستعلامات المعلمة في نقطتين:

  • يتم تنفيذها بشكل أسرع من الاستعلامات «الثابتة» التي يتعين تحليلها عند كل عملية تنفيذ. لا يتم تحليل الاستعلام المعلم [executemany] إلا مرة واحدة. بعد ذلك يتم تنفيذه n مرات دون الحاجة إلى تحليله مرة أخرى؛
  • يتم التحقق من المعلمات التي يتم إدخالها في الاستعلام المعلم. إذا كانت تحتوي على أحرف محجوزة، مثل الفاصلة العليا على سبيل المثال، يتم «حمايتها» حتى لا تتداخل مع تنفيذ الأمر SQL. وللتحقق من هذه النقطة، تم تضمين أسماء وألقاب تحتوي على علامات الفاصلة العليا في القائمة (السطران 16 و21)؛

والنتائج التي تم الحصول عليها في phpMyAdmin هي كما يلي:

Image

  • تجدر الإشارة إلى أن السلاسل التي تحتوي على علامة الفاصلة العليا، وهي حرف محجوز في SQL، قد أُدرجت بشكل صحيح. فقد «حمتها» الاستعلامات المُعلمة. وبدون الاستعلامات المُعلمة، كان سيتعين علينا القيام بهذا العمل بأنفسنا؛