Вопрос по sql-server, sql – Delphi: как передать список в качестве параметра в SQL-запрос?

12

У меня есть список целых чисел или строк, и мне нужно передать его в качестве параметра для 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 ;. Если кто-то даст мне нехакский ответ, принятый ответ будет изменен.

Это не проблема Delphi, а проблема SQL. Увидетьthis answer to a similar question для получения дополнительной информации и некоторых возможных обходных путей. afrazier
Там может быть несколько вариантов для вас в зависимости от используемой СУБД. Что вы используете? SQL Server, Oracle, ....? Mikael Eriksson
@MikaelEriksson: я использую Sql Server, но считаю, что это проблема языка Delphi. neves
Вы не можете, к сожалению. Это недостаток языка SQL: он не имеет никакого понятия «тип списка». Mason Wheeler

Ваш Ответ

7   ответов
1

если вы используете firedac, вы можете использовать макросы, подобные этому:

Запрос - & gt;"select * from myTable where intKey in (&listParam)"

Установка макроса - & gt;MyQuery.MacroByName('listParam').AsRaw := '1, 2, 3';

3

Есть несколько вариантов для вас, но в основном вам нужно поместить свои значения в таблицу. Я бы предложил переменную таблицы для этого.

Вот версия, которая распаковывает список 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';
Error: User Rate Limit ExceededyourError: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
11

Вам придется преобразовать список в список 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']);
Error: User Rate Limit Exceeded neves
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededMyQuery.ParamByName('listParam').AsString := ListToText(['a','b','c']);
4

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

Это создаст безопасный для инъекций параметризованный запрос.

Error: User Rate Limit ExceededParameters.AddError: User Rate Limit Exceeded
1

ьзуйте эту таблицу как часть подзапроса.

Например, создайте MyListTable в вашей базе данных. Вставьте ваши значения в MyListTable. Тогда делай

select * from myTable where keyvalue in (select keyvalue from MyListTable)

Это позволяет избежать атак с использованием SQL-инъекций. Но это не элегантно, не снижает производительность, потому что вы должны вставлять записи перед выполнением запроса, и может привести к проблемам параллелизма.

Не мой первый выбор, чтобы справиться с вашей ситуацией, но он решает вашу проблему с инъекцией SQL.

0

Я использую некоторые "IN" замена. Вот запрос, который я использую:

SELECT * FROM MyTable WHERE CHARINDEX(','+cast(intKey as varchar(10))+',', :listParam) > 0

код для отправки параметра:

MyQuery.ParamByName('listParam').AsString := ',1,2,3,';  

Значение элемента массива может частично соответствовать некоторым другим значениям. Например, "1" может быть частью "100". Чтобы защититься от этого, я использую запятую в качестве разделителя

0

Почему бы не сделать динамический 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;

Похожие вопросы