Delphi: how to pass a list as a parameter to a SQL query?

前端 未结 7 732
你的背包
你的背包 2020-12-17 16:44

I have a list of integers or of strings and need to pass it as a parameter for a Delphi DataSet. How to do it?

Here is an example. MyQuery is something like:

相关标签:
7条回答
  • 2020-12-17 16:49

    I use some "IN" replacement. Here is the query I use:

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

    the code to send parameter:

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

    The array item value can partially match some other values. For instance, "1" can be part of "100". To protect against it, I use comma as delimiter

    0 讨论(0)
  • 2020-12-17 16:51

    Why not make a dynamic sql:

    Quick and dirty, but still using parameters. check 10 elements. I don't know how well this scales.

        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;
    
    0 讨论(0)
  • 2020-12-17 16:54

    Create a temporary table and insert your values in it. Then use that table as part of a subquery.

    For example, create MyListTable in your database. Insert your values into MyListTable. Then do

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

    This avoids SQL injection attacks. But it's not elegant, is not performance friendly because you have to insert records before running your query, and can lead to concurrency issues.

    Not my first choice to deal with your situation but it addresses your concern about sql injection.

    0 讨论(0)
  • 2020-12-17 16:55

    There are several options for you but basically you need to get your values into a table. I would suggest a table variable for that.

    Here is a version that unpacks an int list.

    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)
    

    It is possible to have multi-statement queries. The parameter :listParam should be a string:

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

    You can use the same technique for strings. You just need to change the data type of ID to for instance varchar(10).

    Instead of unpacking with a while loop you could make use of a split function

    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)
    

    A string param could look like this:

    MyQuery.ParamByName('listParam').AsString := 'Adam,Bertil,Caesar';
    
    0 讨论(0)
  • 2020-12-17 16:58

    AFAIK, it is not possible directly.

    You'll have to convert the list into a SQL list in plain text.

    For instance:

    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;
    

    To be used as such:

    SQL.Text := 'select * from myTable where intKey in '+ListToText([1,2,3]);
    SQL.Text := 'select * from myTable where stringKey in '+ListToText(['a','b','c']);
    
    0 讨论(0)
  • 2020-12-17 17:01

    If someone still having the same problem, if you are using firedac you can use macros like this:

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

    Setting the macro -> MyQuery.MacroByName('listParam').AsRaw := '1, 2, 3';

    0 讨论(0)
提交回复
热议问题