5

我看到很多人说您应该始终使用准备好的语句进行数据库查询。但是,PHP 文档说:

每个准备好的语句都会占用服务器资源。语句应在使用后立即显式关闭。如果没有显式完成,当 PHP 释放语句句柄时,语句将被关闭。

使用准备好的语句并不总是执行语句的最有效方式。只执行一次的准备好的语句比未准备的语句会导致更多的客户端-服务器往返。

来自http://php.net/manual/en/mysqli.quickstart.prepared-statements.php

鉴于上述情况,如果您只打算使用一次查询,那么使用准备好的语句不是更好吗?

4

4 回答 4

5

差异被认为可以忽略不计。

然而,人们必须将本地准备好的语句与准备好的语句的一般概念区分开来。

前者只是大多数 DBMS 支持的运行查询的一种形式,在此处进行了解释。它的用法可以质疑。
后者是用占位符替换实际数据的一般概念,意味着对替换数据的进一步处理。它在编程中被广泛使用,一个众所周知的printf()函数就是一个例子。并且后一种方法必须始终用于对数据库运行查询,无论它是否由本机准备好的语句支持。因为:

  • 准备好的语句使正确的格式化(或处理)不可避免
  • 准备好的语句在唯一适当的地方进行正确的格式化(或处理)——就在查询执行之前,而不是其他地方,所以,我们的安全不会依赖于像这样不可靠的来源
    • 一些 PHP 的“魔法”功能,它会破坏数据而不是使其安全。
    • 一个(或几个)程序员的好意,他们可以决定在程序流的某个地方格式化(或不格式化)我们的变量。这是非常重要的一点。
  • 准备好的语句影响进入查询的值,但不影响源变量,源变量保持不变,可以在进一步的代码中使用(通过电子邮件发送或显示在屏幕上)。
  • 准备好的语句可以显着缩短应用程序代码,在后台进行所有格式化(*仅在驱动程序允许的情况下)。

因此,即使您考虑不使用本机准备好的语句(这很好),您也必须始终使用占位符而不是实际数据来创建查询。为此,您可以使用PDO,它的工作原理与上述完全一样 - 默认情况下它只是模拟 prepares,这意味着从准备好的查询和数据创建常规 SQL 查询,然后针对数据库运行。

但是,PDO 缺乏对许多重要数据类型(例如标识符或数组)的支持——因此它使您无法始终使用占位符,从而使注入成为可能。幸运的是,safeMysql对每种数据类型都有占位符,并允许您安全地运行查询。

于 2013-05-03T18:47:39.993 回答
1

SQL 注入是首选方法的原因。如果您的查询是不变的,那么没有理由使用准备好的语句。如果您确定即使通过字符串连接构造查询也将是安全的,则可以跳过准备好的语句。

常数没问题。

$sql = "SELECT * FROM foobar";

如果 $id 变量不可能包含除 int 之外的任何其他类型的数据,那么这是可以的。

$sql = "SELECT * FROM users WHERE id=".$id;

通常很容易无法确保变量中包含的数据是正确的类型,因此准备好的语句是构造查询的更安全的方式。

于 2013-05-03T18:47:06.363 回答
1

除非您可以对此进行基准测试并证明它们对性能有可衡量的拖累,例如至少慢 10-15% 的东西,否则没有理由对此大惊小怪。即便如此,假设您只使用占位符,这对于几乎绝对确定数据完整性来说是一个很小的代价。

准备好的语句,即使只使用一次,也很容易正确实现,并且如果您遵守从不使用字符串插值注入数据的纪律,则很难出错。

任何认为自己是专业程序员的人都必须定期审核他们的应用程序,以确保其中没有 SQL 注入错误。如果逃跑不是很明显,就不能推定逃跑了,所以你必须调查。

占位符还可以减少您错误地混淆列及其相关值的可能性。PDO 的命名参数特性很好地避免了这种情况。

于 2013-05-03T18:48:38.920 回答
1

准备语句会产生适合变量的查询计划。然后它仍然可供多次使用。

顺便说一句,有一些与它们相关的陷阱。考虑这个陈述:

select * from posts order by post_date limit 10 offset ?;

如果你准备好了,你将不会得到索引扫描。因为,就计划者所知,您想要最后几行,并且在您浏览索引时,不可能一一浏览您的数百万个帖子。

如果您直接使用参数运行它,您将获得小偏移量的索引扫描,并且没有超过阈值的索引扫描,原因与准备好的语句不使用索引相同。

鉴于此,考虑到大多数应用程序只运行一次查询,您通常可以坚持使用模拟准备。具体见PDO::ATTR_EMULATE_PREPARES

于 2013-05-03T18:49:03.493 回答