Cerere pregătită

În sistemele de management al bazelor de date, o interogare pregătită sau o interogare parametrizată este capacitatea unui SGBD de a precompila cod SQL separat de date [1] . Avantajele interogărilor pregătite:

O instrucțiune pregătită este de fapt un șablon precompilat care este înlocuit cu valori constante în timpul fiecărei execuții și este obișnuit să se utilizeze instrucțiuni SQL DML precum INSERT , SELECT sau UPDATE .

Secvența obișnuită pentru utilizarea instrucțiunilor pregătite este:

INSERT INTO produse (nume, preț) VALORI (?, ?);

O alternativă la o interogare pregătită este apelarea SQL direct din codul sursă al aplicației într-un mod care combină codul și datele. Echivalent direct cu exemplul de mai sus:

INSERT INTO produse (nume, preț) VALORI ("bicicletă", "10900");

Nu toate optimizările pot fi efectuate în momentul compilării șablonului de instrucțiuni din două motive: cel mai bun plan de interogare poate depinde de valorile anumitor parametri, iar cel mai bun plan de interogare se poate schimba în timp din cauza modificării tabelelor și indicilor [4] . Când și dacă o interogare pregătită este executată o singură dată, aceasta va rula mai lent din cauza călătoriei suplimentare dus-întors către server [5] . Limitările de implementare pot duce, de asemenea, la degradarea performanței; de exemplu, unele versiuni de MySQL nu au memorat în cache rezultatele interogărilor pregătite [6] . Procedurile stocate , care sunt, de asemenea, precompilate și stocate pe server pentru a fi executate ulterioare, oferă beneficii similare. Spre deosebire de procedurile stocate, o interogare pregătită nu este de obicei scrisă într-un limbaj procedural și nu poate folosi sau modifica variabile sau folosi structuri de flux de control, bazându-se în schimb pe un limbaj declarativ de interogare a bazei de date. Datorită simplității și capacității de emulare din partea clientului (dacă SGBD-ul țintă nu le acceptă), interogările pregătite sunt mai portabile între diferite SGBD decât procedurile stocate.

Suport software

Aproape toate SGBD-urile comune , inclusiv SQLite , [7] MySQL , [8] Oracle , [9] DB2 , [10] Microsoft SQL Server [11] și PostgreSQL [12] acceptă interogări pregătite. Interogările pregătite sunt de obicei invocate folosind un protocol binar special care pare să mărească rata de transfer de date și ar trebui să protejeze în continuare împotriva injectării SQL, dar unele DBMS, inclusiv de exemplu MySQL, permit, în scopuri de depanare, să apeleze interogări pregătite folosind sintaxa Interogări SQL [13] .

Multe limbaje de programare acceptă interogări pregătite în bibliotecile lor standard și le emulează pentru cazurile în care DBMS-ul țintă nu acceptă această capacitate. Printre aceste limbaje se numără Java (folosind JDBC [14] ), Perl (folosind DBI (perl) [15] ), PHP (folosind PDO [1] ) și Python (folosind DB-API [16] ) . Emularea la nivelul clientului poate fi mai eficientă în ceea ce privește performanța pentru cereri individuale și mai puțin eficientă pentru cereri multiple. De asemenea, ajută împotriva injecțiilor SQL, la fel ca și implementarea directă a interogărilor pregătite pe partea DBMS [17] .

Exemple

Java JDBC

Acest exemplu folosește Java și JDBC :

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource ; import java.sql.Connection ; import java.sql.DriverManager ; import java.sql.PreparedStatement ; import java.sql.ResultSet ; import java.sql.SQLException ; import java.sql.Statement ; clasă publică principală { public static void main ( String [] args ) aruncă SQLException { MysqlDataSource ds = new MysqlDataSource (); ds . setDatabaseName ( "mysql" ); ds . setUser ( "rădăcină" ); try ( Connection conn = ds . getConnection ()) { try ( Statement stmt = conn . createStatement ()) { stmt . executeUpdate ( "CREATE TABLE IF NU EXISTIST products (nume VARCHAR(40), pret INT)" ); } încercați ( PreparedStatement stmt = conn . prepareStatement ( „INSERT INTO products VALUES (?, ?)” )) { stmt . setString ( 1 , „bicicletă” ); stmt . setInt ( 2 , 10900 ); stmt . executeUpdate (); stmt . setString ( 1 , „pantofi” ); stmt . setInt ( 2 , 7400 ); stmt . executeUpdate (); stmt . setString ( 1 , „telefon” ); stmt . setInt ( 2 , 29500 ); stmt . executeUpdate (); } încercați ( PreparedStatement stmt = conn . prepareStatement ( " SELECT * FROM products WHERE name = ?" )) { stmt . setString ( 1 , „pantofi” ); ResultSet rs = stmt . executeQuery (); rs . următorul (); Sistem . afară . println ( rs . getInt ( 2 )); } } } }

