Az adatbázis-kezelő rendszerekben az előkészített lekérdezés vagy paraméterezett lekérdezés egy DBMS képessége az adatoktól elkülönített SQL kód előfordítására [1] . Az előkészített lekérdezések előnyei:
Az előkészített utasítás valójában egy előre lefordított sablon, amelyet minden végrehajtás során állandó értékekkel helyettesítenek, és általános az olyan SQL DML utasítások használata , mint az INSERT , a SELECT vagy az UPDATE .
Az elkészített nyilatkozatok használatának szokásos sorrendje a következő:
Az előkészített lekérdezés alternatívája az SQL hívása közvetlenül az alkalmazás forráskódjából, kódot és adatot kombináló módon. A fenti példa közvetlen megfelelője:
INSERT INTO termékek (név, ár) ÉRTÉKEK ("bike", "10900");Nem minden optimalizálás hajtható végre az utasítássablon fordítási idején, két okból kifolyólag: a legjobb lekérdezési terv függhet adott paraméterértékektől, a legjobb lekérdezési terv pedig idővel változhat a változó táblák és indexek miatt [4] . Ha egy előkészített lekérdezést csak egyszer hajtanak végre, akkor lassabban fog futni a kiszolgálóhoz való további oda-vissza út miatt [5] . A megvalósítás korlátai a teljesítmény romlásához is vezethetnek; például a MySQL egyes verziói nem tárolták a gyorsítótárban az előkészített lekérdezések eredményeit [6] . Hasonló előnyöket kínálnak a tárolt eljárások , amelyeket szintén előre lefordítanak és a szerveren tárolnak későbbi végrehajtás céljából. A tárolt eljárásoktól eltérően az előkészített lekérdezések általában nem eljárási nyelven íródnak, és nem használhatnak vagy módosíthatnak változókat vagy vezérlőfolyamat-struktúrákat, hanem egy deklaratív adatbázis-lekérdezési nyelvre támaszkodnak. Az elkészített lekérdezések egyszerűségüknek és az ügyféloldali emulációnak köszönhetően (ha a cél DBMS nem támogatja őket) jobban hordozhatóak a különböző DBMS-ek között, mint a tárolt eljárások.
Szinte az összes elterjedt DBMS , köztük az SQLite , [7] MySQL , [8] Oracle , [9] DB2 , [10] Microsoft SQL Server [11] és PostgreSQL [12] támogatja az előkészített lekérdezéseket. Az előkészített lekérdezéseket általában egy speciális bináris protokoll segítségével hívják meg, amely úgy tűnik, hogy növeli az adatátviteli sebességet, és állítólag tovább védi az SQL-befecskendezést, de egyes DBMS-ek, köztük például a MySQL, lehetővé teszik hibakeresési célokra előkészített lekérdezések meghívását a szintaxis használatával. SQL lekérdezések [13] .
Sok programozási nyelv támogatja az előkészített lekérdezéseket a szabványos könyvtáraiban, és emulálja azokat olyan esetekben, amikor a cél DBMS nem támogatja ezt a képességet. Ezen nyelvek közé tartozik a Java ( JDBC [14] használatával ), Perl ( DBI (perl) [15] ), PHP ( PDO [1] használatával ) és Python (DB-API használatával [16] ). . Az ügyféloldali emuláció hatékonyabb lehet a teljesítmény szempontjából egyetlen kérés esetén, és kevésbé hatékony több kérés esetén. Az SQL injekciók ellen is segít, akárcsak az előkészített lekérdezések DBMS oldalon történő közvetlen megvalósítása [17] .
Ezek a példák Java -t és JDBC -t használnak :
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 ; public class Fő { public static void main ( String [] args ) SQLException { MysqlDataSource ds = new MysqlDataSource ( ); ds . setDatabaseName ( "mysql" ); ds . setUser ( "root" ); try ( Connection conn = ds . getConnection ()) { try ( Statement stmt = conn . createStatement ()) { stmt . executeUpdate ( "CREATE TABLE IF NOT EXISTS termékek (név VARCHAR(40, ár INT)" ); } try ( PreparedStatement stmt = conn . readyStatement ( "INSERT INTO products VALUES (?, ?)" )) { stmt . setString ( 1 , "kerékpár" ); stmt . setInt ( 2 , 10900 ); stmt . executeUpdate (); stmt . setString ( 1 , "cipők" ); stmt . setInt ( 2 , 7400 ); stmt . executeUpdate (); stmt . setString ( 1 , "telefon" ); stmt . setInt ( 2 , 29500 ); stmt . executeUpdate (); } try ( PreparedStatement stmt = conn . readyStatement ( "SELECT * FROM products WHERE name =?" )) { stmt . setString ( 1 , "cipők" ); ResultSet rs = stmt . executeQuery (); rs . következő (); Rendszer . ki . println ( rs . getInt ( 2 )); } } } }A Java PreparedStatement"beállítókat" ( setInt(int), setString(String), setDouble(double),stb.) biztosít az összes főbb beépített adattípushoz.
Ez a példa PHP -t és PDO -t használ :
<?php try { // Csatlakozás egy "mysql" nevű adatbázishoz "root" jelszóval $connection = new PDO ( 'mysql:dbname=mysql' , 'root' ); // Végrehajt egy kérést a kapcsolaton, amely létrehoz // egy "products" táblát két oszloppal, "name" és "price" $connection -> exec ( 'CREATE TABLE IF NOT EXISTS products (név VARCHAR(40), ár INT)' ); // Lekérdezés készítése több termék beszúrásához a táblába $ Statement = $connection -> ready ( 'INSERT INTO products VALUES (?, ?)' ); $termékek = [ [ 'kerékpár' , 10900 ], [ 'cipő' , 7400 ], [ 'telefon' , 29500 ], ]; // Iteráljon a "products" tömbben lévő termékeken, és // hajtsa végre az előkészített utasítást minden termékhez foreach ( $products as $product ) { $statement -> execute ( $product ); } // Új utasítás készítése elnevezett paraméterrel $statement = $connection -> ready ( 'SELECT * FROM products WHERE name = :name' ); $statement -> execute ([ ':name' => 'cipők' , ]); // Használja a tömb destrukturálást a termék nevének és árának hozzárendeléséhez // a megfelelő változókhoz [ $product , $price ] = $statement -> fetch (); // Az eredmény megjelenítése a felhasználónak echo "A termék ára { $product } is \$ { $price } ." ; // Zárja be a kurzort, hogy a `fetch` ismét használható legyen $ Statement -> closeCursor (); } catch ( \Exception $e ) { echo 'Hiba történt: ' . $e -> getMessage (); }Ez a példa Perl -t és DBI -t használ :
#!/usr/bin/perl -w a szigorú ; használja a DBI-t ; my ( $db_name , $ db_user , $db_password ) = ( 'sajat_adatbázis' , 'moi' , 'Passw0rD' ); my $dbh = DBI -> connect ( "DBI:mysql:database=$db_name" , $ db_user , $db_password , { RaiseError => 1 , AutoCommit => 1 }) vagy die "ERROR (main:DBI->connect) miközben csatlakozik a $db_name adatbázishoz: " . $ DBI:: errstr . "\n" ; $dbh -> do ( 'CREATE TABLE IF NOT EXISTS products (VARCHAR(40 név, ár INT)' ); my $sth = $dbh -> ready ( 'INSERT INTO products VALUES (?, ?)' ); $sth -> végrehajt ( @$_ ) foreach [ 'bike' , 10900 ], [ 'cipők' , 7400 ], [ 'telefon' , 29500 ]; $sth = $dbh -> előkészíti ( "SELECT * FROM products WHERE név =?" ); $sth -> execute ( 'cipők' ); print "$$_[1]\n" foreach $sth -> fetchrow_arrayref ; $sth -> befejezni ; $dbh -> leválasztás ;Ez a példa C# -t és ADO.NET -t használ :
using ( SqlCommand parancs = kapcsolat . CreateCommand ()) { parancs . CommandText = "SELECT * FROM felhasználók WHERE USERNAME = @felhasználónév AND ROOM = @szoba" ; parancsot . Paraméterek . AddWithValue ( "@felhasználónév" , felhasználónév ); parancsot . Paraméterek . AddWithValue ( "@szoba" , szoba ); using ( SqlDataReader dataReader = parancs . ExecuteReader ()) { // ... } }Ez a példa Python és DB-API-t használ:
import mysql.connector mysql -lel . csatlakozó . connect ( adatbázis = "mysql" , user = "root" ) as conn : with conn . kurzor ( előkészített = True ) mint kurzor : kurzor . execute ( "CREATE TABLE IF NOT EXISTS products (név VARCHAR(40), ár INT)" ) params = [( "bike" , 10900 ), ( "cipők" , 7400 ), ( "phone" , 29500 )] kurzor . executemany ( "INSERT INTO products VALUES ( %s , %s )" , params ) params = ( "cipők" ,) kurzor . execute ( "SELECT * FROM products WHERE name = %s " , paraméterek ) print ( kurzor . fetchall ()[ 0 ][ 1 ])