Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/55.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 准备好的语句对于正常查询来说是一种浪费吗?(菲律宾)_Php_Mysql_Database_Prepared Statement - Fatal编程技术网

Php 准备好的语句对于正常查询来说是一种浪费吗?(菲律宾)

Php 准备好的语句对于正常查询来说是一种浪费吗?(菲律宾),php,mysql,database,prepared-statement,Php,Mysql,Database,Prepared Statement,如今,“准备好的语句”似乎是所有人建议向数据库发送查询的唯一方式。我甚至看到了为存储过程使用准备好的语句的建议。但是,对于额外的查询准备语句,我确信它们只对一行INSERT/UPDATE查询有用,因为它们所需的时间很短 我希望有人能纠正我的这一点,但它似乎只是重复了整个“表是邪恶的”CSS的事情。只有用于布局而非表格数据时,表格才是有害的。对表格数据使用DIV是违反WC3的一种样式 像wise一样,普通SQL(或从AR生成的SQL)似乎对80%的查询更有用,在大多数站点上,这是一个单一的选择,不

如今,“准备好的语句”似乎是所有人建议向数据库发送查询的唯一方式。我甚至看到了为存储过程使用准备好的语句的建议。但是,对于额外的查询准备语句,我确信它们只对一行INSERT/UPDATE查询有用,因为它们所需的时间很短

我希望有人能纠正我的这一点,但它似乎只是重复了整个“表是邪恶的”CSS的事情。只有用于布局而非表格数据时,表格才是有害的。对表格数据使用DIV是违反WC3的一种样式

像wise一样,普通SQL(或从AR生成的SQL)似乎对80%的查询更有用,在大多数站点上,这是一个单一的选择,不会在页面加载时重复(我在这里谈论的是像PHP这样的脚本语言)。为什么我要让过度征税的DB准备一个声明,声明在被删除之前只运行一次

MySQL:

事先准备好的声明是针对特定客户的 创建它的会话。 如果您终止会话而没有 解除分配先前准备的 语句,则服务器将其解除分配 自动地

因此,在脚本结束时,PHP将自动关闭连接,并且您将丢失准备好的语句,只有在下一次加载时才能重新创建脚本

我是否遗漏了什么,或者这只是降低性能的一种方式

:更新:

我突然意识到,我正在为每个脚本建立新的连接。我假设如果使用持久连接,那么这些问题就会消失。这是正确的吗

:UPDATE2:

似乎即使持久连接是解决方案——它们适用于大多数web——尤其是在使用事务的情况下。因此,我回到原点,除了下面的基准测试之外,没有什么可以继续下去

:更新3:

大多数人只是重复“准备好的语句防止SQL注入”这句话,这并不能完全解释问题。为每个DB库提供的“escape”方法也可以防止SQL注入。但不仅仅如此:

以正常方式发送查询时, 客户端(脚本)转换数据 转换为字符串,然后传递给 数据库服务器。然后,数据库服务器使用 CPU功率将它们转换回 正确的二进制数据类型。这个 然后,数据库引擎解析 语句并查找语法错误

使用准备好的语句时。。。这个 数据以本机二进制形式发送, 这节省了转换CPU的使用, 使数据传输更加方便 有效率的显然,这也会 如果客户端 与数据库服务器不在同一位置

…变量类型是预定义的, 因此,MySQL需要考虑 这些字符,它们不需要 逃脱


感谢OIS最终在这个问题上给我设置了障碍。

与CSS表格辩论不同,事先准备好的声明有明确的安全含义


如果您使用准备好的语句作为将用户提供的数据放入查询的唯一方式,那么在SQL注入方面,它们绝对是防弹的。

使用SQL查询时,如
选择x,y,z FROM foo WHERE c='mary had a little lamb'
服务器必须解析包含数据的sql语句+您必须清理“mary had…”部分(对每个参数调用mysql_real_escape()或类似内容)。 使用准备好的语句,服务器也必须解析语句,但不解析数据,只返回语句的标识符(一个很小的数据包)。然后发送实际数据,而不首先对其进行清理。我没有看到这里的开销,尽管我坦率地承认我从未测试过。有吗?;-)


编辑:使用准备好的语句可以消除将每个参数(in/out)转换为字符串的需要。如果您的php版本使用(而不是“旧”的libmysql客户机库),则可能更为如此。也没有测试过这方面的性能。

在数据库上执行sql语句时,sql解析器需要事先对其进行分析,这与准备过程完全相同

因此,直接执行sql语句与准备和执行sql语句相比没有缺点,但有一些优点:

  • 首先,正如朗内克已经说过的,将用户输入传递到准备好的语句中会自动转义输入。这就好像数据库已经为这些值准备了过滤器,并且只允许那些合适的值进入

  • 第二,如果完全使用准备好的语句,并且需要多次执行,则不需要重写代码来准备和执行,只需执行即可

  • 第三:如果操作得当,代码的可读性会提高:



而不是




假设您必须更改sql语句,您最喜欢哪种代码?;-)

我似乎没有发现使用持久连接的任何好处,也没有为这一问题准备好声明。看看这些数字——对于6000条select语句(这在页面请求中永远不会发生!),你几乎看不出区别。我的大多数页面使用的查询不到10个

更新我刚刚修改了我的测试 包括4k选择和4k插入 声明!你自己跑,让我来 了解是否存在任何设计错误

