Вопрос по sql-server, sql – Delphi: как передать список в качестве параметра в SQL-запрос?
У меня есть список целых чисел или строк, и мне нужно передать его в качестве параметра для Delphi DataSet. Как это сделать?
Вот пример. MyQuery это что-то вроде:
<code>select * from myTable where intKey in :listParam </code>
Я установил параметр в виде списка, массива или чего-то еще:
<code>MyQuery.ParamByName('listParam').AsSomething := [1,2,3]; </code>
и это приведет к тому, что этот запрос будет отправлен на сервер SQL:
<code>select * from myTable where intKey in (1, 2, 3) </code>
Было бы еще лучше, если бы решение также работало со строками, выполняя этот запрос:
<code>select * from myTable where stringKey in :listParam </code>
становиться:
<code>select * from myTable where stringKey in ('a', 'b', 'c') </code>
Я считаю, что это простой вопрос, но "IN" не является хорошим ключевым словом для поиска в Интернете.
Пожалуйста, ответьте, как мне настроить параметр в IDE, запрос и как передать параметры.
Я использую Delphi 7.
EditedЯ считаю, что ответ - & quot;it isn't possible to do directly& Quot ;. Если кто-то даст мне нехакский ответ, принятый ответ будет изменен.
Есть несколько вариантов для вас, но в основном вам нужно поместить свои значения в таблицу. Я бы предложил переменную таблицы для этого.
Вот версия, которая распаковывает список int.
declare @IDs varchar(max)
set @IDs = :listParam
set @IDs = @IDs+','
declare @T table(ID int primary key)
while len(@IDs) > 1
begin
insert into @T(ID) values (left(@IDs, charindex(',', @IDs)-1))
set @IDs = stuff(@IDs, 1, charindex(',', @IDs), '')
end
select *
from myTable
where intKey in (select ID from @T)
Возможно иметь несколько операторов запросов. Параметр:listParam
должна быть строка:
MyQuery.ParamByName('listParam').AsString := '1,2,3';
Вы можете использовать ту же технику для строк. Вам просто нужно изменить тип данныхID
напримерvarchar(10)
.
Вместо распаковки с циклом while вы можете использоватьфункция разделения
declare @T table(ID varchar(10))
insert into @T
select s
from dbo.Split(',', :listParam)
select *
from myTable
where charKey in (select ID from @T)
Строковый параметр может выглядеть так:
MyQuery.ParamByName('listParam').AsString := 'Adam,Bertil,Caesar';
Вам придется преобразовать список в список SQL в виде простого текста.
Например:
function ListToText(const Args: array of string): string; overload;
var i: integer;
begin
result := '(';
for i := 0 to high(Args) do
result := result+QuotedStr(Args[i])+',';
result[length(result)] := ')';
end;
function ListToText(const Args: array of integer): string; overload;
var i: integer;
begin
result := '(';
for i := 0 to high(Args) do
result := result+IntToStr(Args[i])+',';
result[length(result)] := ')';
end;
Для использования как таковой:
SQL.Text := 'select * from myTable where intKey in '+ListToText([1,2,3]);
SQL.Text := 'select * from myTable where stringKey in '+ListToText(['a','b','c']);
MyQuery.ParamByName('listParam').AsString := ListToText(['a','b','c']);
SQL принимает в качестве параметров только отдельные значения, поэтому вы не можете создать оператор с одним параметром, который может быть сопоставлен с переменным числом значений, например, в приведенном вами примере.
Однако в этой ситуации вы все равно можете использовать параметризованный SQL. Решение состоит в том, чтобы перебрать список имеющихся у вас значений, добавив маркер параметра в SQL и параметр в список параметров для каждого значения.
Это проще всего сделать с позиционными, а не именованными параметрами, но также можно адаптировать для именованных параметров (вам может потребоваться настроить этот код, поскольку у меня нет Delphi и я не помню синтаксис создания параметра):
//AValues is an array of variant values
//SQLCommand is some TDataSet component with Parameters.
for I := Low(AValues) to High(AValues) do
begin
if ParamString = '' then
ParamString = '?'
else
ParamString = ParamString + ', ?';
SQLCommand.Parameters.Add(AValues[I]);
end
SQLCommand.CommandText =
'SELECT * FROM MyTable WHERE KeyValue IN (' + ParamString + ')';
Это создаст безопасный для инъекций параметризованный запрос.
Parameters.Add
Error: User Rate Limit Exceeded
ьзуйте эту таблицу как часть подзапроса.
Например, создайте MyListTable в вашей базе данных. Вставьте ваши значения в MyListTable. Тогда делай
select * from myTable where keyvalue in (select keyvalue from MyListTable)
Это позволяет избежать атак с использованием SQL-инъекций. Но это не элегантно, не снижает производительность, потому что вы должны вставлять записи перед выполнением запроса, и может привести к проблемам параллелизма.
Не мой первый выбор, чтобы справиться с вашей ситуацией, но он решает вашу проблему с инъекцией SQL.
Я использую некоторые "IN" замена. Вот запрос, который я использую:
SELECT * FROM MyTable WHERE CHARINDEX(','+cast(intKey as varchar(10))+',', :listParam) > 0
код для отправки параметра:
MyQuery.ParamByName('listParam').AsString := ',1,2,3,';
Значение элемента массива может частично соответствовать некоторым другим значениям. Например, "1" может быть частью "100". Чтобы защититься от этого, я использую запятую в качестве разделителя
Почему бы не сделать динамический sql:
Быстро и грязно, но все еще используя параметры. проверьте 10 элементов. Я не знаю, насколько хорошо это масштабируется.
MyQuerySQL.Text:='SELECT * FROM myTable WHERE intKey in (:listParam0'
for i := 1 to 9 do begin
MyQuerySQL.Text := MyQuerySQL.Text + ',:listParam'+IntToStr(i)
end;
MyQuerySQL.Text := MyQuerySQL.Text+')';
for i:=0 to 9 do begin
MyQuery.ParamByName('listParam'+IntToStr(i)).AsInteger := ArrayofInt[0];
end;