Значения или параметры?

Материал из GedeminWiki
Перейти к: навигация, поиск

При программном формировании SQL запроса часто необходимо ограничить выборку в зависимости от значений некоторых переменных. Наиболее быстрый способ -- это включить значения непосредственно в текст запроса. Например так:

...
Dim CompanyID ' Переменная, которая хранит идентификатор компании
...
q.SQL.Text = "SELECT name FROM gd_contact WHERE id = " & CompanyID
...

Обратите внимание, мы всегда отдаем предпочтение оператору & вместо +, так как первый в дополнение к слиянию произведет преобразование всех операндов к строковому представлению.

Такой код будет работать до тех пор, пока CompanyID будет содержать целочисленные значения. Но, что произойдет, если по условиям задачи переменная CompanyID может быть не заполнена? В этом случае оператор конкатенции строк -- & -- даст пустую строку и на выходе мы получим ошибку в синтаксисе SQL.

Выкрутиться из такой ситуации можно введя в запрос неявное преобразование к строковому типу. Например, взяв значение переменной в кавычки:

...
Dim CompanyID ' Переменная, которая хранит идентификатор компании
...
q.SQL.Text = "SELECT name FROM gd_contact WHERE id = '" & CompanyID & "'"
...

Цена такого решения -- снижение производительности, так как сервер для каждой строки будет переводить целочисленный идентификатор в строковое представление. Кроме этого, пустая строка ни есть NULL, т.е. на выходе мы получим не полную выборку.

Еще хуже обстоит ситуация с переменными типа дата, время и число с плавающей точкой. Дело в том, что сервер преобразовывает даты и числа из строк только при строгом соблюдении формата. Например, разделителем целой и дробной части числа должна быть точка, а на локальном компьютере, где и происходит формирование запроса, в региональных настройках операционной системы может быть выставлена запятая...

Решение у данной проблемы одно -- использование параметризованных запросов. Перепишем наш запрос следующим образом:

...
Dim CompanyID ' Переменная, которая хранит идентификатор компании
...
q.SQL.Text = "SELECT name FROM gd_contact WHERE id = :ID"
q.ParamByName("ID").AsInteger = CompanyID
...

Если, по условиям задачи, ограничение накладывается на поле, которое может содержать NULL значения, то запрос и передачу параметра следует организовать следующим образом:

... 
Dim PlaceKey
...
q.SQL.Text = "SELECT * FROM gd_contact WHERE COALESCE(placekey, 0) = COALESCE(:placekey, 0)
q.ParamByName("PlaceKey").AsVariant = PlaceKey
...

Обратите внимание на присваивание значения через AsVariant.

Для версии сервера начиная с Firebird 2.0 вместо конструкции:

SELECT * FROM gd_contact WHERE COALESCE(placekey, 0) = COALESCE(:placekey, 0)

можно написать:

SELECT * FROM gd_contact WHERE placekey IS NOT DISTINCT FROM :PlaceKey

Даже, если по условиям задачи в запрос надо подставить строковое значение, которое не может быть NULL, все равно стоит использовать параметры вместо непосредственных значений. Дело в том, что для повышения производительности Гедымин кэширует подготовленные (prepared) запросы. Но, для того, чтобы получить выгоду от этого механизма, текст запроса не должен меняться. Т.е.

SELECT * FROM gd_contact WHERE id = :ID

С точки зрения сервера это всегда один и тот же запрос, а:

SELECT * FROM gd_contact WHERE id = 1
SELECT * FROM gd_contact WHERE id = 2
SELECT * FROM gd_contact WHERE id = 3
...

-- разные.

Использование непараметризированных запросов

Использование непараметризированных запросов оправдано если:

  1. Переменная -- это строка или целое число;
  2. Переменная не может принимать NULL значений;
  3. Запрос единичный. Т.е. он не будет формироваться многократно с разными значениями переменной.
Персональные инструменты
Пространства имён

Варианты
Действия
Навигация
Инструменты