Java PreparedStatementfurnizează „seteri” ( setInt(int), setString(String), setDouble(double),etc.) pentru toate tipurile majore de date încorporate.

PHP PDO

Acest exemplu folosește PHP și PDO :

<?php try { // Conectați-vă la o bază de date numită "mysql", cu parola "root" $connection = new PDO ( 'mysql:dbname=mysql' , 'root' ); // Executați o solicitare pe conexiune, care va crea // un tabel „produse” cu două coloane, „nume” și „preț” $connection -> exec ( 'CREATE TABLE IF NOT EXISTS products (nume VARCHAR(40),) preţ INT)' ); // Pregătește o interogare pentru a insera mai multe produse în tabel $statement = $connection -> prepare ( 'INSERT INTO products VALUES (?, ?)' ); $produse = [ [ 'bicicletă' , 10900 ], [ 'pantofi' , 7400 ], [ 'telefon' , 29500 ], ]; // Iterează prin produsele din matricea „produse” și // execută instrucțiunea pregătită pentru fiecare produs foreach ( $produse ca $produs ) { $statement -> execute ( $produs ); } // Pregătește o nouă instrucțiune cu un parametru numit $statement = $connection -> prepare ( 'SELECT * FROM products WHERE nume = :nume' ); $statement -> execute ([ ':name' => 'pantofi' , ]); // Folosește destructurarea matricei pentru a atribui numele produsului și prețul acestuia // variabilelor corespunzătoare [ $produs , $preț ] = $statement -> fetch (); // Afișează rezultatul pentru utilizatorul ecou "Prețul produsului { $produs } este \$ { $preț } ." ; // Închide cursorul astfel încât `fetch` să poată fi folosit din nou $statement -> closeCursor (); } catch ( \Exception $e ) { echo 'A apărut o eroare: ' . $e -> getMessage (); }

Perl DBI

Acest exemplu folosește Perl și DBI :

#!/usr/bin/perl -w use strict ; utilizați DBI ; my ( $db_name , $db_user , $db_parola ) = ( 'my_database' , 'moi' , 'Passw0rD' ); my $dbh = DBI -> connect ( "DBI:mysql:database=$db_name" , $db_user , $db_password , { RaiseError => 1 , AutoCommit => 1 }) sau die "EROARE (principal:DBI->connect) în timpul conectării la baza de date $db_name: " . $ DBI:: errstr . „\n” ; $dbh -> do ( 'CREAȚI TABEL DACĂ NU EXISTĂ produse (nume VARCHAR(40), preț INT)' ); my $sth = $dbh -> prepare ( 'INSERT INTO products VALUES (?, ?)' ); $sth -> execute ( @$_ ) foreach [ 'bicicletă' , 10900 ], [ 'pantofi' , 7400 ], [ 'telefon' , 29500 ]; $sth = $dbh -> prepare ( "SELECT * FROM products WHERE name = ?" ); $sth -> execute ( 'pantofi' ); print "$$_[1]\n" pentru fiecare $sth -> fetchrow_arrayref ; $sth -> termina ; $dbh -> deconectare ;

C# ADO.NET

Acest exemplu folosește C# și ADO.NET :

folosind ( SqlCommand command = connection . CreateCommand ()) { command . CommandText = "SELECT * FROM users WHERE USERNAME = @username AND ROOM = @room" ; comanda . Parametrii . AddWithValue ( "@username" , nume de utilizator ); comanda . Parametrii . AddWithValue ( "@camera" , camera ); folosind ( SqlDataReader dataReader = comandă . ExecuteReader ()) { // ... } }

Python DB-API

Acest exemplu utilizează Python și DB-API:

import mysql.connector cu mysql . conector . connect ( baza de date = "mysql" , utilizator = "rădăcină" ) ca conn : cu conn . cursor ( preparat = True ) ca cursor : cursor . execute ( „CREATE TABLE IF NOT EXISTS products (nume VARCHAR(40), preț INT)” ) params = [( „bicicletă” , 10900 ), ( „pantofi” , 7400 ), ( „telefon” , 29500 )] cursor . executemany ( „INSERT INTO products VALUES ( %s , %s )” , params ) params = ( „pantofi” ,) cursor . execute ( "SELECT * FROM produse WHERE nume = %s " , parametri ) print ( cursor . fetchall ()[ 0 ][ 1 ])

Note

  1. 1 2 Grupul de documentație PHP Declarații pregătite și proceduri stocate . manual PHP . Preluat la 25 septembrie 2011. Arhivat din original la 8 aprilie 2022.
  2. Shuping Ran, Doug Palmer, Paul Brebner, Shiping Chen, Ian Gorton, Jeffrey Gosper, Lei Hu, Anna Liu și Phong Tran. TEHNOLOGIA J2EE METODOLOGIA DE EVALUARE A PERFORMANȚEI . citeseerx.ist.psu.edu . Consultat la 15 aprilie 2022. Arhivat din original pe 15 aprilie 2022.
  3. Stephen Thomas, Laurie Williams, Tao Xie. La generarea automată de declarații pregătite pentru a elimina vulnerabilitățile de injectare SQL  //  Tehnologia informației și software-ului. — 2009-03-01. — Vol. 51 , iss. 3 . — P. 589–598 . — ISSN 0950-5849 . - doi : 10.1016/j.infsof.2008.08.002 . Arhivat din original pe 9 mai 2012.
  4. Petrunia, Sergey MySQL Optimizer și Prepared Statements . Blogul lui Sergey Petrunia (28 aprilie 2007). Consultat la 25 septembrie 2011. Arhivat din original la 5 februarie 2018.
  5. Zaitsev, Peter MySQL Prepared Statements . MySQL Performance Blog (2 august 2006). Preluat la 25 septembrie 2011. Arhivat din original la 23 martie 2014.
  6. 7.6.3.1. Cum funcționează cache-ul de interogări . Manual de referință MySQL 5.1 . Oracol. Consultat la 26 septembrie 2011. Arhivat din original pe 25 septembrie 2011.
  7. Prepared Statement Objects . SQLite (18 octombrie 2021). Preluat la 9 aprilie 2022. Arhivat din original pe 7 mai 2022.
  8. Oracle 20.9.4. C API Prepared Statements . Manual de referință MySQL 5.5 . Preluat la 27 martie 2012. Arhivat din original la 30 iunie 2017.
  9. 13 Oracle Dynamic SQL . Ghidul programatorului Pro*C/C++ Precompiler, versiunea 9.2 . Oracol. Preluat la 25 septembrie 2011. Arhivat din original la 26 octombrie 2011.
  10. Utilizarea instrucțiunilor PREPARE și EXECUTE . Centrul de informare i5/OS, versiunea 5 ediția 4 . IBM. Preluat: 25 septembrie 2011.  (link inaccesibil)
  11. SQL Server 2008 R2: Pregătirea instrucțiunilor SQL . Bibliotecă MSDN . Microsoft. Preluat la 25 septembrie 2011. Arhivat din original la 5 iulie 2017.
  12. PREGĂTIRE . Documentație PostgreSQL 9.5.1 . Grupul de dezvoltare globală PostgreSQL. Consultat la 27 februarie 2016. Arhivat din original pe 9 martie 2018.
  13. Oracolul 12.6. Sintaxa SQL pentru instrucțiunile pregătite . Manual de referință MySQL 5.5 . Preluat la 27 martie 2012. Arhivat din original la 16 iulie 2019.
  14. Utilizarea declarațiilor pregătite . Tutorialele Java . Oracol. Preluat la 25 septembrie 2011. Arhivat din original la 12 noiembrie 2011.
  15. Bunce, specificația Tim DBI-1.616 . CPAN . Preluat: 26 septembrie 2011.
  16. Python PEP 289: Python Database API Specification v2.0 . Preluat la 9 aprilie 2022. Arhivat din original la 3 martie 2022.
  17. Anikin Evgheni Alexandrovici. Injectarea SQL și cum să vă protejați de accesul neautorizat  // CONTINUUM. MATEMATICA. INFORMATICĂ. EDUCAŢIE. - 2016. - Nr 4 . — ISSN 2500-1957 .