关于php:Doctrine – 如何打印出真正的sql,而不仅仅是准备好的语句?

Doctrine - How to print out the real sql, not just the prepared statement?

我们使用的是条令,一种PHP ORM。我正在创建这样的查询:

1
$q = Doctrine_Query::create()->select('id')->from('MyTable');

然后在函数中,我添加了各种各样的where子句和适当的东西,像这样

1
$q->where('normalisedname = ? OR name = ?', array($string, $originalString));

稍后,在execute()使用该查询对象之前,我想打印出原始SQL以检查它,并执行以下操作:

1
$q->getSQLQuery();

但是,这只打印出准备好的语句,而不是完整的查询。我想看看它发送给MySQL的是什么,但是它却打印出一个准备好的语句,包括?。有什么方法可以看到"完整"查询吗?


条令并没有向数据库服务器发送"真正的SQL查询":它实际上使用的是准备好的语句,这意味着:

  • 发送报表,以便准备(这是$query->getSql()返回的内容)
  • 然后,发送参数(由$query->getParameters()返回)
  • 执行准备好的陈述

这意味着在PHP方面从来没有"真正的"SQL查询——因此,条令无法显示它。


一个例子……

1
2
$qb = $this->createQueryBuilder('a');
$query=$qb->getQuery();

显示sql:EDOCX1[4]

显示参数:$parameters=$query->getParameters();


如果将所有查询记录在mysql中,则可以检查应用程序执行的查询:

http://dev.mysql.com/doc/refman/5.1/en/query-log.html

将有更多的查询,不仅是您正在寻找的查询,而且您可以更好地进行查询。

但通常情况下,->getSql();起作用。

编辑:

查看我使用的所有mysql查询

1
sudo vim /etc/mysql/my.cnf

加上这两行:

1
2
general_log = on
general_log_file = /tmp/mysql.log

重启mysql


我已经创建了一个doctrine2记录器,它可以做到这一点。它"水合物"的参数化SQL查询值使用条令2自己的数据类型转换器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<?php


namespace Drsm\Doctrine\DBAL\Logging;
use Doctrine\DBAL\Logging\SQLLogger,
    Doctrine\DBAL\Types\Type,
    Doctrine\DBAL\Platforms\AbstractPlatform;
/**
 * A SQL logger that logs to the standard output and
 * subtitutes params to get a ready to execute SQL sentence

 * @author  [email protected]
 */

class EchoWriteSQLWithoutParamsLogger implements SQLLogger

{
    const QUERY_TYPE_SELECT="SELECT";
    const QUERY_TYPE_UPDATE="UPDATE";
    const QUERY_TYPE_INSERT="INSERT";
    const QUERY_TYPE_DELETE="DELETE";
    const QUERY_TYPE_CREATE="CREATE";
    const QUERY_TYPE_ALTER="ALTER";

    private $dbPlatform;
    private $loggedQueryTypes;
    public function __construct(AbstractPlatform $dbPlatform, array $loggedQueryTypes=array()){
        $this->dbPlatform=$dbPlatform;
        $this->loggedQueryTypes=$loggedQueryTypes;
    }
    /**
     * {@inheritdoc}
     */

    public function startQuery($sql, array $params = null, array $types = null)

    {
        if($this->isLoggable($sql)){
            if(!empty($params)){
                foreach ($params as $key=>$param) {
                    $type=Type::getType($types[$key]);
                    $value=$type->convertToDatabaseValue($param,$this->dbPlatform);
                    $sql = join(var_export($value, true), explode('?', $sql, 2));
                }

            }
            echo $sql ." ;".PHP_EOL;
        }
    }

    /**
     * {@inheritdoc}
     */

    public function stopQuery()
    {

    }
    private function isLoggable($sql){
        if (empty($this->loggedQueryTypes)) return true;
        foreach($this->loggedQueryTypes as $validType){
            if (strpos($sql, $validType) === 0) return true;
        }
        return false;
    }
}

