Jump to content

Подготовленное заявление

(Перенаправлено из переменной Bind )

В системах управления базами данных (СУБД) подготовленный оператор , параметризованный оператор или параметризованный запрос — это функция, при которой база данных предварительно компилирует код SQL и сохраняет результаты, отделяя их от данных. Преимущества подготовленных отчетов: [1]

  • эффективность, поскольку их можно использовать повторно без повторной компиляции.
  • безопасность за счет уменьшения или устранения с использованием SQL-инъекций атак

Подготовленный оператор принимает форму предварительно скомпилированного шаблона , в который во время каждого выполнения подставляются константные значения, и обычно использует операторы SQL DML, такие как INSERT , SELECT или UPDATE .

Общий рабочий процесс для подготовленных операторов:

  1. Подготовка : приложение создает шаблон заявления и отправляет его в СУБД. Определенные значения остаются неуказанными и называются параметрами , заполнителями или переменными привязки (помечены знаком «?» ниже):
    INSERT INTO products (name, price) VALUES (?, ?);
  2. Компиляция : СУБД компилирует (анализирует, оптимизирует и транслирует) шаблон оператора и сохраняет результат, не выполняя его.
  3. Выполнить : приложение предоставляет (или связывает ) значения для параметров шаблона оператора, а СУБД выполняет оператор (возможно, возвращая результат). Приложение может много раз запрашивать у СУБД выполнение оператора с разными значениями. В приведенном выше примере приложение может предоставить значения «велосипед» для первого параметра и «10900» для второго параметра, а затем значения «обувь» и «7400».

Альтернативой подготовленному оператору является вызов SQL непосредственно из исходного кода приложения таким образом, чтобы объединить код и данные. Прямой эквивалент приведенному выше примеру:

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

Не вся оптимизация может быть выполнена во время компиляции шаблона инструкции по двум причинам: лучший план может зависеть от конкретных значений параметров, а лучший план может меняться по мере изменения таблиц и индексов с течением времени. [2]

С другой стороны, если запрос выполняется только один раз, подготовленные на стороне сервера операторы могут работать медленнее из-за дополнительного обращения к серверу. [3] Ограничения реализации также могут привести к снижению производительности; например, некоторые версии MySQL не кэшировали результаты подготовленных запросов. [4] , Аналогичными преимуществами обладает хранимая процедура которая также предварительно компилируется и сохраняется на сервере для последующего выполнения. В отличие от хранимой процедуры, подготовленный оператор обычно не пишется на процедурном языке и не может использовать или изменять переменные или использовать структуры потока управления, вместо этого полагаясь на декларативный язык запросов к базе данных. Благодаря своей простоте и эмуляции на стороне клиента подготовленные операторы более переносимы между поставщиками.

Поддержка программного обеспечения

[ редактировать ]

Основные СУБД , включая SQLite , [5] MySQL , [6] Оракул , [7] IBM Дб2 , [8] Microsoft SQL-сервер [9] и PostgreSQL [10] поддержка подготовленных заявлений. Подготовленные операторы обычно выполняются через двоичный протокол, отличный от SQL, для эффективности и защиты от SQL-инъекций, но в некоторых СУБД, таких как MySQL, подготовленные операторы также доступны с использованием синтаксиса SQL для целей отладки. [11]

Ряд языков программирования поддерживают подготовленные операторы в своих стандартных библиотеках и эмулируют их на стороне клиента, даже если базовая СУБД их не поддерживает, Java JDBC . включая [12] Perl DBI , [13] PHP PDO [1] и Python . DB-API [14] Эмуляция на стороне клиента может быть быстрее для запросов, которые выполняются только один раз, за ​​счет уменьшения количества обращений к серверу, но обычно медленнее для запросов, выполняемых много раз. Он одинаково эффективно противостоит атакам SQL-инъекций.

Многие типы атак SQL-инъекций можно устранить, отключив литералы , что фактически требует использования подготовленных операторов; по состоянию на 2007 год только H2 поддерживает эту функцию. [15]

В этом примере используются Java и 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;

public class Main {

    public static void main(String[] args) throws 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 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.prepareStatement("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

try {
    // Connect to a database named "mysql", with the password "root"
    $connection = new PDO('mysql:dbname=mysql', 'root');

    // Execute a request on the connection, which will create
    // a table "products" with two columns, "name" and "price"
    $connection->exec('CREATE TABLE IF NOT EXISTS products (name VARCHAR(40), price INT)');

    // Prepare a query to insert multiple products into the table
    $statement = $connection->prepare('INSERT INTO products VALUES (?, ?)');
    $products  = [
        ['bike', 10900],
        ['shoes', 7400],
        ['phone', 29500],
    ];

    // Iterate through the products in the "products" array, and
    // execute the prepared statement for each product
    foreach ($products as $product) {
        $statement->execute($product);
    }

    // Prepare a new statement with a named parameter
    $statement = $connection->prepare('SELECT * FROM products WHERE name = :name');
    $statement->execute([
        ':name' => 'shoes',
    ]);

    // Use array destructuring to assign the product name and its price
    // to corresponding variables
    [ $product, $price ] = $statement->fetch();

    // Display the result to the user
    echo "The price of the product {$product} is \${$price}.";

    // Close the cursor so `fetch` can eventually be used again
    $statement->closeCursor();
} catch (\Exception $e) {
    echo 'An error has occurred: ' . $e->getMessage();
}

Перл ДБИ

[ редактировать ]

В этом примере используются Perl и DBI :

#!/usr/bin/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->disconnect;

В этом примере используются C# и ADO.NET :

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);

