Perl DBI:带有 OR 语句的奇数绑定变量(需要 y 时使用 x 绑定变量调用)
Posted
技术标签:
【中文标题】Perl DBI:带有 OR 语句的奇数绑定变量(需要 y 时使用 x 绑定变量调用)【英文标题】:Perl DBI: uneven-number of bind variables with OR Statement (called with x bind variables when y are needed) 【发布时间】:2020-06-14 18:02:57 【问题描述】:任务定义: 使用 OR 从两个不同的列中获取数据。
问题: 在使用普通 (mysql) 查询时,Perl DBI 由于 bind variables 的奇数个数而引发异常。
让我们假设以下数据库架构:
customer ***_primary_ip ***_secondary_ip
1000 1.1.1.1 2.2.2.2
1001 3.3.3.3 NULL
1002 4.4.4.4 5.5.5.5
1003 NULL 6.6.6.6
旁注: 由于存储 ip 地址的列是不可预测的,我使用 OR 运算符组合搜索列 ***_primary_ip AND ***_secondary_ip。 普通 SQL查询如下:
SELECT
customer,
***_primary_ip,
***_secondary_ip,
FROM
table
WHERE
***_primary_ip IN ( '1.1.1.1', '4.4.4.4', '5.5.5.5', '6.6.6.6' )
OR
***_secondary_ip IN ( '1.1.1.1', '4.4.4.4', '5.5.5.5', '6.6.6.6' );
上面的查询给出了以下(适当的)结果:
+----------+-----------------+------------------+
| customer | ***_primary_ip | ***_secondary_ip |
+----------+-----------------+------------------+
| 1000 | 1.1.1.1 | 2.2.2.2 |
| 1002 | 4.4.4.4 | 5.5.5.5 |
| 1003 | NULL | 6.6.6.6 |
+----------+-----------------+------------------+
与 Perl DBI 相同的 SQL 查询:
my @ip_addresses = ('1.1.1.1', '4.4.4.4', '5.5.5.5', '6.6.6.6');
my $sth = $dbh->prepare (
"SELECT
customer,
***_primary_ip,
***_secondary_ip,
FROM
table
WHERE
***_primary_ip IN ( @[join',', ('?') x @ip_addresses] )
OR
***_secondary_ip IN ( @[join',', ('?') x @ip_addresses] )"
);
$sth->execute(@ip_addresses);
抛出以下异常:
DBD::mysql::st execute failed: called with 4 bind variables when 8 are needed at get_***_customers line 211, <DATA> line 1.
DBD::mysql::st execute failed: called with 4 bind variables when 8 are needed at get_***_customers line 211, <DATA> line 1.
使其工作的唯一方法是将@ip_addresses 传递给execute 方法两次:
$sth->execute(@ip_addresses, @ip_addresses);
问题: 这是正确的方法还是有其他方法,比如最佳或更好的做法?
【问题讨论】:
这是正确的方法。 Perl 无法知道您将同一组值传递了两次,因此它只希望在调用的参数中指定所有 8 个值。 【参考方案1】:$sth->execute(@ip_addresses, @ip_addresses);
这是正确的方法。所有 DBI 都知道您已经向它传递了一个包含八个绑定点的 SQL 查询。因此,它需要将八个匹配值传递给execute()
方法。
Perl、DBI 或 MySQL 无法知道绑定值是否重复。
【讨论】:
PostgreSQL 支持$1
、$2
形式的参数,因此您可以在查询中重复这些,并且只传递四个参数。遗憾的是,MySQL 无法实现。
你也可以写成$sth->execute((@ip_addresses)x2)
(括号在这里很奇怪)
@Grinnz:我在想你真正需要的是像WHERE (***_primary_ip OR ***_secondary_ip) IN (LIST)
这样的语法。但我从未在任何版本的 SQL 中看到过类似的情况。
您实际上可以使用 PostgreSQL 数组重叠运算符来执行此操作,我相信它看起来类似于 WHERE ARRAY[***_primary_ip, ***_secondary_ip] && ?
,将 \@ip_addresses
作为单个参数传递。不知道它会被优化到什么程度。【参考方案2】:
其他可能的解决方案是在$sth->execute()
之前按摩 SQL 查询到可工作状态
use strict;
use warnings;
use feature 'say';
my @ip_addresses = ('1.1.1.1', '4.4.4.4', '5.5.5.5', '6.6.6.6');
my $query = "
SELECT
customer,
***_primary_ip,
***_secondary_ip,
FROM
table
WHERE
***_primary_ip IN ( @[join',', ('?') x @ip_addresses] )
OR
***_secondary_ip IN ( @[join',', ('?') x @ip_addresses] )
";
say $query;
my $ip_addresses;
my $flag = 0;
for (@ip_addresses)
$ip_addresses .= ', ' if $flag;
$ip_addresses .= "'$_'";
$flag = 1;
$query = "
SELECT
customer,
***_primary_ip,
***_secondary_ip,
FROM
table
WHERE
***_primary_ip IN ( $ip_addresses )
OR
***_secondary_ip IN ( $ip_addresses )
";
say $query;
输出
SELECT
customer,
***_primary_ip,
***_secondary_ip,
FROM
table
WHERE
***_primary_ip IN ( ?,?,?,? )
OR
***_secondary_ip IN ( ?,?,?,? )
SELECT
customer,
***_primary_ip,
***_secondary_ip,
FROM
table
WHERE
***_primary_ip IN ( '1.1.1.1', '4.4.4.4', '5.5.5.5', '6.6.6.6' )
OR
***_secondary_ip IN ( '1.1.1.1', '4.4.4.4', '5.5.5.5', '6.6.6.6' )
【讨论】:
以上是关于Perl DBI:带有 OR 语句的奇数绑定变量(需要 y 时使用 x 绑定变量调用)的主要内容,如果未能解决你的问题,请参考以下文章