我知道准备一个语句然后多次执行它比简单地重复相同的查询要高效得多。
正确。因为服务器端准备好的语句(mysqli 使用的)仅在后续执行时将绑定的参数发送到数据库,而不是整个查询。此外,数据库服务器不必重新解析查询,但可以重用准备好的查询。因此,这可以对检索数据产生影响。所以理论上,这样更有效。
见http://php.net/manual/en/mysqli.quickstart.prepared-statements.php。
在实践中,不要期望有那么大的差异。这当然取决于您打算调用的查询的数量和复杂性以产生重大影响。如有疑问,请进行基准测试。
它还具有protecting you from SQL Injection 的额外好处。
下面的类通过主键获取声音对象。如果我需要在同一个脚本中拥有这个类的多个实例,我会从准备好的语句中获得好处吗?
没有。因为 mysqli 会在您每次创建新实例以准备该语句时向服务器发起查询。除了执行调用。所以你什么也赢不了。使用类似的东西会更直接
$query = sprintf('SELECT name, musickey FROM sounds WHERE id = %d', $id);
如果您真的希望每个 ID 有一个实例,则将其作为一个整体查询执行。
如果您想使用准备好的语句执行此操作,您只需要一个准备一次但可以将 ID 传递给多次的实例。
这是否适合存储过程?
如果您将应用程序逻辑放入存储过程中,您的应用程序的可移植性就会降低。如果您想在某一时刻更改数据库系统,则需要重新创建存储过程。这取决于您做出明智的选择。如果您不需要更改数据库系统,那么它可能是可行的。我更喜欢将逻辑保留在应用程序中。
我对面向对象的 PHP 也很陌生。
One aspect of OOP is about encapsulating and hiding information(和复杂性)来自客户。因此,拥有公共财产并不是最好的主意。此外,doing work in the constructor is not what the constructor is for. 构造函数只应将您的对象置于有效状态。并且由于准备会执行查询,因此您希望将其推迟到需要时。
说了这么多,考虑
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);