Doctrine-準備されたステートメントだけでなく、実際のSQLを出力する方法は?


167

PHP ORMであるDoctrineを使用しています。私はこのようなクエリを作成しています:

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

次に、関数に、次のようにさまざまなwhere句や必要なものを追加します

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

後で、execute()そのクエリオブジェクトを実行する前に、生のSQLを印刷して調べて、これを実行します。

$q->getSQLQuery();

ただし、これは準備されたステートメントのみを出力し、完全なクエリは出力しません。MySQLに何が送信されているのかを確認したいのですが、代わりにを含む準備済みステートメントを出力してい?ます。「完全」クエリを確認する方法はありますか?


完全なクエリを確認した最良の方法は、この回答で説明されています。stackoverflow.com
Marek

Doctrineによって行われた作業を利用できます(プロファイラーは実行可能なクエリを表示しています)。詳細については、以下の私の回答を参照してください
Vincent Pazeller '11年

回答:


164

Doctrineは「実際のSQLクエリ」をデータベースサーバーに送信していません:実際に準備されたステートメントを使用しています、つまり:

  • 準備するためのステートメントの送信(これはによって返されます$query->getSql()
  • そして、パラメータを送信します(によって返されます$query->getParameters()
  • 準備されたステートメントを実行する

これは、PHP側に「実際の」SQLクエリが存在しないことを意味します—したがって、Doctrineはそれを表示できません。


14
Pascal:準備されたステートメントは実際のSQLクエリであるため、「実際のSQLクエリ」ではないと言ってはいけません。パラメーターが個別に送信されるだけです。この文言は人々を混乱させる可能性があります(例:olivierpons.fr/2014/03/22/symfony-2-avantages-et-inconvenients)。
Matthieu Napoli、2014

$query->getParameters();彼らは準備されたクエリ文で表示されますよう、正しい順序でパラメータを返しません
権藤

4
私はここで質問の著者は教義が何を送るかどうかを気にしていなかったと思います。ユーザーと私が知りたかったのは、疑問符をパラメーターで手動で置き換えることなく、コピーして貼り付けて実行できるクエリを取得する方法です。codeigniterのように。symfonyデバッガーでこれを見つけたと思いますが、コマンドラインからスクリプトを実行するとまだ見つかりません。
Darius.V 2019年

104

実際の例:

$qb = $this->createQueryBuilder('a');
$query=$qb->getQuery();
// SHOW SQL: 
echo $query->getSQL(); 
// Show Parameters: 
echo $query->getParameters();

5
これは変数の割り当てとして機能しますが、これを検討することをお勧めします。print $ query-> getSQL(); foreach($ query-> getParameters()as $ param){print "{$ param-> getName()}-> {$ param-> getValue()} \ n"; より読みやすい出力が得られるため
Justin Finkelstein

それは少しメリットを与えます。SQLをコピーしても、手動で挿入する検索wichiパラメーターがまだあるので、時間がかかります。パラメータを挿入したクエリが必要ですが、なぜそれほど長く検索できないのですか?私が覚えている限り、codeigniterフレームワークでも、プロファイラーでクエリをコピーして手動で実行しなくても即座に実行できます。symfonyでも同じことが必要です。
Darius.V 2018

35

すべてのクエリをmysqlに記録すると、アプリによって実行されたクエリを確認できます。

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

あなたが探しているものだけでなく、あなたはそれのためにgrepすることができるより多くのクエリがあるでしょう。

しかし、通常は->getSql();機能します

編集:

私が使用するすべてのmysqlクエリを表示するには

sudo vim /etc/mysql/my.cnf 

次の2行を追加します。

general_log = on
general_log_file = /tmp/mysql.log

とmysqlを再起動します


17

これを正確に行うDoctrine2ロガーを作成しました。Doctrine 2独自のデータ型コンバーターを使用して、パラメーター化されたSQLクエリを値で「ハイドレート」します。

<?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  dsamblas@gmail.com
 */
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 Entity Managerで生成されたINSERT、UPDATE、DELETE SQL文を標準出力にエコーします。

/**@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
                        )
                    )
                );

1
パラメータが '2019-01-01'のような日付文字列の場合は機能しません
Darius.V

14

getSqlQuery() 技術的にはSQLコマンド全体を表示しますが、パラメーターも確認できると、はるかに便利です。

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

このパターンをより再利用可能にするために、Doctrine Query ObjectのRaw SQLコメントで説明されている素晴らしいアプローチがあります


これは古い投稿ですが、両方のリンクから404ページが表示されています。回答を更新していただけますか?あなたが何を意味するのかわからないので、私は尋ねています$q。クエリでもクエリビルダーでもないようです。
k00ni

1
再利用可能なコードが見つからないようです。 $qこの場合はDoctrine 1のクエリです。あなたはDoctrine 2を使用しているかもしれません、その場合は$qb = $this->createQueryBuilder('a'); $q = $qb->getQuery(); $sql = $q->getSQL(); $params = $q->getParameters(); うまくいけばうまくいくような何かが欲しいでしょう!
ladenedge

13

他の実際のクエリはありません。これが準備されたステートメントの動作方法です。値は、アプリケーション層ではなくデータベースサーバーにバインドされます。

この質問への私の回答を参照してください:PDOを使用したPHPで、最終的なSQLパラメーター化クエリを確認する方法は?

(便宜上、ここで繰り返します。)

パラメータ化された値で準備されたステートメントを使用することは、SQLの文字列を動的に作成するための単なる別の方法ではありません。データベースで準備済みステートメントを作成し、パラメーター値のみを送信します。

それでは、おそらくデータベースに送信されることになりPREPARE ...、その後、SET ...そして最後にEXECUTE ....

SELECT * FROM ...そのようなクエリが実際にデータベースに送信されたことはないため、同等の結果が生成される場合でも、のようなSQL文字列を取得することはできません。


9

私の解決策:

 /**
 * 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 <yosefk@spotoption.com>
 * @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;}

使用例:

$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());

これをありがとう:D
Saad Achemlal

非常に素晴らしい。通常のクエリで動作しますが、正規表現を使用したクエリがあり、$ qb = $ this-> createQueryBuilder( 'r')-> innerJoin( 'r.profile'、 'p')-> addSelect( 'をサポートしていないようです。 p ')-> where(' REGEXP(:fileNamePattern、r.fileNamePattern)= 1 ')-> andWhere(' p.incomingLocation =:incomingLocation ')-> setParameters([' fileNamePattern '=> $ fileName、' incomingLocation ' => $ location])-> getQuery();
ファヒム

すべてのクエリでは機能しません。これがあったとき-> setParameters(array( 'insuranceCarrier' => $ insuranceCarrier、 'dateFrom' => $ dateFrom-> format( 'Ym-d')、 'dateTo' => $ dateTo-> format( 'Ym- d ')、))それらは残っていましたか?SQLのマーク。
Darius.V

9

次の方法を使用すると、SQLパラメータに簡単にアクセスできます。

   $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 (:?)通常、内部のパラメータはネストされた配列であるため、反復する必要があります。

その間、別のアプローチを見つけた場合は、私たちと共有してください。

ありがとうございました!


6

より明確な解決策:

 /**
 * 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);
}

$ query-> getFlattenedParams(); 存在しない
開発者

5
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);

5

使用できます:

$query->getSQL();

MySQLを使用している場合、Workbenchを使用して実行中のSQLステートメントを表示できます。以下を使用して、mysqlから実行中のクエリを表示することもできます。

 SHOW FULL PROCESSLIST \G

4

多分それは誰かのために役立つことができます:

// 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;

2

TL; DR

$qb = ... // your query builder
$query = $qb->getQuery();
// temporarily enable logging for your query (will also work in prod env)
$conf = $query->getEntityManager()->getConnection()->getConfiguration();
$backupLogger = $conf->getSQLLogger();
$logger = new \Doctrine\DBAL\Logging\DebugStack();
$conf->setSQLLogger($logger);
// execute query
$res = $query->getResult();
$conf->setSQLLogger($backupLogger); //restore logger for other queries
$params = [
  'query' => array_pop($logger->queries) //extract query log details
  //your other twig params here...
]
return $params; //send this to your twig template...

twigファイルで、Doctrineのtwigヘルパーフィルターを使用します。

// show raw query:
{{ (query.sql ~ ';')|doctrine_replace_query_parameters(query.params)
// highlighted
{{ (query.sql ~ ';')|doctrine_replace_query_parameters(query.params)|doctrine_pretty_query(highlight_only = true) }}
// highlighted and formatted (i.e. with tabs and newlines)
{{ (query.sql ~ ';')|doctrine_replace_query_parameters(query.params)|doctrine_pretty_query }}

説明:

Preparedステートメントが実際には「実際のクエリ」であることを述べた他の答えは正しいですが、それらは明白な質問者の期待には答えません...すべての開発者はデバッグのために(またはユーザーに表示するために)「実行可能なクエリ」を表示したいと考えています。

それで、私はSymfonyプロファイラーのソースを調べて、それらがどのように実行されるかを確認しました。Doctrineの部分はDoctrineの責任であり、Symfonyと統合するためのDoctrineバンドルを作成しました。を見てdoctrine-bundle/Resources/views/Collector/db.html.twigファイルをファイルの実行方法がわかります(バージョンによって異なる場合があります)。興味深いことに、彼らは私たちが再利用できる小枝フィルターを作成しました(上記を参照)。

すべてが機能するためには、クエリのロギングを有効にする必要があります。これを行うにはいくつかの方法があります。ここでは、実際に印刷することなくクエリをログに記録できるDebugStackを使用しています。これにより、これが必要な場合にプロダクションモードで機能するようになります...

さらにフォーマットが必要な場合は、スタイルタグにCSSが含まれていることがわかります。そのため、単に「盗む」^^:

.highlight pre { margin: 0; white-space: pre-wrap; }
.highlight .keyword   { color: #8959A8; font-weight: bold; }
.highlight .word      { color: #222222; }
.highlight .variable  { color: #916319; }
.highlight .symbol    { color: #222222; }
.highlight .comment   { color: #999999; }
.highlight .backtick  { color: #718C00; }
.highlight .string    { color: #718C00; }
.highlight .number    { color: #F5871F; font-weight: bold; }
.highlight .error     { color: #C82829; }

これが役に立てば幸いです;-)


1

パラメータを挿入してクエリを記録できるシンプルなロガーを作成しました。インストール:

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

使用法:

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

1
$sql = $query->getSQL();

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

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

コードの内容を説明するテキストを回答に追加する必要があります。
DarkMukke

0

パラメータがこの「2019-01-01」のような日付文字列であり、配列がINを使用して渡された場合に機能するように@dsamblas関数を変更しました

$qb->expr()->in('ps.code', ':activeCodes'),

。だから、dsamblasが書いたことをすべて行いますが、startQueryをこれに置き換えるか、違いを確認してコードを追加します。(彼が自分の機能で何かを変更し、私のバージョンに変更がない場合)。

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

{
    if($this->isLoggable($sql)){
        if(!empty($params)){
            foreach ($params as $key=>$param) {

                try {
                    $type=Type::getType($types[$key]);
                    $value=$type->convertToDatabaseValue($param,$this->dbPlatform);
                } catch (Exception $e) {
                    if (is_array($param)) {
                        // connect arrays like ("A", "R", "C") for SQL IN
                        $value = '"' . implode('","', $param) . '"';
                    } else {
                        $value = $param; // case when there are date strings
                    }
                }

                $sql = join(var_export($value, true), explode('?', $sql, 2));
            }

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

あまりテストしませんでした。


0

生成されたSQLクエリをデバッグしてSQLエディターで実行したかったので、このトピックについていくつか調査を行いました。すべての回答に見られるように、それは非常に技術的なトピックです。

最初の質問がdev-envに基づいていると仮定すると、現時点では非常に単純な答えが1つありません。Symfonyプロファイラーでビルドを使用できます。Doctrineタブをクリックするだけで、調べたいクエリまでスクロールします。次に、「実行可能なクエリを表示」をクリックすると、SQLエディタにクエリを直接貼り付けることができます

より多くのUIベースのアプローチですが、非常に迅速で、コードのオーバーヘッドをデバッグしません。

ここに画像の説明を入力してください


0
$sql = $query->getSQL();
$obj->mapDQLParametersNamesToSQL($query->getDQL(), $sql);
echo $sql;//to see parameters names in sql
$obj->mapDQLParametersValuesToSQL($query->getParameters(), $sql);
echo $sql;//to see parameters values in sql

public function mapDQLParametersNamesToSQL($dql, &$sql)
{
    $matches = [];
    $parameterNamePattern = '/:\w+/';
    /** Found parameter names in DQL */
    preg_match_all($parameterNamePattern, $dql, $matches);
    if (empty($matches[0])) {
        return;
    }
    $needle = '?';
    foreach ($matches[0] as $match) {
        $strPos = strpos($sql, $needle);
        if ($strPos !== false) {
            /** Paste parameter names in SQL */
            $sql = substr_replace($sql, $match, $strPos, strlen($needle));
        }
    }
}

public function mapDQLParametersValuesToSQL($parameters, &$sql)
{
    $matches = [];
    $parameterNamePattern = '/:\w+/';
    /** Found parameter names in SQL */
    preg_match_all($parameterNamePattern, $sql, $matches);
    if (empty($matches[0])) {
        return;
    }
    foreach ($matches[0] as $parameterName) {
        $strPos = strpos($sql, $parameterName);
        if ($strPos !== false) {
            foreach ($parameters as $parameter) {
                /** @var \Doctrine\ORM\Query\Parameter $parameter */
                if ($parameterName !== ':' . $parameter->getName()) {
                    continue;
                }
                $parameterValue = $parameter->getValue();
                if (is_string($parameterValue)) {
                    $parameterValue = "'$parameterValue'";
                }
                if (is_array($parameterValue)) {
                    foreach ($parameterValue as $key => $value) {
                        if (is_string($value)) {
                            $parameterValue[$key] = "'$value'";
                        }
                    }
                    $parameterValue = implode(', ', $parameterValue);
                }
                /** Paste parameter values in SQL */
                $sql = substr_replace($sql, $parameterValue, $strPos, strlen($parameterName));
            }
        }
    }
}

-1

DoctrineでSQLクエリを出力するには、以下を使用します:

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

回答に説明を追加することを忘れないでください。説明なしのライナーは1つだけで、受け入れられません。
HaveNoDisplayName 2015年

1
DoctrineでSQLクエリを出力するには$ query-> getResult()-> getSql();を使用します。ありがとう
Jaydeep Patel、2015

2
commnetを追加する代わりに、回答を編集します
HaveNoDisplayName
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.