[英]Delphi: how to pass a list as a parameter to a SQL query?
我有一个整数或字符串列表,需要将其作为 Delphi 数据集的参数传递。 怎么做?
这是一个例子。 MyQuery 是这样的:
select * from myTable where intKey in :listParam
我会将参数设置为列表或数组或其他内容:
MyQuery.ParamByName('listParam').AsSomething := [1,2,3];
它将导致此查询发送到 sql 服务器:
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”不是搜索 web 的好关键字。
请回答我应该如何配置IDE中的参数,查询以及如何传递参数。
我正在使用 Delphi 7。
编辑:我正在考虑答案是“不可能直接做”。 如果有人给我一个非 hackish 的答案,接受的答案将被更改。
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']);
SQL 仅接受单个值作为参数,因此您不能使用一个参数创建一个语句,该参数可以 map 为可变数量的值,例如您给出的示例。
但是,您仍然可以在这种情况下使用参数化的 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 + ')';
这将产生一个注入安全的参数化查询。
您有多种选择,但基本上您需要将您的值放入表格中。 我会为此建议一个表变量。
这是一个解压 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)
。
您可以使用拆分 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';
创建一个临时表并在其中插入您的值。 然后将该表用作子查询的一部分。
例如,在您的数据库中创建 MyListTable。 将您的值插入 MyListTable。 然后做
select * from myTable where keyvalue in (select keyvalue from MyListTable)
这避免了 SQL 注入攻击。 但它并不优雅,对性能也不友好,因为您必须在运行查询之前插入记录,并且可能导致并发问题。
不是我处理您的情况的首选,但它解决了您对 sql 注入的担忧。
如果有人仍然遇到同样的问题,如果您使用的是 firedac,则可以使用这样的宏:
查询 -> "select * from myTable where intKey in (&listParam)"
设置宏 -> MyQuery.MacroByName('listParam').AsRaw:= '1, 2, 3';
我使用一些“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;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.