#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);