    using (SqlDataReader dataReader = command.ExecuteReader())
    {
        // ...
    }
}

ADO.NET SqlCommand примет любой тип для value параметр AddWithValue, и преобразование типов происходит автоматически. Обратите внимание на использование «именованных параметров» (т.е. "@username") скорее, чем "?"— это позволяет использовать параметр несколько раз и в любом произвольном порядке в тексте команды запроса.

Однако метод AddWithValue не следует использовать с типами данных переменной длины, такими как varchar и nvarchar. Это связано с тем, что .NET предполагает, что длина параметра равна длине заданного значения, а не получает фактическую длину из базы данных посредством отражения. Следствием этого является то, что для каждой длины компилируется и сохраняется отдельный план запроса. Как правило, максимальное количество «дубликатов» планов представляет собой произведение длин столбцов переменной длины, указанных в базе данных. По этой причине важно использовать стандартный метод Add для столбцов переменной длины:

command.Parameters.Add(ParamName, VarChar, ParamLength).Value = ParamValue, где ParamLength — длина, указанная в базе данных.

Поскольку стандартный метод Add необходимо использовать для типов данных переменной длины, рекомендуется использовать его для всех типов параметров.

Python БД-API

[ редактировать ]

В этом примере используются Python и DB-API:

import mysql.connector

with mysql.connector.connect(database="mysql", user="root") as conn:
    with conn.cursor(prepared=True) as cursor:
        cursor.execute("CREATE TABLE IF NOT EXISTS products (name VARCHAR(40), price INT)")
        params = [("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

[ редактировать ]

В этом примере используется Direct SQL из языка четвертого поколения, такого как eDeveloper, uniPaaS и Magic XPA от Magic Software Enterprises.

Virtual username  Alpha 20   init: 'sister'
Virtual password  Alpha 20   init: 'yellow'

SQL Command:   SELECT * FROM users WHERE USERNAME=:1 AND PASSWORD=:2

Input Arguments: 
1:  username
2:  password

PureBasic (начиная с версии 5.40 LTS) может управлять 7 типами ссылок с помощью следующих команд:

SetDatabaseBlob, SetDatabaseDouble, SetDatabaseFloat, SetDatabaseLong, SetDatabaseNull, SetDatabaseQuad, SetDatabaseString

Существует 2 разных метода в зависимости от типа базы данных.

Для 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

If DatabaseQuery(#Database, "SELECT * FROM employee WHERE id=$1 AND active=$2 AND years>$3")    
  ; ...
EndIf

См. также

[ редактировать ]
  1. ^ Jump up to: а б Группа документации PHP. «Подготовленные операторы и хранимые процедуры» . Руководство по PHP . Проверено 25 сентября 2011 г.
  2. ^ Петруния, Сергей (28 апреля 2007 г.). «Оптимизатор MySQL и подготовленные операторы» . Блог Сергея Петрунии . Проверено 25 сентября 2011 г.
  3. ^ Зайцев, Петр (2 августа 2006 г.). «Подготовленные операторы MySQL» . Блог о производительности MySQL . Проверено 25 сентября 2011 г.
  4. ^ «7.6.3.1. Как работает кэш запросов» . Справочное руководство MySQL 5.1 . Оракул . Проверено 26 сентября 2011 г.
  5. ^ «Подготовленные объекты операторов» . SQLite . 18 октября 2021 г.
  6. ^ Оракул. «20.9.4. Заявления, подготовленные API C» . Справочное руководство по MySQL 5.5 . Проверено 27 марта 2012 г.
  7. ^ «13 Oracle Dynamic SQL» . Руководство программиста прекомпилятора Pro*C/C++, выпуск 9.2 . Оракул . Проверено 25 сентября 2011 г.
  8. ^ «SQL: определение, история, функции и типы команд SQL» .
  9. ^ «SQL Server 2008 R2: подготовка операторов SQL» . Библиотека MSDN . Майкрософт . Проверено 25 сентября 2011 г.
  10. ^ "ПОДГОТОВИТЬ" . Документация PostgreSQL 9.5.1 . Группа глобального развития PostgreSQL . Проверено 27 февраля 2016 г.
  11. ^ Оракул. «12.6. Синтаксис SQL для подготовленных операторов» . Справочное руководство по MySQL 5.5 . Проверено 27 марта 2012 г.
  12. ^ «Использование подготовленных операторов» . Учебники по Java . Оракул . Проверено 25 сентября 2011 г.
  13. ^ Банс, Тим. «Спецификация ДБИ-1.616» . КПАН . Проверено 26 сентября 2011 г.
  14. ^ «Python PEP 289: Спецификация API базы данных Python v2.0» .
  15. ^ «SQL-инъекции: как не застрять» . Кодист. 8 мая 2007 года . Проверено 1 февраля 2010 г.
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: 23be48d2c0abee31f5f5d034c95e66c8__1721970780
URL1:https://arc.ask3.ru/arc/aa/23/c8/23be48d2c0abee31f5f5d034c95e66c8.html
Заголовок, (Title) документа по адресу, URL1:
Prepared statement - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)