Perl DBI и заполнители

У меня есть этот запрос select * from table where ID in (1,2,3,5...)

Как можно построить этот запрос с помощью DBI с помощью заполнителей?

например:

my @list = (1, 2, 3, 4, 5);
my $sql = "select * from table where ID in (?)";

$sth->prepare($sql);
$sth->execute();

Какой аргумент я должен отправить для выполнения? Это список или строка, разделенная на , или что-то еще?

Ответ 1

Это должно динамически строить запрос в соответствии с количеством элементов в вашем массиве

my @list =(1,2,3,4,5);
my $sql ="select * from table where ID in (@{[join',', ('?') x @list]})";

Ответ 2

Это невозможно. Вам нужно указать местозаполнитель для каждого элемента в вашем массиве:

my @list = (1,2,3,4,5);
my $sql = "select * from table where ID in (?,?,?,?,?)";

$sth->prepare($sql);
$sth->execute(@list);

Если ваш @list не является фиксированным, вам нужно построить $sql с соответствующим количеством заполнителей.

Ответ 3

Цитирование DBI документация:

Кроме того, заполнители могут представлять только одиночные скалярные значения. Например, следующий оператор не будет работать должным образом        для более чем одного значения:

     SELECT name, age FROM people WHERE name IN (?)    # wrong
     SELECT name, age FROM people WHERE name IN (?,?)  # two names

Перепишите на:

my $sql = 'select * from table where ID in ( ?, ?, ?, ?, ? )';
$sth->prepare($sql);
$sth->execute(@list);

Ответ 4

Если вы используете DBI для доступа к базе данных PostgreSQL с помощью драйвера DBD:: Pg, вы можете использовать:

my @list = (1, 2, 3, 4, 5);
my $sql = "select * from table where ID = ANY(?::INT[]);";

$sth->prepare ($sql);
$sth->execute (\@list);

Ответ 5

Если вы переключитесь на DBIx:: Simple, вы можете просто сказать:

$db->query('INSERT INTO foo VALUES (??)', $foo, $bar, $baz);

?? Значит "столько, сколько необходимо"

Edit:

На самом деле, я был слишком оптимистичен: "Если в запросе присутствует строка (??), она заменяется списком столько вопросительных знаков, как @values."

Таким образом, это не работает:

$db->query( "SELECT * FROM foo WHERE id IN (??) AND stuff=?", @ids, $stuff )

Еще полезно, но..

Для любопытных код в модуле:

# Replace (??) with (?, ?, ?, ...)
sub _replace_omniholder {
  my ($self, $query, $binds) = @_;
  return if $$query !~ /\(\?\?\)/;
  my $omniholders = 0;
  my $q = $self->{dbd} =~ /mysql/ ? $quoted_mysql : $quoted;
  $$query =~ s[($q|\(\?\?\))] {
    $1 eq '(??)'
    ? do {
        Carp::croak('There can be only one omniholder')
            if $omniholders++;
        '(' . join(', ', ('?') x @$binds) . ')'
    }
    : $1
  }eg;
}

Ответ 6

Если вы не знаете точное количество элементов, вы не можете использовать заполнители. Попробуйте следующее:

my @list = (1, 2, 3, 4, 5);  # any number of elements
my $in = join(',', map { $dbh->quote($_) } @list);
my $sql = "select * from table where someid IN ($in)";

Ответ 7

Я нашел верный способ для этого обобщить все приведенные выше советы. Мой запрос на производство (здесь я опубликовал гораздо более простую версию) использует IN < > , где ни коды, ни их количество неизвестны. Это может быть один код (например, FIN) или их серия (FITLC FITLD FITLU FITSC FITSD FITSU MDYLC MDYLD MDYLU). Некоторая функция возвращает это как список.

Код, который делает это,

            @codes =  get_muni_evcode( $category );
            my $in = join( ', ', ('?') x @codes );
            print "\n\nProcessing Category: $category --> Codes: @codes   .. in: $in\n";

            my $sql = "select distinct cusip9 
            from material_event 
            where event_date between (trunc(sysdate) - 1) + 2/3 and trunc(sysdate) + 2/3 
            and event_code in ($in)";
            my $sth2 = $dbh->prepare($sql);
            $sth2->execute( @codes );

            while (my $s2 = $sth2->fetchrow_hashref('NAME_lc'))
            {
                    my $cusip9 = $s2->{cusip9};
                    print "$cusip9\t";
                   .................. further processing ..............

            }

Образец результата:

Processing Category: RatingChange --> Codes: FITLC FITLD FITLU FITSC FITSD FITSU MDYLC MDYLD MDYLU MDYSC MDYSD MDYSU SPLD SPLPR SPLU SPSD SPSPR SPSU .. in: ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? 359496HQ2 359496GB6 359496GH3 359496GL4 359496HU3 359496HS8 359496HA7 359496HF6 359496GM2 359496HM1 359496HR0 359496HT6 359496GY6 359496GJ9 359496HL3 359496GU4 359496HK5 359496HN9 359496HP4 359496GW0 359496GZ3 359496HC3 359496GC4 359496GK6 359496GP5 359496GV2 359496GX8 359496GN0

Я очень благодарен всем, кто разместил свои идеи здесь, что, наконец, заставило меня найти правильный способ сделать это. Я думаю, что это довольно распространенная проблема.