Значения или параметры?
При программном формировании 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 ...
-- разные.
Использование непараметризированных запросов
Использование непараметризированных запросов оправдано если:
- Переменная -- это строка или целое число;
- Переменная не может принимать NULL значений;
- Запрос единичный. Т.е. он не будет формироваться многократно с разными значениями переменной.