Doktrin - Sadece hazırlanan ifadeyi değil, gerçek sql nasıl yazdırılır?


167

Bir PHP ORM olan Doktrin kullanıyoruz. Ben böyle bir sorgu oluşturuyorum:

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

ve sonra fonksiyona çeşitli yerlerde yan tümceler ve şeyler ekliyorum, bunun gibi

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

Daha sonra, execute()bu sorgu nesnesini -ing önce , incelemek için ham SQL yazdırmak ve bunu yapmak istiyorum:

$q->getSQLQuery();

Ancak bu sadece hazırlanan ifadeyi yazdırır, tam sorguyu yazdırmaz. MySQL'e ne gönderdiğini görmek istiyorum, ancak bunun yerine ?'s dahil olmak üzere hazırlanmış bir açıklama yazdırıyor . 'Tam' sorguyu görmenin bir yolu var mı?


Sorguyu tam olarak görmenin en iyi yolu bu yanıtta açıklanmıştır: stackoverflow.com/a/678310/229077
Marek

Doctrine tarafından yapılan çalışmadan yararlanabilirsiniz (profil oluşturucu çalıştırılabilir bir sorgu görüntülüyor). Ayrıntılar için aşağıdaki cevabımı görün
Vincent Pazeller

Yanıtlar:


164

Doktrin veritabanı sunucusuna bir "gerçek SQL sorgusu" göndermiyor: aslında hazırlanmış ifadeler kullanıyor, yani:

  • İfadenin gönderilmesi, hazırlanabilmesi için (bu tarafından döndürülür $query->getSql())
  • Ve sonra parametreleri gönderme (döndüren $query->getParameters())
  • ve hazırlanan ifadelerin yürütülmesi

Bu, PHP tarafında asla "gerçek" bir SQL sorgusu olmadığı anlamına gelir; bu nedenle Doktrin bunu görüntüleyemez.


14
Pascal: Bunun "gerçek SQL sorgusu" olmadığını söylememelisiniz çünkü hazırlanmış deyim gerçek SQL sorgusu, sadece parametrelerin ayrı olarak gönderilmesi. Bu ifadeler insanları şaşırtabilir (örn. Olivierpons.fr/2014/03/22/symfony-2-avantages-et-inconvenients ).
Matthieu Napoli

$query->getParameters();hazırlanmış sorgu deyiminde görünmeleri gerektiği için parametreleri doğru sırada döndürmeyecektir
gondo

4
Bence burada soru yazarı doktrinin ne gönderip göndermediğini umursamadı. Ne kullanıcı ve ben bilmek istedim nasıl soru işaretleri parametreleri ile el ile değiştirmek zorunda kalmadan Yapıştır ve Çalıştır kopyalayabilirsiniz sorgu almak için. Codeigniter'de olduğu gibi. Sanırım bu symfony hata ayıklayıcıda buldum, ama komut satırından komut dosyası çalıştırdığımda hala bulamıyorum.
Darius.V

104

Çalışan bir örnek:

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

5
Değişken atamaları olarak çalışırken, bunu dikkate almak isteyebilirsiniz: print $ query-> getSQL (); foreach ($ query-> getParameters () $ param olarak) {print "{$ param-> getName ()} -> {$ param-> getValue ()} \ n"; } daha okunabilir bir çıktı elde
Justin Finkelstein

küçük faydalar sağlar. Sql kopyaladığımda, hala manuel olarak eklemek için arama wichi parametresim var, ton zaman alıyor. Eklenen parametre içeren bir sorgu istiyoruz, neden bu kadar uzun bulamıyoruz? Codeigniter çerçevesinde bile hatırladığım kadarıyla, profiler içinde sorguyu kopyalayabilir ve anında manuel olarak çalıştırabilirsiniz. Symfony için de buna ihtiyacımız var.
Darius.V

35

Mysql'de tüm sorguları günlüğe kaydederseniz uygulamanız tarafından yürütülen sorguyu kontrol edebilirsiniz:

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

sadece aradığınız soru değil, bunun için de grep yapabilirsiniz.

ama genellikle ->getSql();işe yarar

Düzenle:

kullandığım tüm mysql sorgularını görüntülemek için

sudo vim /etc/mysql/my.cnf 

ve bu 2 satırı ekleyin:

