의 테이블을 보자 : DBD :: SQLite, 자리 표시자를 통해 쿼리에서 배열을 전달하는 방법?
sqlite> create table foo (foo int, bar int);
sqlite> insert into foo (foo, bar) values (1,1);
sqlite> insert into foo (foo, bar) values (1,2);
sqlite> insert into foo (foo, bar) values (1,3);
그런 다음 몇 가지 데이터를 선택 :
sqlite> select * from foo where foo = 1 and bar in (1,2,3);
1|1
1|2
1|3
괜찮아 작동합니다. 이제 DBD :: SQLite 1.29 사용하려고합니다 :
my $sth = $dbh->prepare('select * from foo where foo = $1 and bar in ($2)');
$sth->execute(1,[1,2,3]);
그리고이 날 결과가 제공됩니다. DBI 추적은 제 2 자리 표시자가 배열에 올바르게 바인딩되지만 점수는 없음을 보여줍니다. 문자열에서 배열 값을 join
으로 지정하고 전달하면 아무런 결과가 없습니다. 배열을 평평하게하면 "2 대신 N 자리 표시 자로 불린다"라는 예측 오류가 발생합니다.
나는 약간의 상실감이있다. 시도 할 다른 무엇이 있습니까?
업데이트 : 좋아, 여기 실제 적용에서 가져온 한 가지 좋은 예가 있습니다.
첫 번째 설정 : 통계 데이터가 채워진 여러 테이블이 있으며 열 수는 10 개에서 700 개까지입니다. 내가 말하는 쿼리는보고 용으로 해당 데이터의 하위 집합을 선택합니다. 서로 다른 보고서는 서로 다른 측면을 고려하므로 하나의 요청마다 다른 쿼리를 실행합니다. 200 개 이상의 보고서, 즉 200-300 개의 검색어가 있습니다. 이 접근법은 Postgres를 위해 개발되었으며 이제는 그것을 축소하여 SQLite와 함께 사용할 필요가 있습니다. 이 모든 것이 Postgres와 잘 작동한다고 생각하면 모든 쿼리를 검토하고 다시 작성하는 것을 정당화 할 수 없습니다. 유지 관리에 좋지 않습니다. ANY()를 IN()으로 바꾸는 것과 같은 내부 쿼리 조정을 할 수 있고 할 수 있습니다. 이것은 사소한 측면입니다.
그래서, 여기 내 예입니다 거기에 사용되는 입력 매개 변수의 숫자와 다른 장소에서 다시 사용할 수
SELECT SPLIT, syn(SPLIT),
(SELECT COUNT(*) FROM cagent WHERE ACD = $1 AND SPLIT = $2 AND
LOC_ID = ANY ($3) AND LOGID IS NOT NULL AND WORKMODE = 40),
(SELECT COUNT(*) FROM cagent WHERE ACD = $1 AND SPLIT = $2 AND
LOC_ID = ANY ($3) AND LOGID IS NOT NULL AND WORKMODE = 30),
(SELECT COUNT(*) FROM cagent WHERE ACD = $1 AND SPLIT = $2 AND
LOC_ID = ANY ($3) AND LOGID IS NOT NULL AND WORKMODE = 50),
(SELECT COUNT(*) FROM cagent WHERE ACD = $1 AND SPLIT = $2 AND
LOC_ID = ANY ($3) AND LOGID IS NOT NULL AND WORKMODE = 220),
(SELECT COUNT(*) FROM cagent WHERE ACD = $1 AND SPLIT = $2 AND
LOC_ID = ANY ($3) AND LOGID IS NOT NULL),
(SELECT COUNT(*) FROM cagent WHERE ACD = $1 AND SPLIT = $2 AND
LOC_ID = ANY ($3) AND LOGID IS NOT NULL AND WORKMODE = 20),
(SELECT COUNT(*) FROM cagent WHERE ACD = $1 AND SPLIT = $2 AND
LOC_ID = ANY ($3) AND LOGID IS NOT NULL AND WORKMODE = 80)
FROM csplit WHERE ACD = $1 AND SPLIT = $2
SELECT syn(LOGID), syn(LOC_ID), LOGID, EXTENSION, syn(ROLE), PERCENT,
syn(AUXREASON), syn(AWORKMODE), syn(DIRECTION), WORKSKILL, syn(WORKSKLEVEL),
AGTIME FROM cagent WHERE ACD = $1 AND SPLIT = $2 AND LOC_ID = ANY ($3) AND
LOGID IS NOT NULL
이 가장 복잡한 예제되지 않습니다 :이 쿼리를 하나 개의 보고서에 대해 연속적으로 실행 질문; 일반 ?
자리 표시 자로 대체하는 것은 간단한 작업이 아닙니다. 포스트 그레스에 대해 쿼리를 실행하는 코드처럼 보이는 (입력 클렌징 등은 후) :
sub run_select {
my ($class, $dbh, $sql, @bind_values) = @_;
my $sth;
eval {
$sth = $dbh->prepare_cached($sql);
$sth->execute(@bind_values);
};
[email protected] and die "Error executing query: [email protected]";
my %types;
{
my $dbt = $dbh->type_info_all;
@types{ map { $_->[1] } @$dbt[1..$#$dbt] } =
map { $_->[0] } @$dbt[1..$#$dbt];
};
my @result;
while (my $row = $sth->fetchrow_arrayref) {
my $i = 0;
push @result, [ map { [ $types{${$sth->{TYPE}}[$i++]}, $_ ] } @$row ];
};
return \@result;
};
내가 쿼리를 다시 작성하고 값을 직접 삽입 할 수; SQL 인젝션은 SQL 엔진을 치기 훨씬 전에 모든 입력이 정규식 패턴을 통해 유지되기 때문에 별다른 위협이 아닙니다. 필자는 두 가지 이유로 동적으로 쿼리를 다시 작성하고 싶지 않습니다. a) 잠재적으로 값 인용과 관련된 문제를 야기 할 수 있고 b) prepare_cached의 모든 원인을 제거합니다. SQL 엔진은 매번 변경 될 경우 미리 준비된 명령문을 캐시 할 수 없습니다.
이제 내가 말했듯이 위의 코드는 Postgres와 잘 작동합니다. SQLite 엔진 자체가 분명히 데이터 세트로 작업 할 가능성이 있기 때문에 DBite :: SQLite 구현의 결함이라고 생각했습니다. 따라서 실제 질문은 다음과 같습니다. DBD :: SQLite를 사용하여 자리 표시 자에 데이터 세트를 전달하는 방법이 있습니까? 가장 논리적 인 배열 일 필요는 없습니다.
작성한 코드를 게시하는 대신 실제 사례를 게시해야합니다. 응용 프로그램/라이브러리에서 가져 오는 데이터. – MkV
-1이 질문은 품질이 낮습니다 –