用法示例:;以下代码的和平将在标准输出上回响使用$em实体管理器生成的任何插入、更新、删除SQL语句,

1
2
3
4
5
6
7
8
9
10
11
12
13
/**@var  \Doctrine\ORM\EntityManager $em */
$em->getConnection()
                ->getConfiguration()
                ->setSQLLogger(
                    new EchoWriteSQLWithoutParamsLogger(
                        $em->getConnection()->getDatabasePlatform(),
                        array(
                            EchoWriteSQLWithoutParamsLogger::QUERY_TYPE_UPDATE,
                            EchoWriteSQLWithoutParamsLogger::QUERY_TYPE_INSERT,
                            EchoWriteSQLWithoutParamsLogger::QUERY_TYPE_DELETE
                        )
                    )
                );


从技术上讲,getSqlQuery()的确显示了整个SQL命令,但是当您可以看到参数时,它会更有用。

1
2
3
echo $q->getSqlQuery();
foreach ($q->getFlattenedParams() as $index => $param)
  echo"$index => $param";

为了使这个模式更加可重用,有一个很好的方法,可以在来自条令查询对象的原始SQL的注释中描述。


没有其他真正的查询,这就是准备好的语句的工作方式。这些值绑定在数据库服务器中,而不是应用程序层中。

请看我对这个问题的回答:在使用PDO的PHP中,如何检查最终的SQL参数化查询?

(为了方便起见在此重复:)

Using prepared statements with parametrised values is not simply another way to dynamically create a string of SQL. You create a prepared statement at the database, and then send the parameter values alone.

So what is probably sent to the database will be a PREPARE ..., then SET ... and finally EXECUTE ....

You won't be able to get some SQL string like SELECT * FROM ..., even if it would produce equivalent results, because no such query was ever actually sent to the database.


我的解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
 /**
 * Get SQL from query
 *
 * @author Yosef Kaminskyi
 * @param QueryBilderDql $query
 * @return int
 */

public function getFullSQL($query)
{
    $sql = $query->getSql();
    $paramsList = $this->getListParamsByDql($query->getDql());
    $paramsArr =$this->getParamsArray($query->getParameters());
    $fullSql='';
    for($i=0;$i<strlen($sql);$i++){
        if($sql[$i]=='?'){
            $nameParam=array_shift($paramsList);

            if(is_string ($paramsArr[$nameParam])){
                $fullSql.= '"'.addslashes($paramsArr[$nameParam]).'"';
             }
            elseif(is_array($paramsArr[$nameParam])){
                $sqlArr='';
                foreach ($paramsArr[$nameParam] as $var){
                    if(!empty($sqlArr))
                        $sqlArr.=',';

                    if(is_string($var)){
                        $sqlArr.='"'.addslashes($var).'"';
                    }else
                        $sqlArr.=$var;
                }
                $fullSql.=$sqlArr;
            }elseif(is_object($paramsArr[$nameParam])){
                switch(get_class($paramsArr[$nameParam])){
                    case 'DateTime':
                             $fullSql.="'".$paramsArr[$nameParam]->format('Y-m-d H:i:s')."'";
                          break;
                    default:
                        $fullSql.= $paramsArr[$nameParam]->getId();
                }

            }
            else                    
                $fullSql.= $paramsArr[$nameParam];

        }  else {
            $fullSql.=$sql[$i];
        }
    }
    return $fullSql;
}

 /**
 * Get query params list
 *
 * @author Yosef Kaminskyi <[email protected]>
 * @param  Doctrine\ORM\Query\Parameter $paramObj
 * @return int
 */

protected function getParamsArray($paramObj)
{
    $parameters=array();
    foreach ($paramObj as $val){
        /* @var $val Doctrine\ORM\Query\Parameter */
        $parameters[$val->getName()]=$val->getValue();
    }

    return $parameters;
}
 public function getListParamsByDql($dql)
{
    $parsedDql = preg_split("/:/", $dql);
    $length = count($parsedDql);
    $parmeters = array();
    for($i=1;$i<$length;$i++){
        if(ctype_alpha($parsedDql[$i][0])){
            $param = (preg_split("/[' ' )]/", $parsedDql[$i]));
            $parmeters[] = $param[0];
        }
    }

    return $parmeters;}

