#php #pdo #prepared-statement
#php #pdo #подготовленный оператор
Вопрос:
У меня довольно часто возникают очень похожие SQL-запросы, которые я должен отправлять, например, удаление одной строки в таблице, идентификатор которой мне известен. Вот мой фрагмент кода с подготовленным оператором:
$stmt = $conn->prepare("DELETE FROM `".MY_FIRST_TABLE."` WHERE `id` = :id LIMIT 1");
$stmt->bindValue(':id', $id1, PDO::PARAM_INT);
$stmt->execute();
$stmt = $conn->prepare("DELETE FROM `".MY_SECOND_TABLE."` WHERE `id` = :id LIMIT 1");
$stmt->bindValue(':id', $id2, PDO::PARAM_INT);
$stmt->execute();
Я хотел бы сделать что-то вроде этого:
$stmt = $conn->prepare("DELETE FROM `:table` WHERE `id` = :id LIMIT 1");
$stmt->bindValue(':id', $id1, PDO::PARAM_INT);
$stmt->bindValue(':table', MY_FIRST_TABLE);
$stmt->execute();
$stmt->bindValue(':id', $id2, PDO::PARAM_INT);
$stmt->bindValue(':table', MY_SECOND_TABLE);
$stmt->execute();
Если я попробую это, ничего не произойдет. Итак, я использовал следующий фрагмент для анализа ошибки:
print_r($conn->errorInfo());
var_dump($stmt);
$stmt->debugDumpParams();
Я получил:
Array
(
[0] => 00000
[1] =>
[2] =>
)
object(PDOStatement)#4 (1) {
["queryString"]=>
string(45) "DELETE FROM `:table` WHERE `id` = :id LIMIT 1"
}
SQL: [45] DELETE FROM `:table` WHERE `id` = :id LIMIT 1
Params: 2
Key: Name: [3] :id
paramno=-1
name=[3] ":id"
is_param=1
param_type=1
Key: Name: [6] :table
paramno=-1
name=[6] ":table"
is_param=1
param_type=2
Возможно ли что-то подобное?
(В настоящее время я использую подготовленные инструкции только по соображениям безопасности, а не по соображениям производительности.)
Подготовленные операторы и IN-Clause
Я только что прочитал, что я не могу использовать подготовленные операторы для имен таблиц или столбцов (исходный код). Так что, я думаю, мне нужно искать другое решение.
Комментарии:
1. Что произойдет, если вы попытаетесь запустить это?
2. Ничего. Я добавил несколько строк, чтобы показать, что они дают вам как можно больше информации, которая у меня есть об этой «ошибке». Я думаю, что намеренно невозможно заменить имена таблиц, поскольку подготовленные операторы, похоже, сохраняются в базе данных для оптимизации… насколько я понял. Я предполагаю, что может быть сложно сделать это для каждой таблицы одновременно. Итак, я надеюсь, что получу альтернативу для подготовленных операторов или мне скажут, что я ошибаюсь в своих предположениях о них.
Ответ №1:
Я думаю (я не уверен в этом на 100%), что вы пытаетесь запросить таблицу с именем
`'TableName'`
Или
`"TableName"`
в зависимости от того, как PDO пытается указать ваши параметры. Я искал, как не заключать строки в кавычки в PDO (у меня была ваша та же проблема), но я давно отказался от этого и написал свою собственную реализацию.
Возможно, вы могли бы предварительно обработать запросы и заменить параметр именем таблицы, соблюдая осторожность с ошибками (это то, о чем я говорю), которые могут возникнуть при замене имени параметра таблицы.
Комментарии:
1. Спасибо за эту информацию. Я взглянул на запрос, который был получен MySQL-сервером:
SELECT user_password FROM 'chess_users' WHERE 1 = '1'
. Похоже, это причина, по которой я не могу сделать это с помощью подготовленных операторов. Еще одно интересное наблюдение: этот запрос был созданSELECT user_password FROM :table WHERE :uid = :uid
. Почему:uid заменяется один раз на 1 и один раз на ‘1’?2. Я не уверен в этом, какой тип цитаты вы использовали, когда привязывали параметр uid?
3. Я не уверен, что вы имеете в виду под «типом цитаты». Я использовал этот оператор bind (один раз):
$stmt->bindValue(':uid', USER_ID, PDO::PARAM_INT);
а USER_ID находитсяint
до и после операторов PDO. Я проверил это с помощьюvar_dump
.4. Я предполагаю, что PDO заключает в кавычки только первое вхождение параметра с нужным вам типом, а второе — с кавычками по умолчанию (которые
PDO::PARAM_STR
). Сейчас я запускаю некоторый тест, чтобы увидеть, написал ли я дерьмо.
Ответ №2:
Могу ли я предложить другой подход? :
# I'm hard-coding the values so just handle that first and set $id1 and $id2
$parameterArray = array('MY_FIRST_TABLE' => $id1, 'MY_SECOND_TABLE' => $id2);
$sql = '';
foreach($parameterArray as $tableNameKey => $idValue) {
# build sql
$sql = "DELETE FROM `". $tableNameKey ."` WHERE `id`=". $idValue ." LIMIT 1";
# prepare and execute
$stmt = $conn->prepare($sql);
$stmt->execute();
# processing code after execution goes here
// ...
// ...
// ...
}
Здесь вы просто проходите цикл, используя массив таблиц и идентификаторов, из которых вы хотите создавать свои DELETE
инструкции sql.. Не могли бы вы попробовать это?