بيان مُعدّ

في أنظمة إدارة قواعد البيانات (DBMS)، تُعدّ العبارة المُجهّزة ، أو العبارة المُعاملة (لا ينبغي الخلط بينها وبين الاستعلام المُعامل )، ميزةً تقوم فيها قاعدة البيانات بتجميع كود SQL مسبقًا وتخزين النتائج، وفصلها عن البيانات. من فوائد العبارات المُجهّزة: [ 1 ]

  • الكفاءة، لأنها قابلة للاستخدام بشكل متكرر دون الحاجة إلى إعادة تجميعها.
  • الأمان، من خلال تقليل أو القضاء على هجمات حقن SQL

تأخذ العبارة المُعدة مسبقًا شكل قالب مُجمّع مسبقًا يتم فيه استبدال القيم الثابتة أثناء كل عملية تنفيذ، وتستخدم عادةً عبارات SQL DML مثل INSERT أو SELECT أو UPDATE .

تتمثل آلية العمل الشائعة لإعداد البيانات المالية فيما يلي:

  1. التحضير : يقوم التطبيق بإنشاء نموذج البيان وإرساله إلى نظام إدارة قواعد البيانات. تُترك بعض القيم غير محددة، وتُسمى معلمات أو عناصر نائبة أو متغيرات ربط (مُشار إليها بعلامة استفهام "؟" أدناه):
    INSERTINTOproducts(name,price)VALUES(?,?);
  2. التجميع : يقوم نظام إدارة قواعد البيانات بتجميع (تحليل وتحسين وترجمة) قالب العبارة، ويخزن النتيجة دون تنفيذها.
  3. التنفيذ : يُزوّد ​​التطبيق (أو يربط ) قيمًا لمعاملات قالب العبارة، وينفذ نظام إدارة قواعد البيانات العبارة (وقد يُعيد نتيجة). يمكن للتطبيق أن يطلب من نظام إدارة قواعد البيانات تنفيذ العبارة عدة مرات بقيم مختلفة. في المثال أعلاه، قد يُزوّد ​​التطبيق المعامل الأول بالقيمة "bike" والمعامل الثاني بالقيمة "10900"، ثم لاحقًا بالقيمتين "shoes" و"7400".

البديل عن استخدام عبارة مُجهزة هو استدعاء لغة SQL مباشرةً من شفرة المصدر للتطبيق بطريقة تجمع بين الشفرة والبيانات. المكافئ المباشر للمثال أعلاه هو:

INSERT INTO products ( name , price ) VALUES ( 'bike' , '10900' );

لا يمكن إجراء جميع عمليات التحسين في وقت تجميع قالب العبارة، وذلك لسببين: قد تعتمد أفضل خطة على القيم المحددة للمعلمات، وقد تتغير أفضل خطة مع تغير الجداول والفهارس بمرور الوقت. [ 2 ]

من جهة أخرى، إذا نُفِّذ استعلام مرة واحدة فقط، فقد تكون العبارات المُجهَّزة من جانب الخادم أبطأ بسبب الرحلة الإضافية إلى الخادم. [ 3 ] قد تؤدي قيود التنفيذ أيضًا إلى انخفاض الأداء؛ على سبيل المثال، لم تكن بعض إصدارات MySQL تُخزِّن نتائج الاستعلامات المُجهَّزة مؤقتًا. [ 4 ] يتمتع الإجراء المُخزَّن ، الذي يُجمَّع مسبقًا ويُخزَّن على الخادم لتنفيذه لاحقًا، بمزايا مماثلة. على عكس الإجراء المُخزَّن، لا تُكتب العبارة المُجهَّزة عادةً بلغة إجرائية، ولا يمكنها استخدام المتغيرات أو تعديلها أو استخدام هياكل التحكم في التدفق ، بل تعتمد بدلًا من ذلك على لغة استعلام قاعدة البيانات التصريحية . نظرًا لبساطتها ومحاكاتها من جانب العميل، تُعد العبارات المُجهَّزة أكثر قابلية للنقل بين مختلف الموردين.

دعم البرامج

