【发布时间】:2011-04-10 15:47:08
【问题描述】:
我们来张桌子:
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 跟踪显示第二个占位符已绑定到数组,但没有得分。如果我在一个字符串中join 数组值并传递它,则没有结果。如果我展平数组,我会得到“使用 N 个占位符而不是 2 调用”的可预测错误。
我有点不知所措。还有什么可以尝试的?
更新:好的,这是一个来自真实世界应用程序的真实示例。
首先,设置:我有几张表填充了统计数据,列数从 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 引擎之前很久就没有通过正则表达式模式受到污染。我不想动态重写查询有两个原因:a)它可能会导致价值引用问题和b)它有点杀死prepare_cached背后的全部原因。如果每次都改变,SQL引擎不能缓存重用准备好的语句。
正如我所说,上面的代码在 Postgres 上运行良好。由于 SQLite 引擎本身显然有处理数据集的可能性,我认为这是 DBD::SQLite 实现的缺陷。所以真正的问题听起来像:有什么方法可以使用 DBD::SQLite 在占位符中传递数据集?不一定是数组,但这是最合乎逻辑的。
【问题讨论】:
-
或许您应该发布一个实际示例,说明您从应用程序/库中获取的数据,而不是发布虚构的代码。
-
-1 这个问题质量低
标签: arrays perl sqlite placeholder