如何调试PDO数据库查询?

Nat*_*ong 133 php sql pdo

在迁移到PDO之前,我通过连接字符串在PHP中创建了SQL查询.如果我得到数据库语法错误,我可以回显最终的SQL查询字符串,在数据库上自己尝试,并调整它直到我修复错误,然后将其放回代码中.

准备好的PDO语句更快,更好,更安全,但有一件事困扰我:我从未看到最终查询,因为它被发送到数据库.当我在Apache日志或我的自定义日志文件中出现语法错误时(我在catch块中记录错误),我看不到导致它们的查询.

有没有办法捕获PDO发送到数据库的完整SQL查询并将其记录到文件中?

Pas*_*TIN 97

你这样说:

我从未看到最终查询,因为它被发送到数据库

嗯,实际上,在使用预准备语句时,没有" 最终查询 " 这样的东西:

  • 首先,将语句发送到DB,并在那里准备
    • 数据库解析查询,并构建它的内部表示
  • 并且,当您绑定变量并执行该语句时,只会将变量发送到数据库
    • 并且数据库将值"注入"到其语句的内部表示中


那么,回答你的问题:

有没有办法捕获PDO发送到数据库的完整SQL查询并将其记录到文件中?

否:由于任何地方都没有" 完整的SQL查询 ",因此无法捕获它.


出于调试目的,您可以做的最好的事情是通过将值注入语句的SQL字符串来"重构""真正的"SQL查询.

在这种情况下,我通常做的是:

  • 使用占位符回显与语句对应的SQL代码
  • 并在之后使用var_dump (或等效)来显示参数的值
  • 即使您没有可以执行的任何"真实"查询,这通常足以查看可能的错误.

在调试方面,这并不是很好 - 但这是准备好的报表的价格及其带来的好处.


Nat*_*ong 84

查看数据库日志

虽然Pascal MARTIN是正确的,PDO不会立即将完整的查询发送到数据库,但是ryeguy建议使用DB的日志记录功能实际上允许我查看由数据库组装和执行的完整查询.

方法如下:(这些说明适用于Windows机器上的MySQL - 您的里程可能会有所不同)

  • my.ini[mysqld]部分下,添加一个log命令,如log="C:\Program Files\MySQL\MySQL Server 5.1\data\mysql.log"
  • 重启MySQL.
  • 它将开始记录该文件中的每个查询.

该文件将快速增长,因此请确保在完成测试后将其删除并关闭日志记录.

  • 我因此讨厌PDO. (20认同)
  • 在MySQL 5.5+中,你需要`general_log`而不是`log`.请参阅http://dev.mysql.com/doc/refman/5.5/en/query-log.html (11认同)
  • **可能*工作取决于`PDO :: ATTR_EMULATE_PREPARES`的设置.有关详细信息,请参阅此答案:http://stackoverflow.com/questions/10658865/#answer-10658929 (4认同)

小智 16

当然,您可以使用此模式进行调试{{ PDO::ATTR_ERRMODE }} 只需在查询之前添加新行,然后您将显示调试行.

$db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$db->query('SELECT *******');  
Run Code Online (Sandbox Code Playgroud)


fij*_*ron 14

您可能想要做的是使用debugDumParams() 它不会为您构建准备好的语句,但它会显示您的参数.

  • 您可以使用输出缓冲(ob_start()...)来存储输出并记录它. (3认同)
  • 唯一的问题是它输出调试而不是在内部存储而不"回声"它.我不能这样记录它. (2认同)

don*_*ain 12

一个旧帖子,但也许有人会觉得这很有用;

function pdo_sql_debug($sql,$placeholders){
    foreach($placeholders as $k => $v){
        $sql = preg_replace('/:'.$k.'/',"'".$v."'",$sql);
    }
    return $sql;
}
Run Code Online (Sandbox Code Playgroud)

  • 对于也可以处理数字参数的类似函数,请参阅[我的答案](http://stackoverflow.com/a/19326169/560114)(感谢php.net上的评论者)。 (2认同)

Mat*_*wne 9

这是一个函数,可以看到有效的SQL将来自php.net上的 "Mark"评论:

function sql_debug($sql_string, array $params = null) {
    if (!empty($params)) {
        $indexed = $params == array_values($params);
        foreach($params as $k=>$v) {
            if (is_object($v)) {
                if ($v instanceof \DateTime) $v = $v->format('Y-m-d H:i:s');
                else continue;
            }
            elseif (is_string($v)) $v="'$v'";
            elseif ($v === null) $v='NULL';
            elseif (is_array($v)) $v = implode(',', $v);

            if ($indexed) {
                $sql_string = preg_replace('/\?/', $v, $sql_string, 1);
            }
            else {
                if ($k[0] != ':') $k = ':'.$k; //add leading colon if it was left out
                $sql_string = str_replace($k,$v,$sql_string);
            }
        }
    }
    return $sql_string;
}
Run Code Online (Sandbox Code Playgroud)


rye*_*guy 8

没有.PDO查询不是在客户端准备的.PDO只是将SQL查询和参数发送到数据库服务器.该数据库是什么呢取代(的?"S).您有两种选择:

  • 使用数据库的日志记录功能(但即便如此,它通常显示为两个单独的语句(即"非最终")至少与Postgres一起使用)
  • 输出SQL查询和参数并自己将它们拼凑在一起


Ali*_*eza 5

例如你有这个 pdo 语句:

$query="insert into tblTest (field1, field2, field3)
values (:val1, :val2, :val3)";
$res=$db->prepare($query);
$res->execute(array(
  ':val1'=>$val1,
  ':val2'=>$val2,
  ':val3'=>$val3,
));
Run Code Online (Sandbox Code Playgroud)

现在您可以通过定义这样的数组来获取执行的查询:

$assoc=array(
  ':val1'=>$val1,
  ':val2'=>$val2,
  ':val3'=>$val3,
);
$exQuery=str_replace(array_keys($assoc), array_values($assoc), $query);
echo $exQuery;
Run Code Online (Sandbox Code Playgroud)


Zip*_*ppp 5

除了检查错误日志之外,几乎没有关于错误显示的内容,但有一个相当有用的功能:

<?php
/* Provoke an error -- bogus SQL syntax */
$stmt = $dbh->prepare('bogus sql');
if (!$stmt) {
    echo "\PDO::errorInfo():\n";
    print_r($dbh->errorInfo());
}
?>
Run Code Online (Sandbox Code Playgroud)

(来源链接)

很明显,可以修改此代码以用作异常消息或任何其他类型的错误处理

  • 这是官方文档,当然没有人打算在生产中打印该错误,这也是官方网站(php.net)的一个例子,请参阅代码示例下面的链接.当然,更好的是在PDO实例化中使用额外的参数$ db-> setAttribute(PDO :: ATTR_ERRMODE,PDO :: ERRMODE_EXCEPTION),但遗憾的是您无法访问该代码 (3认同)
  • 这是错误的方式.PDO非常聪明,可以使这段代码无用.告诉它在错误上抛出异常.PHP将完成其余的工作,**比这个有限的功能更好**.另外,**请**,学习不要将所有错误直接打印到浏览器中.有更好的方法. (2认同)