Давайте иметь таблицу:
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
массив значений в строке и передать его, нет результата.Если я сглаживаю массив, я получаю предсказуемую ошибку «вызывается с N заполнителями вместо 2».
Я вроде как в растерянности.Что еще можно попробовать?
Upd: Хорошо, вот один добросовестный пример, взятый из реального приложения.
Во-первых, установка: у меня есть несколькоТаблицы заполнены статистическими данными, количество столбцов варьируется от 10 до 700+.Запросы, о которых я говорю, выбирают подмножество этих данных для целей отчетности.Разные отчеты учитывают разные аспекты и, следовательно, запускают разные запросы, по одному или более на запрос.Есть более 200 отчетов, то есть 200-300 запросов.Этот подход был разработан для Postgres, и теперь мне нужно уменьшить его и заставить работать с SQLite.Учитывая, что все это хорошо работает с Postgres, я не могу оправдать просмотр всех запросов и их переписывание.Плохо для обслуживания.Я могу и действительно использовать корректировки запросов на месте, такие как замена = ANY () на IN (), это второстепенные аспекты.
Итак, вот мой пример: 2 запроса выполнялись подряд для одного отчета:
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
Это не самый сложный пример, так как может быть любое количество входных параметров, используемых и повторно используемых в разных местах в запросе;замена их на общие ?
заполнители не является тривиальной задачей.Код, который выполняет запросы к Postgres, выглядит так (после очистки ввода и др.):
sub run_select {
my ($class, $dbh, $sql, @bind_values) = @_;
my $sth;
eval {
$sth = $dbh->prepare_cached($sql);
$sth->execute(@bind_values);
};
$@ and die "Error executing query: $@";
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.Я не хочу переписывать запросы динамически по двум причинам: а) это может потенциально привести к проблемам с цитированием значения и б) это как бы убивает всю причину, что стоит за prepare_cached.Движок SQL не может кэшировать повторно используемое подготовленное утверждение, если оно меняется каждый раз.
Теперь, как я уже сказал, приведенный выше код хорошо работает с Postgres.Поскольку сам движок SQLite, очевидно, имеет возможность работать с наборами данных, я подумал, что это недостаток в реализации DBD :: SQLite.Таким образом, реальный вопрос звучит так: есть ли способ передать набор данных в заполнитель с помощью DBD :: SQLite?Не обязательно массив, хотя это было бы наиболее логичным.