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-&gt;execute((@ip_addresses)x2)(括号在这里很奇怪) @Grinnz:我在想你真正需要的是像WHERE (***_primary_ip OR ***_secondary_ip) IN (LIST) 这样的语法。但我从未在任何版本的 SQL 中看到过类似的情况。 您实际上可以使用 PostgreSQL 数组重叠运算符来执行此操作,我相信它看起来类似于 WHERE ARRAY[***_primary_ip, ***_secondary_ip] &amp;&amp; ?,将 \@ip_addresses 作为单个参数传递。不知道它会被优化到什么程度。【参考方案2】:

其他可能的解决方案是在$sth-&gt;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 绑定变量调用)的主要内容,如果未能解决你的问题,请参考以下文章

Perl 执行 DBI 循环执行

Perl DBI:如何使用绑定值查看失败的查询?

解释 Perl DBI MySQL column_info()

Perl DBI - 使用多个语句运行 SQL 脚本

Perl DBI 语句句柄和错误处理

当 Perl 的 DBI 在准备语句时遇到错误时,如何避免程序退出?