ошибка perl при передаче значений DBI-> execute для предложения IN

#perl #dbi

#perl #dbi

Вопрос:

У меня есть запрос для вычисления объектов, которые находятся в пределах определенного радиуса точки, на основе документа здесь: http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc /

Это работает очень хорошо, однако я хочу искать только те объекты, которые имеют определенный тип, и это вызывает проблему;

Код выглядит следующим образом:

 my $sql = "SELECT *
 FROM (
 SELECT b.*, pr.postcode, pr.prize, pr.title, pr.collection, pr.redeemed, pr.delivery, pr.archived, bt.category,
        p.radius,
        p.distance_unit
                 * DEGREES(ACOS(COS(RADIANS(p.latpoint))
                 * COS(RADIANS(b.lat))
                 * COS(RADIANS(p.longpoint - b.lng))
                   SIN(RADIANS(p.latpoint))
                 * SIN(RADIANS(b.lat)))) AS distance
  FROM bubbles AS b, bubble_prizes AS pr, bubble_types AS bt
  JOIN (   /* these are the query parameters */
        SELECT  ?  AS latpoint, ? AS longpoint,
                ? AS radius,      ? AS distance_unit
    ) AS p
  WHERE b.lat
     BETWEEN p.latpoint  - (p.radius / p.distance_unit)
         AND p.latpoint    (p.radius / p.distance_unit)
    AND b.lng
     BETWEEN p.longpoint - (p.radius / (p.distance_unit * COS(RADIANS(p.latpoint))))
         AND p.longpoint   (p.radius / (p.distance_unit * COS(RADIANS(p.latpoint))))
    AND pr.bubble = b.id
    AND b.type IN ?
    AND b.type = bt.type
 ) AS d
 WHERE distance <= radius
 ORDER BY distance";    
 

Затем я делаю

 my $points = $y->dbh->prepare($sql);
$results  = $points->execute($lat, $lng, $rad, $units, '(type1, type2)');
 

где ‘(type1, type2)’ должен быть передан

 b.type IN ?
 

(который находится в нижней части SQL).

Я перепробовал все возможные способы экранирования этой строки, чтобы она работала (включая множество способов, которые явно безумны, но я в отчаянии), включая

 '(type1, type2)'
'('type1', 'type2')'
'('type1', 'type2')'
"('type1', 'type2')"
 

и т.д. (Я перепробовал так много вещей, что даже не могу вспомнить их все.)

Независимо от того, что я пытаюсь, я получаю ошибку SQL вида

 DBD::mysql::st execute failed: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''(type1, type2)'
    AND b.type = bt.type
 ) AS d
 WHERE distance <= radius'
 

В зависимости от того, как я пытался экранировать строку, сообщение об ошибке немного отличается, но всегда относится к одной и той же части sql.

Теперь я думаю, что побег — это не моя проблема, и я что-то упускаю из виду в выполнении. Если я запускаю код в БД, он отлично работает с обычным оператором IN, т.е. b.type IN (‘type1’, ‘type2’) работает нормально.

Может кто-нибудь просветить меня? Как я должен это сделать?

Спасибо

Ответ №1:

Вам нужно будет использовать заполнители внутри IN (...) инструкции. Весь смысл execute() в том, чтобы избежать внедрения SQL, и вы в основном пытаетесь внедрить туда SQL. Вы можете создать динамический список заполнителей следующим образом:

 my @types = qw(type1 type2);
my $placeholders = join ", ", ("?") x @types;
my $sql = "...
        b.typeID IN ($placeholders)
    ...";
my $points = $y->dbh->prepare($sql);
$results  = $points->execute($lat, $lng, $rad, $units, @types);