Delphi:如何将列表作为参数传递给SQL查询?

时间:2012-04-10 15:35:18

标签: sql sql-server delphi delphi-7

我有一个整数或字符串列表,需要将它作为Delphi DataSet的参数传递。怎么做?

这是一个例子。 MyQuery类似于:

select * from myTable where intKey in :listParam

我将参数设置为列表或数组或其他:

MyQuery.ParamByName('listParam').AsSomething := [1,2,3];

并且会导致将此查询发送到sql server:

select * from myTable where intKey in (1, 2, 3)

如果解决方案也可以使用字符串,那么这个查询会更好:

select * from myTable where stringKey in :listParam

成为:

select * from myTable where stringKey in ('a', 'b', 'c')

我认为这是一个简单的问题,但“IN”不是搜索网络的好关键字。

请回答我应该如何配置IDE中的参数,查询以及如何传递参数。

我正在使用Delphi 7。

已编辑:我正在考虑答案是“无法直接执行”。如果有人给我一个非hackish的答案,接受的答案将会改变。

7 个答案:

答案 0 :(得分:11)

AFAIK,直接无法实现。

您必须将列表转换为纯文本的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']);

答案 1 :(得分: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 + ')';

这将产生注入安全的参数化查询。

答案 2 :(得分: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)

您可以使用split function

而不是使用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';

答案 3 :(得分:1)

创建临时表并在其中插入值。然后使用该表作为子查询的一部分。

例如,在数据库中创建MyListTable。将值插入MyListTable。然后做

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

这可以避免SQL注入攻击。但它不优雅,不具备性能,因为您必须在运行查询之前插入记录,并且可能导致并发问题。

不是我处理你的情况的第一选择,但它解决了你对sql注入的担忧。

答案 4 :(得分:1)

如果有人仍有同样的问题,如果你使用的是fedac,你可以使用这样的宏:

查询 - > "select * from myTable where intKey in (&listParam)"

设置宏 - > MyQuery.MacroByName('listParam').AsRaw := '1, 2, 3';

答案 5 :(得分:0)

我使用了一些“IN”替代品。这是我使用的查询:

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

发送参数的代码:

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

数组项值可以部分匹配其他一些值。例如,“1”可以是“100”的一部分。为了防止它,我使用逗号作为分隔符

答案 6 :(得分: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;