我知道准备一条语句然后多次执行它比简单地重复相同的查询要有效得多。
正确的。因为服务器端准备好的语句(mysqli 使用的)仅在后续执行时将绑定的参数发送到数据库,而不是整个查询。此外,数据库服务器不必重新解析查询,但可以重用准备好的查询。因此,这可以对检索数据产生影响。所以理论上,这更有效。
请参阅http://php.net/manual/en/mysqli.quickstart.prepared-statements.php。
在实践中,不要期望有那么大的差异。这当然取决于您打算调用的查询的数量和复杂性以产生重大影响。如有疑问,请进行基准测试。
它还具有保护您免受 SQL 注入的额外好处。
下面的类通过主键获取声音对象。如果我需要在同一个脚本中有这个类的多个实例,我会得到准备好的语句的好处吗?
不会。因为 mysqli 会在您每次创建新实例以准备该语句时向服务器发起查询。除了执行调用。所以你什么也赢不了。然后使用类似的东西会更直接
$query = sprintf('SELECT name, musickey FROM sounds WHERE id = %d', $id);
如果您真的想要每个 ID 一个实例,然后将其作为一个整体查询执行。
如果您想使用准备好的语句执行此操作,您只需要一个准备一次但可以将 ID 传递给多次的实例。
这会是存储过程的良好候选者吗?
如果您将应用程序逻辑放入存储过程中,那么您的应用程序的可移植性就会降低。如果您想在某一时刻更改数据库系统,则需要重新创建存储过程。这取决于您做出明智的选择。如果您不需要更改数据库系统,那么它可能是可行的。我更喜欢将逻辑保留在应用程序中。
我对面向对象的 PHP 也很陌生。
OOP 的一个方面是关于封装和隐藏来自客户端的信息(和复杂性)。因此,拥有公共财产并不是最好的主意。此外,在构造函数中工作并不是构造函数的用途。构造函数只应将您的对象置于有效状态。并且由于准备进行查询,因此您希望将其推迟到需要时。
综上所述,考虑
interface FindById
{
public function findById($id);
}
class SoundFinder implements FindById
{
private $mysqli;
private $statement;
private $query = "SELECT name, musickey FROM sounds WHERE id = ?";
public function __construct(mysqli $mysqli)
{
$this->mysqli = $mysqli;
}
private function prepare()
{
$this->statement = $mysqli->prepare($this->query)
}
public function findById($id)
{
if (!$this->statement) {
$this->prepare();
}
$this->statement->bind_param("i", $this->id);
$this->statement->execute();
$this->statement->bind_result($name, $musickey);
$this->statement->free_result();
return array($name, $musickey);
}
}
然后你可以像这样使用它:
$connection = new mysqli(/* your connection data */);
$soundFnder = new SoundFinder($connection);
$sound42 = $soundFinder->findById(42);
$sound314 = $soundFinder->findById(314);