general_log = on
general_log_file = /tmp/mysql.log

ve mysql'yi yeniden başlat


17

Tam olarak bunu yapan bir Doctrine2 Logger oluşturdum. Parametrelenmiş sql sorgusunu Doctrine 2 kendi veri tipi konvertörleri kullanarak değerlerle "nemlendirir".

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

Kullanım Örneği :; Aşağıdaki kod huzuru, $ em Entity Manager ile oluşturulan INSERT, UPDATE, DELETE SQL cümlelerini standart çıktıda yankılandıracak,

/**@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
Parametreler '2019-01-01' gibi tarih dizeleri olduğunda çalışmaz
Darius.V

14

getSqlQuery() teknik olarak tüm SQL komutunu gösterir, ancak parametreleri de görebildiğinizde çok daha yararlıdır.

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

Bu model daha yeniden kullanılabilir hale getirmek için, tarif güzel yaklaşım vardır yorumlarla de Doktrin Sorgu Object Ham SQL .


Bunun eski bir gönderi olduğunu biliyorum, ancak her iki bağınız da 404 sayfaya yönlendiriyor. Cevabınızı güncelleyebilir misiniz lütfen? Soruyorum, çünkü ne demek istediğinden emin değilim $q. Sorgu veya sorgu oluşturucu gibi görünmüyor.
k00ni

1
Korkarım daha çok yeniden kullanılabilir kod bulamıyorum. $qbu durumda Doktrin 1 sorgusudur. Doctrine 2 kullanıyor olabilirsiniz, bu durumda $qb = $this->createQueryBuilder('a'); $q = $qb->getQuery(); $sql = $q->getSQL(); $params = $q->getParameters(); Umarım bu yardımcı olur!
ladenedge

13

Başka bir gerçek sorgu yoktur, hazırlanan ifadeler bu şekilde çalışır. Değerler, uygulama katmanında değil, veritabanı sunucusunda bağlanır.

Şu soruya cevabımı görün: PDO ile PHP'de, son SQL parametreli sorguyu nasıl kontrol edebilirim?

(Kolaylık sağlamak için burada tekrarlandı :)

Parametreli değerlerle hazırlanan ifadeleri kullanmak, bir SQL dizesini dinamik olarak oluşturmanın başka bir yolu değildir. Veritabanında hazırlanmış bir ifade oluşturun ve sonra parametre değerlerini tek başına gönderin.

Yani muhtemelen veritabanına gönderilen şey PREPARE ..., sonra SET ...ve sonunda olacakEXECUTE ....

Veritabanına SELECT * FROM ...hiç böyle bir sorgu gönderilmediğinden, eşdeğer sonuçlar üretse bile gibi bir SQL dizesi alamazsınız .


9

Çözümüm:

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

Kullanım örneği:

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

Bunun için teşekkür ederim: D
Saad Achemlal

çok hoş. normal sorgular ile çalışır ama regexp ile bir sorgu var ve $ qb = $ this-> createQueryBuilder ('r') -> innerJoin ('r.profile', 'p') -> addSelect (' p ') -> burada (' REGEXP (: fileNamePattern, r.fileNamePattern) = 1 ') -> veWhere (' p.incomingLocation =: incomingLocation ') -> setParameters ([' fileNamePattern '=> $ fileName,' incomingLocation ' => $ konum]) -> getQuery ();
Fahim

Tüm sorgularla çalışmaz. Bunu yaptığımda -> setParameters (dizi ('insuranceCarrier' => $ insuranceCarrier, 'dateFrom' => $ dateFrom-> biçim ('Ym-d'), 'dateTo' => $ dateTo-> biçim ('Ym- d '),)) bunlar kaldı mı? sql işaretleri.
Darius.V

9

Aşağıdaki yaklaşımı kullanarak SQL parametrelerine kolayca erişebilirsiniz.

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

Eğer $param_valuesve öğesini yazdırdıysanız $col_names, sql ve ilgili sütun adlarından geçen parametre değerlerini alabilirsiniz.

Not: Bir $paramdizi döndürürse, IN (:?)genellikle içindeki parametreler iç içe bir dizi olduğu için yinelemelisiniz .

Bu arada başka bir yaklaşım bulduysanız, lütfen bizimle paylaşacak kadar nazik olun :)

Teşekkür ederim!


6

Daha net çözüm:

 /**
 * 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 (); mevcut değil
Geliştirici

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

Kullanabilirsiniz :

$query->getSQL();

MySQL kullanıyorsanız, çalışan SQL deyimlerini görüntülemek için Workbench'i kullanabilirsiniz. Aşağıdakileri kullanarak mysql'den çalışan sorguyu görüntüle özelliğini de kullanabilirsiniz:

 SHOW FULL PROCESSLIST \G

4

Belki biri için yararlı olabilir:

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

dal dosyalarınızda Doctrine'in dal yardımcıları filtrelerini kullanın:

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

Açıklama:

Hazırlanan ifadenin aslında "gerçek sorgular" olduğunu belirten diğer yanıtlar doğrudur, ancak bariz sorucunun beklentilerini yanıtlamazlar ... Her geliştirici, hata ayıklama için (veya kullanıcıya görüntülemek için "çalıştırılabilir bir sorgu" görüntülemek istiyor) .

Böylece, nasıl yaptıklarını görmek için Symfony profiler kaynağına baktım. Doktrin kısmı Doktrinin sorumluluğudur, bu yüzden Symfony ile entegrasyon için bir doktrin paketi hazırladılar. doctrine-bundle/Resources/views/Collector/db.html.twigDosyaya baktığınızda, nasıl yaptıklarını öğreneceksiniz (bu, sürümler arasında değişebilir). İlginç bir şekilde, tekrar kullanabileceğimiz dal filtreleri oluşturdular (yukarıya bakın).

Her şeyin çalışması için sorgumuz için Günlüğe Kaydetmeyi etkinleştirmeliyiz. Bunu yapmanın birden çok yolu vardır ve burada sorguları gerçekten yazdırmadan günlüğe kaydetmeye izin veren DebugStack kullanıyorum. Bu, ihtiyacınız olan şeyse bunun üretim modunda çalışmasını da sağlar ...

Daha fazla biçimlendirmeye ihtiyacınız varsa, bir stil etiketine bazı CSS eklediklerini göreceksiniz, bu yüzden basitçe "çalın" ^^:

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

Umarım bu yardımcı olacaktır ;-)


1

Eklenen parametrelerle sorguyu kaydedebilen basit bir kaydedici yazdım. Kurulum:

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

Kullanımı:

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

Yanıtınıza kodun ne yaptığını açıklayan bir metin eklemelisiniz.
DarkMukke

0

@Dsamblas işlevi, parametreler bu '2019-01-01' gibi tarih dizeleri olduğunda ve IN gibi kullanılarak dizi geçirildiğinde çalışmak üzere işlev görür

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

. Yani dsamblas'ın yazdığı her şeyi yapın, ancak startQuery'yi bununla değiştirin veya farkları görün ve kodumu ekleyin. (işlevinde bir şeyi değiştirmesi ve sürümümde değişiklik olmaması durumunda).

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

Çok fazla test yapmadı.


0

Oluşturulan bir SQL sorgusunda hata ayıklamak ve sql editörü yürütmek istedim, çünkü bu konu için biraz araştırma yaptım. Tüm cevaplarda görüldüğü gibi, son derece teknik bir konudur.

İlk sorunun dev-env'ye dayandığını varsaydığımda, şu anda çok basit bir cevap eksik. Sadece Symfony profil oluşturucuda kullanabilirsiniz. Doktrin Sekmesine tıklamanız, incelemek istediğiniz sorguya ilerlemeniz yeterlidir. Ardından "çalıştırılabilir sorguyu görüntüle" yi tıklayın ve sorgunuzu doğrudan SQL düzenleyicinize yapıştırabilirsiniz

Daha fazla UI temel yaklaşımı ancak çok hızlı ve hata ayıklama kodu yükü olmadan.

resim açıklamasını buraya girin


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

Doktrin'de bir SQL sorgusu yazdırmak için şunu kullanın:

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

Cevabınıza açıklama eklemeyi unutmayın? Açıklama olmadan sadece bir astar, kabul edilemez.
46

1
Doctrine'de sql sorgusu yazdırmak için $ query-> getResult () -> getSql (); Teşekkürler
Jaydeep Patel

2
commnet eklemek yerine, cevabınızı
düzenleyin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.