使用示例:

1
2
3
4
5
$query = $this->_entityRepository->createQueryBuilder('item');
$query->leftJoin('item.receptionUser','users');
$query->where('item.customerid = :customer')->setParameter('customer',$customer)
->andWhere('item.paymentmethod = :paymethod')->setParameter('paymethod',"Bonus");
echo $this->getFullSQL($query->getQuery());


您可以使用以下方法轻松访问SQL参数。

1
2
3
4
5
6
7
8
9
10
11
12
   $result = $qb->getQuery()->getSQL();

   $param_values = '';  
   $col_names = '';  

   foreach ($result->getParameters() as $index => $param){              
            $param_values .= $param->getValue().',';
            $col_names .= $param->getName().',';
   }

   //echo rtrim($param_values,',');
   //echo rtrim($col_names,',');

因此,如果打印出$param_values$col_names,就可以通过SQL和相应的列名称获得参数值。

注意:如果$param返回一个数组,则需要重新迭代,因为IN (:?)中的参数通常是嵌套数组。

同时,如果您找到了另一种方法,请友好地与我们分享:)

谢谢您!


更清晰的解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
 /**
 * Get string query
 *
 * @param Doctrine_Query $query
 * @return string
 */

public function getDqlWithParams(Doctrine_Query $query){
    $vals = $query->getFlattenedParams();
    $sql = $query->getDql();
    $sql = str_replace('?', '%s', $sql);
    return vsprintf($sql, $vals);
}


您可以使用:

1
$query->getSQL();

如果您使用的是MySQL,那么可以使用Workbench查看正在运行的SQL语句。您还可以使用以下命令从MySQL中查看正在运行的查询:

1
 SHOW FULL PROCESSLIST \G


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Solution:1
====================================================================================

function showQuery($query)
{
    return sprintf(str_replace('?', '%s', $query->getSql()), $query->getParams());
}

// call function  
echo showQuery($doctrineQuery);

Solution:2
====================================================================================

function showQuery($query)
{
    // define vars              
    $output    = NULL;
    $out_query = $query->getSql();
    $out_param = $query->getParams();

    // replace params
   for($i=0; $i<strlen($out_query); $i++) {
       $output .= ( strpos($out_query[$i], '?') !== FALSE ) ?"'" .str_replace('?', array_shift($out_param), $out_query[$i])."'" : $out_query[$i];
   }

   // output
   return sprintf("%s", $output);
}

// call function  
echo showQuery($doctrineQueryObject);


也许它对某人有用:

1
2
3
4
5
6
7
8
// Printing the SQL with real values
$vals = $query->getFlattenedParams();
foreach(explode('?', $query->getSqlQuery()) as $i => $part) {
    $sql = (isset($sql) ? $sql : null) . $part;
    if (isset($vals[$i])) $sql .= $vals[$i];
}

echo $sql;

1
2
3
4
5
6
7
8
9
$sql = $query->getSQL();

$parameters = [];
    foreach ($query->getParameters() as $parameter) {
        $parameters[] = $parameter->getValue();
    }

$result = $connection->executeQuery($sql, $parameters)
        ->fetchAll();


我编写了一个简单的日志记录器,它可以用插入的参数记录查询。安装:

1
composer require cmyker/doctrine-sql-logger:dev-master

用途:

1
2
3
4
5
$connection = $this->getEntityManager()->getConnection();
$logger = new \Cmyker\DoctrineSqlLogger\Logger($connection);
$connection->getConfiguration()->setSQLLogger($logger);
//some query here
echo $logger->lastQuery;


要打印条令中的SQL查询,请使用:

1
$query->getResult()->getSql();