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?
Doctrine sendet keine "echte SQL-Abfrage" an den Datenbankserver: Es werden tatsächlich vorbereitete Anweisungen verwendet. Dies bedeutet:
$query->getSql()
zurückgegeben)$query->getParameters()
)Dies bedeutet, dass auf der PHP Seite niemals eine "echte" SQL-Abfrage vorhanden ist. Doctrine kann sie also nicht anzeigen.
Ein Beispiel..
$qb = $this->createQueryBuilder('a');
$query=$qb->getQuery();
SQL anzeigen: $sql=$query->getSQL();
Parameter anzeigen: $parameters=$query->getParameters();
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
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
)
)
);
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.
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 ...
, dannSET ...
und schließlichEXECUTE ....
.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.
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());
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!
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);
}
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
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);
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;
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;
$sql = $query->getSQL();
$parameters = [];
foreach ($query->getParameters() as $parameter) {
$parameters[] = $parameter->getValue();
}
$result = $connection->executeQuery($sql, $parameters)
->fetchAll();
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.