web-dev-qa-db-de.com

Doctrine - Wie kann man die echte SQL ausdrucken, nicht nur die vorbereitete Aussage?

Wir verwenden Doctrine, einen PHP ORM. Ich erstelle eine Abfrage wie folgt:

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

und dann füge ich in der Funktion verschiedene Klauseln und Dinge hinzu, wie es passend ist

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

Später, bevor execute() dieses Abfrageobjekt _, möchte ich die unformatierte SQL-Datei ausdrucken, um sie zu untersuchen, und zwar so:

$q->getSQLQuery();

Dadurch wird jedoch nur die vorbereitete Anweisung ausgedruckt, nicht die vollständige Abfrage. Ich möchte sehen, was es an MySQL sendet, aber stattdessen wird eine vorbereitete Anweisung einschließlich ? ausgegeben. Gibt es eine Möglichkeit, die "vollständige" Abfrage zu sehen?

145
Rory

Doctrine sendet keine "echte SQL-Abfrage" an den Datenbankserver: Es werden tatsächlich vorbereitete Anweisungen verwendet. Dies bedeutet:

  • Senden der Anweisung, damit sie vorbereitet wird (dies wird von $query->getSql() zurückgegeben)
  • Und dann das Senden der Parameter (zurückgegeben von $query->getParameters())
  • und die vorbereiteten Anweisungen ausführen

Dies bedeutet, dass auf der PHP Seite niemals eine "echte" SQL-Abfrage vorhanden ist. Doctrine kann sie also nicht anzeigen.

145
Pascal MARTIN

Ein Beispiel..

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

SQL anzeigen: $sql=$query->getSQL();

Parameter anzeigen: $parameters=$query->getParameters();

77
Andy.Diaz

Sie können die von Ihrer App ausgeführte Abfrage überprüfen, wenn Sie alle Abfragen in mysql protokollieren:

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

es gibt mehr Abfragen, nicht nur die, nach der Sie suchen, sondern Sie können auch danach suchen.

aber normalerweise funktioniert ->getSql();

Bearbeiten:

um alle mysql-Abfragen anzuzeigen, die ich verwende 

Sudo vim /etc/mysql/my.cnf 

und füge diese 2 Zeilen hinzu: 

general_log = on
general_log_file = /tmp/mysql.log

und starte mysql neu

33
alex toader

Ich habe einen Doctrine2 Logger erstellt, der genau dies tut. Es "hydratisiert" die parametrisierte SQL-Abfrage mit den Werten mithilfe von Doctrine 2-eigenen Datentypkonvertern.

<?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;
    }
}

Verwendungsbeispiel:; Der folgende Code für Ruhezustand wird bei der Standardausgabe alle mit $ em Entity Manager generierten INSERT-, UPDATE- und DELETE-SQL-Sätze widergeben. 

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

getSqlQuery() zeigt technisch den gesamten SQL-Befehl an, aber es ist viel nützlicher, wenn Sie auch die Parameter sehen können.

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

Damit dieses Muster besser wiederverwendet werden kann, gibt es einen Ansatz von Nice, der in comments at Raw SQL aus Doctrine Query Object beschrieben wird.

13
ladenedge

Es gibt keine andere echte Abfrage. So funktionieren vorbereitete Anweisungen. Die Werte sind an den Datenbankserver gebunden, nicht an die Anwendungsebene.

Siehe meine Antwort auf diese Frage: Wie prüfe ich in PHP mit PDO die endgültige SQL-parametrisierte Abfrage?

(Hier der Einfachheit halber wiederholt :)

Die Verwendung von vorbereiteten Anweisungen mit parametrisierten Werten ist keine einfache Möglichkeit, eine SQL-Zeichenfolge dynamisch zu erstellen. Sie erstellen eine vorbereitete Anweisung in der Datenbank und senden dann die Parameterwerte alleine.

Was wahrscheinlich an die Datenbank gesendet wird, ist PREPARE ..., dann SET ... und schließlich EXECUTE .....

Sie können keine SQL-Zeichenfolge wie SELECT * FROM ... erhalten, selbst wenn dies zu gleichwertigen Ergebnissen führen würde, da keine solche Abfrage tatsächlich an die Datenbank gesendet wurde.

13
Ben James

Meine Lösung:

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

Verwendungsbeispiel: 

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

Sie können leicht auf die SQL-Parameter zugreifen, indem Sie den folgenden Ansatz verwenden. 

   $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,',');    

Wenn Sie also $param_values und $col_names ausgedruckt haben, können Sie die Parameterwerte durch das SQL und die entsprechenden Spaltennamen ziehen lassen.

Hinweis: Wenn $param ein Array zurückgibt, müssen Sie es wiederholen, da die Parameter in IN (:?) normalerweise als verschachteltes Array vorliegen.

Wenn Sie in der Zwischenzeit einen anderen Ansatz gefunden haben, teilen Sie uns dies bitte mit :)

Vielen Dank!

7
Anjana Silva

Klarere Lösung:

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

Sie können verwenden:

$query->getSQL();

Wenn Sie MySQL verwenden, können Sie Workbench zum Anzeigen der ausgeführten SQL-Anweisungen verwenden. Sie können auch die laufende Abfrage von mysql anzeigen, indem Sie Folgendes verwenden:

 SHOW FULL PROCESSLIST \G
5
lac_dev
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);
4
Sandip Patel

Vielleicht kann es für jemanden nützlich sein:

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

Ich habe einen einfachen Logger geschrieben, der die Abfrage mit den eingegebenen Parametern protokollieren kann.

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

Verwendungszweck:

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

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

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

Die @dsamblas-Funktion wurde so geändert, dass sie funktioniert, wenn Parameter Datumsstrings wie der '2019-01-01' sind und wenn ein Array mit IN-like übergeben wird

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

. Also mach alles was dsamblas geschrieben hat, aber ersetze startQuery durch dieses oder sieh die Unterschiede und füge meinen Code hinzu. (für den Fall, dass er etwas in seiner Funktion geändert hat und meine Version keine Änderungen aufweist).

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

Hab nicht viel getestet.

0
Darius.V