提问者:小点点

从PDO准备好的语句获取原始SQL查询字符串


在对准备好的语句调用PDOStatement::Execute()时,有没有方法让原始SQL字符串得到执行? 对于调试目的,这将是非常有用的。


共3个答案

匿名用户

我假设您的意思是希望得到最终的SQL查询,并在其中插入参数值。 我理解这对调试很有用,但这不是准备好的语句的工作方式。 参数不与客户端的准备好的语句组合,因此PDO永远不能访问与其参数组合的查询字符串。

当您执行prepare()时,SQL语句被发送到数据库服务器,当您执行execute()时,参数被单独发送。 MySQL的一般查询日志确实显示了在执行()之后插入值的最终SQL。 下面是我的一般查询日志的摘录。 我从mysql CLI运行查询,而不是从PDO运行查询,但原理是相同的。

081016 16:51:28 2 Query       prepare s1 from 'select * from foo where i = ?'
                2 Prepare     [2] select * from foo where i = ?
081016 16:51:39 2 Query       set @a =1
081016 16:51:47 2 Query       execute s1 using @a
                2 Execute     [2] select * from foo where i = 1

如果您设置PDO属性PDO::attr_emulate_prepares,也可以得到您想要的结果。 在这种模式下,PDO将参数插值到SQL查询中,并在执行()时发送整个查询。 这不是真正的准备好的查询。 您将通过在execute()之前将变量插值到SQL字符串中来规避准备好的查询的好处。

来自@afilina的评论:

否,文本SQL查询在执行过程中不与参数组合。 所以PDO没有什么可以向您展示的。

在内部,如果使用PDO::attr_emulate_prepares,PDO将创建SQL查询的副本,并在执行准备和执行之前将参数值插入其中。 但是PDO并不公开这个修改过的SQL查询。

PDOStatement对象有一个属性$queryString,但它仅在PDOStatement的构造函数中设置,并且在用参数重写查询时不会更新。

PDO要求他们公开重写的查询将是一个合理的特性请求。 但是即使这样也不能提供“完整”查询,除非您使用pdo::attr_emulate_prepares。

这就是为什么我在上面展示了使用MySQL服务器的通用查询日志的变通方法,因为在这种情况下,即使是带有参数占位符的准备好的查询也会在服务器上重写,参数值回填到查询字符串中。 但这只在日志记录期间进行,而不是在查询执行期间进行。

匿名用户

/**
 * Replaces any parameter placeholders in a query with the value of that
 * parameter. Useful for debugging. Assumes anonymous parameters from 
 * $params are are in the same order as specified in $query
 *
 * @param string $query The sql query with parameter placeholders
 * @param array $params The array of substitution parameters
 * @return string The interpolated query
 */
public static function interpolateQuery($query, $params) {
    $keys = array();

    # build a regular expression for each parameter
    foreach ($params as $key => $value) {
        if (is_string($key)) {
            $keys[] = '/:'.$key.'/';
        } else {
            $keys[] = '/[?]/';
        }
    }

    $query = preg_replace($keys, $params, $query, 1, $count);

    #trigger_error('replaced '.$count.' keys');

    return $query;
}

匿名用户

我修改了该方法,以包括处理像WHERE IN(?)这样的语句的数组输出。

更新:刚刚添加了检查空值和重复的$params,所以实际的$params值不会被修改。

伟大的工作,大网民和谢谢!

/**
 * Replaces any parameter placeholders in a query with the value of that
 * parameter. Useful for debugging. Assumes anonymous parameters from 
 * $params are are in the same order as specified in $query
 *
 * @param string $query The sql query with parameter placeholders
 * @param array $params The array of substitution parameters
 * @return string The interpolated query
 */
public function interpolateQuery($query, $params) {
    $keys = array();
    $values = $params;

    # build a regular expression for each parameter
    foreach ($params as $key => $value) {
        if (is_string($key)) {
            $keys[] = '/:'.$key.'/';
        } else {
            $keys[] = '/[?]/';
        }

        if (is_string($value))
            $values[$key] = "'" . $value . "'";

        if (is_array($value))
            $values[$key] = "'" . implode("','", $value) . "'";

        if (is_null($value))
            $values[$key] = 'NULL';
    }

    $query = preg_replace($keys, $values, $query);

    return $query;
}