Perl DBI: неравномерное число переменных связывания с оператором OR (вызывается с переменными связывания x, когда требуется y) - PullRequest
0 голосов
/ 02 марта 2020

Определение задачи : выборка данных из двух разных столбцов с использованием ИЛИ .

Проблема : при работе с равниной (MySQL) запрос, Perl DBI генерирует исключение из-за неравномерного числа bind переменных .

Давайте предположим следующую схему БД:

customer    vpn_primary_ip   vpn_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-адрес, непредсказуем, я объединяю поиск по столбцам vpn_primary_ip AND vpn_secondary_ip используя оператор ИЛИ . Запрос plain SQL выглядит следующим образом:

SELECT
     customer,
     vpn_primary_ip,
     vpn_secondary_ip,
 FROM
     table
 WHERE
     vpn_primary_ip IN ( '1.1.1.1', '4.4.4.4', '5.5.5.5', '6.6.6.6' )
 OR  
     vpn_secondary_ip IN ( '1.1.1.1', '4.4.4.4', '5.5.5.5', '6.6.6.6' );

Приведенный выше запрос дает следующий (соответствующий) результат:

+----------+-----------------+------------------+
| customer | vpn_primary_ip  | vpn_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          |
+----------+-----------------+------------------+

То же самое SQL запрос с Perl DBI:

 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,
       vpn_primary_ip,
       vpn_secondary_ip,
     FROM
       table
     WHERE
       vpn_primary_ip IN ( @{[join',', ('?') x @ip_addresses]} )
     OR  
       vpn_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_vpn_customers line 211, <DATA> line 1.
DBD::mysql::st execute failed: called with 4 bind variables when 8 are needed at get_vpn_customers line 211, <DATA> line 1.

Единственная идея, чтобы заставить его работать, это передать @ip_addresses в execute метод дважды :

$sth->execute(@ip_addresses, @ip_addresses);

Вопрос : Это правильный подход или есть другой, скажем, лучший или лучшая практика ?

Ответы [ 2 ]

3 голосов
/ 02 марта 2020
$sth->execute(@ip_addresses, @ip_addresses);

Это правильный подход. Все, что знает DBI, - это то, что вы передали ему запрос SQL, содержащий восемь точек привязки. Поэтому ему требуется восемь соответствующих значений, переданных методу execute().

У Perl, DBI или MySQL нет способа узнать, что значения привязки повторяются.

1 голос
/ 02 марта 2020

Другое возможное решение - массаж SQL запрос в рабочее состояние до $sth->execute()

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,
       vpn_primary_ip,
       vpn_secondary_ip,
     FROM
       table
     WHERE
       vpn_primary_ip IN ( @{[join',', ('?') x @ip_addresses]} )
     OR  
       vpn_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,
       vpn_primary_ip,
       vpn_secondary_ip,
     FROM
       table
     WHERE
       vpn_primary_ip IN ( $ip_addresses )
     OR  
       vpn_secondary_ip IN ( $ip_addresses )
    ";

say $query;

Вывод

SELECT
   customer,
   vpn_primary_ip,
   vpn_secondary_ip,
 FROM
   table
 WHERE
   vpn_primary_ip IN ( ?,?,?,? )
 OR
   vpn_secondary_ip IN ( ?,?,?,? )


SELECT
   customer,
   vpn_primary_ip,
   vpn_secondary_ip,
 FROM
   table
 WHERE
   vpn_primary_ip IN ( '1.1.1.1', '4.4.4.4', '5.5.5.5', '6.6.6.6' )
 OR
   vpn_secondary_ip IN ( '1.1.1.1', '4.4.4.4', '5.5.5.5', '6.6.6.6' )
...