如何对 PDO 中的不同表使用相同的准备好的语句?

Posted

技术标签:

【中文标题】如何对 PDO 中的不同表使用相同的准备好的语句?【英文标题】:How can I use the same prepared statement for different tables in PDO? 【发布时间】:2011-11-11 22:41:09 【问题描述】:

我经常需要提交一些非常相似的 SQL 查询,例如删除我知道 ID 的表中的一行。这是我的一段代码,prepared statement:

$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();

如果我尝试这个,什么都不会发生。于是我用下面的sn-p来分析错误:

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

我刚刚读到我不能对表名或列名使用准备好的语句 (source)。所以我想我必须寻找另一个解决方案。

【问题讨论】:

如果你尝试运行它会发生什么? 没什么。我添加了一些行来显示给你尽可能多的关于这个“bug”的信息。我认为故意不可能替换表名,因为准备好的语句似乎存储在数据库中以进行优化......据我所知。我想同时为每张桌子执行此操作可能很困难。所以我希望我能找到一个替代准备好的陈述,或者告诉我我对它们的假设是错误的。 【参考方案1】:

我认为(我对此不是 100% 确定)您正在尝试查询一个名为

的表
`'TableName'`

或者

`"TableName"`

取决于 PDO 如何尝试引用您的参数。我一直在研究如何不在 PDO 中引用字符串(我遇到了同样的问题),但我很久以前就放弃了并编写了自己的实现。

也许您可以预处理查询并用表名替换参数,但要小心替换表参数名时可能发生的陷阱 (That's what I'm talking about)。

【讨论】:

感谢您提供此信息。我查看了 mysql 服务器收到的查询:SELECT user_password FROM 'chess_users' WHERE 1 = '1'。这似乎是我不能用准备好的语句来做的原因。另一个有趣的观察:这个查询是由SELECT user_password FROM :table WHERE :uid = :uid 创建的。为什么 :uid 被 1 替换一次,被 '1' 替换一次? 这个我不太清楚,你绑定uid参数的时候用的是什么类型的引号? 我不确定您所说的“报价类型”是什么意思。我使用过这个绑定语句(一次):$stmt->bindValue(':uid', USER_ID, PDO::PARAM_INT); 和 USER_ID 是 int 在 PDO 语句之前和之后。我已经通过var_dump 进行了检查。 我猜 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 语句的表和 ID 数组进行循环。您可以尝试一下吗?

【讨论】:

以上是关于如何对 PDO 中的不同表使用相同的准备好的语句?的主要内容,如果未能解决你的问题,请参考以下文章

带有位域的 PDO 准备好的语句

如何查看准备好的 PDO SQL 语句 [重复]

如何使用带有 BIT(1) 列的 PDO 准备语句?

如何使用准备好的 PDO 语句设置 ORDER BY 参数?

如何使用准备好的 PDO 语句设置 ORDER BY 参数?

在 pdo 准备好的语句中执行多插入或许多不同的插入更好/更快吗?