PDO参数化查询 - 重用命名占位符?

26

实际上,我有一个值需要在 SQL 查询中调用多次。因此,在语句中是否可以重复使用相同的命名占位符,例如 SELECT :Param FROM Table WHERE Column = :Param,然后简单地绑定值 ":Param",并且该值对于两个 :Params 都存在?


为什么不试一下呢?PDO::prepare说可以,而PDOStatement::execute则表示根据驱动程序可能需要关闭数据库游标。 - Steve-o
我尝试了一下,但是一直出现错误,后来发现我做错了其他事情。在文档中找不到任何东西(正在查看PDO :: bindParam)。不过在PDO :: prepare中找到了它,感谢您指引我正确的方向! - gamers2000
实际上,第一部分 SELECT :Param 是不可能的;你不能绑定表/列,请参考 https://dev59.com/8XVC5IYBdhLWcg3wykaj/。*然而*,你可以这样做 $Param = "column"; SELECT $Param... - Funk Forty Niner
你觉得能否授予我答案,这是可能的吗?目前被接受的状态是不可能的,但是我的解决方案可以实现。 - Jonathan
6个回答

23

PDO::prepare 表示:"不能在准备好的语句中重复使用同名的命名参数标记", 所以我猜那就不行了。


13
文档并不完全正确。如果PDO::ATTR_EMULATE_PREPAREStrue,PDO实际上支持重用命名参数标记。 - Mike
4
似乎有人更新了文档。现在文档中写道:“除非启用仿真模式,否则无法在准备好的语句中多次使用相同名称的命名参数标记。” - Mike
2
你说的部分正确。但是 SELECT :Param 是不可能的,因为你不能绑定表/列。请查看 https://dev59.com/8XVC5IYBdhLWcg3wykaj/。 - Funk Forty Niner
1
但是如果您使用PDO :: ATTR_EMULATE_PREPARES,那么pdo会将所有内容转换为字符串。 - Gaby_64

9

如果设置PDO::ATTR_EMULATE_PREPARES = true,则可以实现此功能。

例如:$connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);

如果您使用的是Laravel,则可以在config/database.php文件中的options数组中设置。例如:PDO::ATTR_EMULATE_PREPARES => true


3
除了重复利用之外,这里的主要问题是您正在尝试动态更改列名。
这是一个匿名用户在http://php.net/manual/en/pdo.prepare.php上发布的答案。
对于那些想知道为什么在占位符周围添加引号是错误的,以及为什么您不能使用占位符作为表或列名称的人: 有一个关于准备语句中占位符工作方式的常见误解:它们不仅仅被替换为(转义的)字符串,然后执行生成的SQL。相反,要求“准备”语句的DBMS会为如何执行该查询制定完整的查询计划,包括将使用哪些表和索引,这将与您如何填充占位符无关。 “SELECT name FROM my_table WHERE id = :value”的计划将是相同的,无论您为“:value”替换什么,但看似类似的“SELECT name FROM :table WHERE id = :value”无法计划,因为DBMS不知道您实际上要从哪个表中选择。 即使在使用“模拟准备”时,PDO也不能让您在任何地方使用占位符,因为它必须弄清楚您的意思: “Select:foo From some_table”是否意味着“:foo”将成为列引用还是文字字符串?
当您的查询使用动态列引用时,应该明确地将您知道存在于表中的列列入白名单,例如使用带有在默认情况下抛出异常的switch语句。

2
许多像您这样的查询可以重写为只使用一个占位符。最初的回答。
SELECT :Param FROM Table WHERE Column = :Param

would be the same as

SELECT Column FROM Table WHERE Column = :Param

但有时候情况并不那么简单。例如:

最初的回答

SELECT *
FROM my_table
WHERE first_name LIKE :Param
   OR last_name  LIKE :Param
   OR biography  LIKE :Param

在这种情况下,您可以通过将参数值存储在交叉连接的派生表(FROM子句中的子查询)中进行重用:

在这种情况下,您可以通过将参数值存储在交叉连接的派生表(FROM子句中的子查询)中进行重用:

最初的回答
SELECT t.*
FROM my_table t
CROSS JOIN (SELECT :Param as Param) AS x
WHERE first_name LIKE x.Param
   OR last_name  LIKE x.Param
   OR biography  LIKE x.Param

1
正如其他人指出的那样,有很多解决方法:
  1. 在提供相同值的同时使用不同的占位符。
  2. 重写查询,只需要唯一的占位符。
  3. ATTR_EMULATE_PREPARES设置为true

如果上述方法不可行或不可取,还有另一种优雅的解决方法,即在SQL中使用变量:

DECLARE @MyVariable AS INT = :value;
SELECT column_x FROM table WHERE column_y = @MyVariable OR column_z = @MyVariable;

然后,只需将value赋值给此语句。

0

有一个解决方法:

  function search($criteria) {

     $sql = "SELECT * FROM my_table
        WHERE column_1 like CONCAT('%', :criteria1, '%') 
           OR column_2 like CONCAT('%', :criteria2, '%') 
           OR column_3 like CONCAT('%', :criteria3, '%')  
     ";

     $stmt =  $this->db->prepare($sql);
     $stmt->bindParam(':criteria1', $criteria);
     $stmt->bindParam(':criteria2', $criteria);
     $stmt->bindParam(':criteria3', $criteria);
     $stmt->execute();
     return($stmt->fetchAll(PDO::FETCH_ASSOC));
  }

总之,使用具有相同条件的不同占位符。


更少的代码,更易读的SQL - Your Common Sense

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接