2012-04-10 40 views
12

私は整数または文字列のリストを持っており、それをDelphi DataSetのパラメータとして渡す必要があります。どうやってするの?Delphi:リストをパラメータとしてSQLクエリに渡す方法は?

ここは例です。 MYQUERYのようなものです:

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

をし、それは、SQL Serverに送信され、このクエリにつながる:

select * from myTable where intKey in :listParam 

私は、リストや配列または何か他のものなどのパラメータを設定したい

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でのパラメータの設定方法、クエリ、およびパラメータの受け渡し方法をお答えください。

私が編集したデルファイ7

を使用しています:私は答えを検討している「直接を行うことはできません」です。誰かが私にハックレス以外の回答を与えると、受け入れられた回答が変更されます。

+3

することができます - マクロの設定]> ["select * from myTable where intKey in (&listParam)"

を残念ながら、これはSQL言語の欠点です。「リスト型」という概念はありません。 –

+0

使用するDBMSによっては、いくつかのオプションがあるかもしれません。あなたは何を使っていますか? SQL Server、Oracle、....? –

+0

@MikaelEriksson:Sql Serverを使用していますが、Delphi言語の問題だと思います。 – neves

答えて

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

確かに、これは私の現在の解決策ですが、SQLプロパティを変更する代わりにパラメータとして渡したいと思います。連結することで、DBMSはクエリを準備することができず、SQLインジェクション攻撃も可能になります。 – neves

+1

パラメータ化は引き続き可能です(ただし、SQLプロパティを更新する必要はあります)。下の私の長年の答えを見てください。 –

+3

@neves上記の2つの関数は、SQLインジェクション攻撃を許可しません。これは、最初の文字が指定されたテキストを引用し、2番目の文字が整数から値を作成するためです。現代のデータベース(少なくとも私が知っているオラクルのデータベース)では、遅くなることはありません。キーに適切なインデックスがあることを確認してください。内部的には、DBはSQL文を準備し、その実行計画の1つのパラメータとして "in()"式全体を再利用します。それはSQLであり、あなたが望むものをコード化し、DBで検索する方法ではありません。 –

1

は一時テーブルを作成し、その中で自分の価値観を挿入します。その後、そのテーブルをサブクエリの一部として使用します。

たとえば、データベースにMyListTableを作成します。 MyListTableに値を挿入します。次にdo

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」の一部とすることができる。これを防ぐために、カンマを区切り文字として使用します。

4

SQLはパラメータとして単一の値しか受け入れないため、与えられた例のように、可変数の値にマップできる1つのパラメータで文を作成することはできません。

ただし、この状況でパラメータ化された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 + ')'; 

これは、注入安全なパラメータ化クエリを生成します。

+0

誰かが 'Parameters.Add'を使っているのを初めて見ました。 CommandTextを割り当てないと、それらが上書きされますか? – nurettin

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) 

複数のステートメントクエリを持つことは可能です。

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

あなたは、文字列に同じ手法を使用することができます。パラメータ:listParamは文字列でなければなりません。 IDのデータタイプをたとえばvarchar(10)に変更するだけで済みます。

代わりにwhileループで開梱のあなたは、文字列のparamは次のようになります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) 

を利用することができます

動的SQLにしないのはなぜ
MyQuery.ParamByName('listParam').AsString := 'Adam,Bertil,Caesar'; 
+0

...あなたの文字列のうちの1つがその内容(可能性が高い)内に '、'文字を持つ場合、動作しません。 –

+0

@ArnaudBouchez - コンマはこの解決策の要件ではありません。 *あなたの*選択の任意の文字にすることができます。区切り文字を独自のパラメータとして追加することもできます。デリミタを特定できない非常に多様なデータがある場合は、TSQLコードのテーブルに分割する代わりにXML文字列を設定することができます。 –

+0

@ArnaudBouchez - XML版も追加すれば興味がありますか? –

0

クイックで汚れていますが、引き続きパラメーターを使用しています。 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; 
1

あなたは、このようなマクロを使用することができますfiredac使用している場合、誰かがまだ、同じ問題を抱えている場合:

クエリ - >MyQuery.MacroByName('listParam').AsRaw := '1, 2, 3';

関連する問題