如果我的MySQL服务器与Apache不在同一台机器上运行,那么差异可能会更大

Persistent: TRUE
Prepare: TRUE
2.3399310112 seconds

Persistent: FALSE
Prepare: TRUE
2.3265211582184 seconds

Persistent: TRUE
Prepare: FALSE
2.3666892051697 seconds

Persistent: FALSE
Prepare: FALSE
2.3496441841125 seconds
以下是我的测试代码:

$hostname = 'localhost';
$username = 'root';
$password = '';
$dbname = 'db_name';

$persistent = FALSE;
$prepare = FALSE;

try 
{

    // Force PDO to use exceptions for all errors
    $attrs = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);

    if($persistent) 
    { 
        // Make the connection persistent
        $attrs[PDO::ATTR_PERSISTENT] = TRUE;
    }

    $db = new PDO("mysql:host=$hostname;dbname=$dbname", $username, $password, $attrs);

    // What type of connection?
    print 'Persistent: '.($db->getAttribute(PDO::ATTR_PERSISTENT) ? 'TRUE' : 'FALSE').'<br />';
    print 'Prepare: '.($prepare ? 'TRUE' : 'FALSE').'<br />';

    //Clean table from last run
    $db->exec('TRUNCATE TABLE `pdo_insert`');

}
catch(PDOException $e)
{
    echo $e->getMessage();
}

$start = microtime(TRUE);

$name = 'Jack';
$body = 'This is the text "body"';

if( $prepare ) {

    // Select
    $select = $db->prepare('SELECT * FROM pdo_insert WHERE id = :id');
    $select->bindParam(':id', $x);

    // Insert
    $insert = $db->prepare('INSERT INTO pdo_insert (`name`, `body`, `author_id`) 
    VALUES (:name, :body, :author_id)');
    $insert->bindParam(':name', $name);
    $insert->bindParam(':body', $body);
    $insert->bindParam(':author_id', $x);


    $run = 0;
    for($x=0;$x<4000;++$x) 
    {
        if( $insert->execute() && $select->execute() ) 
        {
            $run++;
        }
    }

}
else
{

    $run = 0;
    for($x=0;$x<4000;++$x) {

        // Insert
        if( $db->query('INSERT INTO pdo_insert (`name`, `body`, `author_id`) 
        VALUES ('.$db->quote($name).', '. $db->quote($body).', '. $db->quote($x).')') 

        AND

        // Select
        $db->query('SELECT * FROM pdo_insert WHERE id = '. $db->quote($x)) )
        {
            $run++;
        }

    }

}





print (microtime(true) - $start).' seconds and '.($run * 2).' queries';
$hostname='localhost';
$username='root';
$password='';
$dbname='db_name';
$persistent=FALSE;
$prepare=FALSE;
尝试
{
//强制PDO对所有错误使用异常
$attrs=array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_异常);
如果($persistent)
{ 
//骗局
Persistent: TRUE
Prepare: TRUE
2.3399310112 seconds

Persistent: FALSE
Prepare: TRUE
2.3265211582184 seconds

Persistent: TRUE
Prepare: FALSE
2.3666892051697 seconds

Persistent: FALSE
Prepare: FALSE
2.3496441841125 seconds
$hostname = 'localhost';
$username = 'root';
$password = '';
$dbname = 'db_name';

$persistent = FALSE;
$prepare = FALSE;

try 
{

    // Force PDO to use exceptions for all errors
    $attrs = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);

    if($persistent) 
    { 
        // Make the connection persistent
        $attrs[PDO::ATTR_PERSISTENT] = TRUE;
    }

    $db = new PDO("mysql:host=$hostname;dbname=$dbname", $username, $password, $attrs);

    // What type of connection?
    print 'Persistent: '.($db->getAttribute(PDO::ATTR_PERSISTENT) ? 'TRUE' : 'FALSE').'<br />';
    print 'Prepare: '.($prepare ? 'TRUE' : 'FALSE').'<br />';

    //Clean table from last run
    $db->exec('TRUNCATE TABLE `pdo_insert`');

}
catch(PDOException $e)
{
    echo $e->getMessage();
}

$start = microtime(TRUE);

$name = 'Jack';
$body = 'This is the text "body"';

if( $prepare ) {

    // Select
    $select = $db->prepare('SELECT * FROM pdo_insert WHERE id = :id');
    $select->bindParam(':id', $x);

    // Insert
    $insert = $db->prepare('INSERT INTO pdo_insert (`name`, `body`, `author_id`) 
    VALUES (:name, :body, :author_id)');
    $insert->bindParam(':name', $name);
    $insert->bindParam(':body', $body);
    $insert->bindParam(':author_id', $x);


    $run = 0;
    for($x=0;$x<4000;++$x) 
    {
        if( $insert->execute() && $select->execute() ) 
        {
            $run++;
        }
    }

}
else
{

    $run = 0;
    for($x=0;$x<4000;++$x) {

        // Insert
        if( $db->query('INSERT INTO pdo_insert (`name`, `body`, `author_id`) 
        VALUES ('.$db->quote($name).', '. $db->quote($body).', '. $db->quote($x).')') 

        AND

        // Select
        $db->query('SELECT * FROM pdo_insert WHERE id = '. $db->quote($x)) )
        {
            $run++;
        }

    }

}





print (microtime(true) - $start).' seconds and '.($run * 2).' queries';