2012-04-10 2 views
12

정수 나 문자열 목록이있어서이를 Delphi DataSet의 매개 변수로 전달해야합니다. 그것을하는 방법?Delphi : 목록을 SQL 쿼리에 매개 변수로 전달하는 방법?

다음은 예입니다.

select * from myTable where intKey in :listParam 

나는 목록 또는 배열 또는 뭔가 다른과 같은 매개 변수를 설정 한 것 :

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

을하고는 SQL 서버로 전송이 쿼리가 발생할 것 :

을 MyQuery 뭔가처럼
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을 사용하고

: 나는 대답을 고려하고있어 "직접적을 할 수 없습니다 "입니다. 다른 사람이 비 hackish 답을 주면, 받아 들인 답이 바뀔 것입니다.

+3

당신이 할 수있는 불행히도. 이것은 SQL 언어의 결함입니다 : "목록 유형"의 개념이 없습니다. –

+0

사용하는 DBMS에 따라 몇 가지 옵션이있을 수 있습니다. 뭐라구? SQL 서버, 오라클, ....? –

+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 위의 두 함수는 SQL 주입 공격을 허용하지 않습니다. 첫 번째 함수는 제공된 텍스트를 "인용"하고 두 번째 함수는 정수 값을 만듭니다. 현대 데이터베이스 (적어도 내가 아는 오라클)를 사용하면 느려지지 않을 것입니다. 단지 키에 대해 적절한 색인을 가지고 있는지 확인하십시오. 내부적으로 DB는 SQL 문을 준비하고 실행 계획에서 하나의 매개 변수로 전체 "in()"식을 다시 사용합니다. SQL : 원하는대로 코드를 작성하고 DB에서 검색 할 방법을 지정하는 것이 아닙니다. –

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"의 일부일 수 있습니다. 이를 방지하기 위해 구분 기호로 쉼표를 사용합니다.

4

SQL은 하나의 값만 매개 변수로 받아들이므로 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 + ')'; 

이렇게하면 분사 안전 매개 변수화 된 쿼리가 생성됩니다.

+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

... 문자열 중 하나에 해당 내용 (해당되는 것)에 ','문자가 포함되어있는 경우 작동하지 않습니다. –

+0

@ArnaudBouchez -이 솔루션에는 쉼표가 필요하지 않습니다. * 귀하의 * 선택의 성격이 될 수 있습니다. 구분 기호를 자체 매개 변수로 추가 할 수도 있습니다. 구분 기호를 찾을 수없는 매우 다양한 데이터가있는 경우 TSQL 코드의 테이블에 파쇄하는 대신 XML 문자열을 설정할 수 있습니다. –

+0

@ArnaudBouchez - XML ​​버전을 추가해도 관심이 있습니까? –

0

:

빠르고 더럽지 만 여전히 매개 변수를 사용합니다. 요소를 확인하십시오. 이 비늘이 얼마나 잘 나는지 모르겠습니다.

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 사용하는 경우 다른 사람 여전히 같은 문제가, 당신은이 같은 매크로를 사용할 수있는 경우 :

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

매크로를 설정하기 ->MyQuery.MacroByName('listParam').AsRaw := '1, 2, 3';

관련 문제