تدعم أنظمة إدارة قواعد البيانات الرئيسية ، بما في ذلك SQLite [ 5 ] و MySQL [ 6 ] وOracle [ 7 ] و IBM Db2 [ 8 ] و Microsoft SQL Server [ 9 ] و PostgreSQL [ 10 ] ، العبارات المُجهزة. تُنفذ هذه العبارات عادةً عبر بروتوكول ثنائي غير SQL لتحسين الكفاءة والحماية من هجمات حقن SQL، ولكن في بعض أنظمة إدارة قواعد البيانات، مثل MySQL، تتوفر العبارات المُجهزة أيضًا باستخدام صيغة SQL لأغراض تصحيح الأخطاء. [ 11 ]

تدعم العديد من لغات البرمجة العبارات المُجهزة في مكتباتها القياسية، وتُحاكيها على جانب العميل حتى لو لم يدعمها نظام إدارة قواعد البيانات الأساسي، بما في ذلك JDBC في Java [ 12 ] ، و DBI في Perl [ 13 ] ، و PDO في PHP [ 1 و DB-API في Python [ 14 ] . قد تكون محاكاة جانب العميل أسرع للاستعلامات التي تُنفذ مرة واحدة فقط، وذلك بتقليل عدد مرات الاتصال بالخادم، ولكنها عادةً ما تكون أبطأ للاستعلامات التي تُنفذ عدة مرات. كما أنها تُقاوم هجمات حقن SQL بكفاءة عالية.

يمكن القضاء على العديد من أنواع هجمات حقن SQL عن طريق تعطيل القيم الحرفية ، مما يستلزم فعليًا استخدام العبارات المُجهزة؛ اعتبارًا من عام 2007يدعم H2 فقط هذه الميزة. [ 15 ]

أمثلة

لغة سي++

في C++ MySQL Connector/C++ X DevAPI [ 16 ] [ 17 ] ، يتم الإعداد ضمنيًا باستخدام واجهة برمجة التطبيقات الخاصة بالمكتبة.

استيراد < mysqlx / xdevapi.h > استيراد std ؛باستخدام mysqlx :: Schema ؛ باستخدام mysqlx :: Session ؛ باستخدام mysqlx :: Table ؛int main () { try { Session session ( "localhost" , 33060 , "user" , "password" ); Schema db = session . getSchema ( "testdb" ); Table users = db . getTable ( "users" );users.insert ( " name" , "age" ) . values ( "Alice" , 30 ) .execute (); } catch ( const mysqlx :: Error & e ) { std :: println ( stderr , " MySQL error: {}" , e.what ( ) ); } }

سي شارب ADO.NET

يستخدم هذا المثال لغة C# و ADO.NET :

namespace Wikipedia.Examples ;باستخدام System ؛ باستخدام System.Data ؛باستخدام Microsoft.Data.SqlClient ؛public class Example { static void Main ( string [] args ) { using ( SqlCommand command = connection . CreateCommand ()) { command . CommandText = "SELECT * FROM users WHERE USERNAME = @username AND ROOM = @room" ; command . Parameters . AddWithValue ( "@username" , username ); command . Parameters . AddWithValue ( "@room" , room );باستخدام ( SqlDataReader dataReader = command.ExecuteReader ( ) ) { // ... } } } }

SqlCommandيقبل ADO.NET أي نوع valueكمعامل AddWithValue، ويتم تحويل النوع تلقائيًا. لاحظ استخدام "المعاملات المسماة" (أي "@username") بدلاً من "?"— فهذا يسمح لك باستخدام المعامل عدة مرات وبأي ترتيب ضمن نص أمر الاستعلام.

مع ذلك، لا يُنصح باستخدام دالة AddWithValue مع أنواع البيانات ذات الأطوال المتغيرة، مثل varchar و nvarchar. والسبب هو أن .NET يفترض أن طول المعامل هو طول القيمة المُعطاة، بدلاً من الحصول على الطول الفعلي من قاعدة البيانات باستخدام الانعكاس. ونتيجةً لذلك، يتم تجميع خطة استعلام مختلفة وتخزينها لكل طول مختلف. وبشكل عام، يكون الحد الأقصى لعدد الخطط "المكررة" هو حاصل ضرب أطوال الأعمدة ذات الأطوال المتغيرة كما هو مُحدد في قاعدة البيانات. لهذا السبب، من المهم استخدام دالة Add القياسية للأعمدة ذات الأطوال المتغيرة.

command.Parameters.Add(ParamName,VarChar,ParamLength).Value=ParamValue، حيث يمثل ParamLength الطول كما هو محدد في قاعدة البيانات.

بما أن طريقة الجمع القياسية يجب استخدامها لأنواع البيانات ذات الطول المتغير، فمن العادات الجيدة استخدامها لجميع أنواع المعلمات.

يذهب

// تعريف نوع BookModel الذي يغلّف مجموعة اتصالات sql.DB. type BookModel struct { DB * sql . DB }// سيؤدي هذا إلى إدراج كتاب جديد في قاعدة البيانات. func ( m * BookModel ) Insert ( title , author string ) ( int , error ) { stmt := "INSERT INTO book (title, author, created) VALUES(?, ?, UTC_TIMESTAMP())" // ستقوم الدالة "Exec" بإعداد العبارة تلقائيًا، // مما يتطلب اتصالًا إضافيًا بقاعدة البيانات. // // من الممكن تجنب العبارات المُجهزة، إذا كنت متأكدًا من عدم الحاجة إليها. // راجع ExecerContext لمزيد من التفاصيل. https://pkg.go.dev/database/sql/driver#ExecerContext // // تعمل الدوال الأخرى مثل "Query" بنفس الطريقة، // ولها واجهة مكافئة. result , err := m . DB . Exec ( stmt , title , author ) if err != nil { return 0 , err }id , err := result.LastInsertId ( ) // غير مدعوم في برنامج تشغيل Postgres -- استخدم RETURNING بدلاً من ذلك. if err != nil { return 0 , err }// المعرّف المُعاد من النوع int64، لذا نقوم بتحويله إلى نوع int // قبل إعادته. // // ضع في اعتبارك أنه على الأجهزة ذات 32 بت، قد يؤدي هذا إلى اقتطاع القيمة. // عادةً ما يكون من الأكثر أمانًا إعادة int64 من دالتك مباشرةً، // وهو ما زال مدعومًا على الأجهزة ذات 32 بت. return int ( id ), nil }

يختلف بناء جملة المعامل النائب باختلاف قاعدة البيانات. تستخدم قواعد بيانات MySQL وSQL Server وSQLite  رمز علامة الاستفهام (؟)، بينما تستخدم PostgreSQL رمز $N. على سبيل المثال، إذا كنت تستخدم PostgreSQL، فستكتب:

_ , err := m . DB . Exec ( "INSERT INTO ... VALUES ($1, $2, $3)" , ... )

جافا JDBC

يستخدم هذا المثال لغة جافا و JDBC :

package org.wikipedia.examples ;استيراد java.sql.Connection ؛ استيراد java.sql.DriverManager ؛ استيراد java.sql.PreparedStatement ؛ استيراد java.sql.ResultSet ؛ استيراد java.sql.SQLException ؛ استيراد java.sql.Statement ؛استيراد com.mysql.jdbc.jdbc2.optional.MysqlDataSource ؛public class Main {public static void main ( String [] args ) throws SQLException { MysqlDataSource ds = new MysqlDataSource (); ds . setDatabaseName ( "mysql" ); ds . setUser ( "root" );حاول ( Connection conn = ds.getConnection ()) { حاول ( Statement stmt = conn.createStatement ()) { stmt.executeUpdate ( " CREATE TABLE IF NOT EXISTS products (name VARCHAR ( 40), price INT) " ) ; }try ( PreparedStatement stmt = conn.prepareStatement ( " INSERT INTO products VALUES (?, ?)" ) ) { stmt.setString ( 1 , " bike " ) ; stmt.setInt ( 2 , 10900 ) ; stmt.executeUpdate ( ) ; stmt.setString ( 1 , " shoes" ) ; stmt.setInt ( 2 , 7400 ) ; stmt.executeUpdate ( ) ; stmt.setString ( 1 , " phone " ) ; stmt.setInt ( 2 , 29500 ) ; stmt.executeUpdate ( ) ; }try ( PreparedStatement stmt = conn.preparedStatement ( " SELECT * FROM products WHERE name = " ? " ) ) { stmt.setString ( 1 , " shoes " ) ; ResultSet rs = stmt.executeQuery ( ) ; rs.next ( ) ; System.out.println ( rs.getInt ( 2 ) ) ; } } } }

توفر لغة جافا PreparedStatement"مُعدِّلات" ( setInt(int), setString(String), setDouble(double),إلخ) لجميع أنواع البيانات الرئيسية المضمنة.

PHP PDO

يستخدم هذا المثال لغة PHP و PDO : [ 18 ]

<?php// الاتصال بقاعدة بيانات باسم "mysql" وكلمة المرور "root" $connection = new PDO ( 'mysql:host=127.0.0.1;dbname=test;charset=utf8mb4' , 'root' );// تنفيذ طلب على الاتصال، والذي سيؤدي إلى إنشاء // جدول "products" بعمودين، "name" و "price" $connection -> exec ( 'CREATE TABLE IF NOT EXISTS products (name VARCHAR(40), price INT)' );// تجهيز استعلام لإدراج منتجات متعددة في الجدول $statement = $connection- > prepare ( 'INSERT INTO products VALUES (?, ?)' ); $products = [ [ 'bike' , 10900 ], [ 'shoes' , 7400 ], [ 'phone' , 29500 ], ];// تكرار العملية على المنتجات في مصفوفة "products"، و // تنفيذ العبارة المُجهزة لكل منتج foreach ( $products as $product ) { $statement -> execute ( $product ); }// تجهيز عبارة جديدة مع مُعامل مُسمى $statement = $connection- > prepare ( 'SELECT * FROM products WHERE name = :name' ); $statement- > execute ([ ':name' => 'shoes' , ]);// استخدم تفكيك المصفوفة لتعيين اسم المنتج وسعره // إلى المتغيرات المقابلة [ $product , $price ] = $statement -> fetch ();// عرض النتيجة للمستخدم echo "سعر المنتج { $product } هو $ { $price } ." ;

بيرل دي بي آي

يستخدم هذا المثال لغة بيرل و DBI :

#!/usr/bin/env perl -w use strict ; use DBI ;my ( $db_name , $db_user , $db_password ) = ( 'my_database' , 'moi' , 'Passw0rD' ); my $dbh = DBI- > connect ( "DBI:mysql:database=$db_name" , $db_user , $db_password , { RaiseError => 1 , AutoCommit => 1 }) or die "ERROR (main:DBI->connect) while connecting to database $db_name: " . $ DBI:: errstr . "\n" ;$dbh -> do ( 'CREATE TABLE IF NOT EXISTS products (name VARCHAR(40), price INT)' );my $sth = $dbh -> prepare ( 'INSERT INTO products VALUES (?, ?)' ); $sth -> execute ( @$_ ) foreach [ 'bike' , 10900 ], [ 'shoes' , 7400 ], [ 'phone' , 29500 ];$sth = $dbh- > prepare ( "SELECT * FROM products WHERE name = "?" ); $sth- > execute ( 'shoes' ); print "$$_[1]\n" foreach $sth- > fetchrow_arrayref ; $sth- > finish ;$dbh -> قطع الاتصال ؛

واجهة برمجة تطبيقات قواعد البيانات في بايثون

يستخدم هذا المثال لغة بايثون و DB-API:

استيراد mysql.connectorباستخدام ` mysql.connector.connect ( database = "mysql" , user = " root" ) as conn` : باستخدام ` conn.cursor ( prepared = True ) as cursor` : ` cursor.execute( " CREATE TABLE IF NOT EXISTS products (name VARCHAR ( 40), price INT)")` )` ، ` params` : ` list [ tuple [ str , int ]] = [( "bike" , 10900 ), ( "shoes" , 7400 ), ( "phone" , 29500 )] ` ، `cursor.executemany ( " INSERT INTO products VALUES ( %s , %s )" , params ) `، ` params = ( "shoes" )`، ` cursor.execute ( " SELECT * FROM products WHERE name = %s " , params )`)، ` print(cursor.fetchall ( ) [ 0 ] [ 1 ] )`

ماجيك دايركت إس كيو إل

يستخدم هذا المثال لغة SQL المباشرة من الجيل الرابع مثل eDeveloper وuniPaaS وmagic XPA من شركة Magic Software Enterprises

اسم المستخدم الافتراضي Alpha 20 init: 'sister' كلمة المرور الافتراضية ألفا 20 تهيئة: 'أصفر' أمر SQL: SELECT*FROMusersWHEREUSERNAME=:1ANDPASSWORD=:2 وسائط الإدخال: 1: اسم المستخدم 2: كلمة المرور

بيور بيسك

يمكن لبرنامج PureBasic (منذ الإصدار 5.40 LTS) إدارة 7 أنواع من الروابط باستخدام الأوامر التالية:

SetDatabase Blob , SetDatabase Double , SetDatabase Float , SetDatabase Long , SetDatabase Null , SetDatabase Quad , SetDatabase String

توجد طريقتان مختلفتان حسب نوع قاعدة البيانات

بالنسبة لـ SQLite و ODBC و MariaDB/MySQL، استخدم:  ؟

SetDatabaseString ( #Database , 0 , "test" ) If DatabaseQuery ( #Database , "SELECT * FROM employee WHERE id=? " ) ; ... EndIf

للاستخدام في PostgreSQL : $1، $2، $3، ...

SetDatabaseString ( #Database , 0 , "Smith" ) ; -> $1 SetDatabaseString ( #Database , 1 , "Yes" ) ; -> $2 SetDatabaseLong(#Database , 2 , 50 ) ; - > $ 3إذا كانت نتيجة استعلام قاعدة البيانات ( # قاعدة البيانات ، "SELECT * FROM employee WHERE id=$1 AND active=$2 AND years>$3" ) ؛ ... نهاية الشرط

انظر أيضاً

مراجع

  1. 1 2 مجموعة توثيق PHP. "العبارات المُجهزة والإجراءات المخزنة" . دليل PHP . تم الاطلاع عليه بتاريخ 25 سبتمبر 2011 .
  2. بيترونيا، سيرجي (28 أبريل 2007). "مُحسِّن MySQL والعبارات المُجهَّزة" . مدونة سيرجي بيترونيا . مؤرشف من الأصل في 5 فبراير 2018. تم الاطلاع عليه في 25 سبتمبر 2011 .
  3. زايتسيف، بيتر (2 أغسطس 2006). "العبارات المُجهزة في MySQL" . مدونة أداء MySQL . تم الاطلاع عليه بتاريخ 25 سبتمبر 2011 .
  4. "7.6.3.1. كيفية عمل ذاكرة التخزين المؤقت للاستعلام" . دليل مرجعي لـ MySQL 5.1 . أوراكل . تم الاطلاع عليه بتاريخ 26 سبتمبر 2011 .
  5. "كائنات العبارات المُجهزة" . SQLite . ١٨ أكتوبر ٢٠٢١.
  6. أوراكل. "20.9.4. عبارات C API المُجهزة" . دليل مرجعي لـ MySQL 5.5 . تم الاطلاع عليه بتاريخ 27 مارس 2012 .
  7. "13 Oracle Dynamic SQL" . دليل مبرمج Pro*C/C++ Precompiler، الإصدار 9.2 . أوراكل . تم الاطلاع عليه بتاريخ 25 سبتمبر 2011 .
  8. "SQL: Pengertian وSejarah وFungsi وJenis Perintah SQL" .
  9. "SQL Server 2008 R2: إعداد عبارات SQL" . مكتبة MSDN . مايكروسوفت . تم الاطلاع عليه بتاريخ 25 سبتمبر 2011 .
  10. "PREPARE" . وثائق PostgreSQL 9.5.1 . مجموعة تطوير PostgreSQL العالمية . تم الاطلاع بتاريخ 27 فبراير 2016 .
  11. أوراكل. "12.6. بناء جملة SQL للعبارات المُجهزة" . دليل مرجعي لـ MySQL 5.5 . تم الاطلاع عليه بتاريخ 27 مارس 2012 .
  12. "استخدام العبارات المُجهزة" . دروس جافا . أوراكل . تم الاطلاع عليه بتاريخ 25 سبتمبر 2011 .
  13. بانس، تيم. "مواصفات DBI-1.616" . CPAN . تم الاطلاع عليه بتاريخ 26 سبتمبر 2011 .
  14. "Python PEP 289: مواصفات واجهة برمجة تطبيقات قاعدة بيانات بايثون الإصدار 2.0" .
  15. "حقن SQL: كيف تتجنب الوقوع في مأزق" . The Codist. 8 مايو 2007. تم الاطلاع عليه في 1 فبراير 2010 .
  16. شركة أوراكل (20 يناير 2026). [dev.mysql.com/doc/dev/connector-cpp/latest/ "MySQL Connector/C++ Documentation"]. dev.mysql.com . شركة أوراكل.{{cite web}}: تحقق من |url=القيمة ( مساعدة )
  17. شركة أوراكل. "دليل مستخدم X DevAPI" . dev.mysql.com . شركة أوراكل . تم الاطلاع عليه في 1 مارس 2026 .
  18. "مدخل دليل PHP لـ PDO::prepare()" .