교리-준비된 진술뿐만 아니라 실제 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로 무엇을 보내고 있는지보고 싶지만 대신을 포함하여 준비된 진술을 인쇄하고 ?있습니다. '전체'쿼리를 볼 수있는 방법이 있습니까?


전체 쿼리를 확인하는 가장 좋은 방법은 this answer : stackoverflow.com/a/678310/229077
Marek

Doctrine이 수행 한 작업을 활용할 수 있습니다 (프로파일 러에 실행 가능한 쿼리가 표시됨). 자세한 내용은 아래 내 대답을 참조하십시오
빈센트 Pazeller

답변:


164

교리는 데이터베이스 서버에 "실제 SQL 쿼리"를 보내지 않습니다. 실제로 준비된 명령문을 사용하고 있습니다.

  • 진술서를 준비하여 보내십시오 (이것이 반환하는 것입니다 $query->getSql())
  • 그런 다음 매개 변수를 보내십시오 (로 반환 $query->getParameters())
  • 그리고 준비된 문장을 실행

이는 PHP 측에 "실제"SQL 쿼리가 없다는 것을 의미하므로 Doctrine은이를 표시 할 수 없습니다.


14
파스칼 : 준비된 명령문이 실제 SQL 쿼리이기 때문에 "실제 SQL 쿼리"가 아니라고 말해서는 안됩니다. 매개 변수는 별도로 전송됩니다. 이 문구는 사람들을 혼동 할 수 있습니다 (예 : olivierpons.fr/2014/03/22/symfony-2-avantages-et-inconvenients ).
Matthieu Napoli

$query->getParameters();준비된 쿼리 문에 나타나야하므로 올바른 순서로 매개 변수를 리턴하지 않습니다.
gondo

4
나는 여기서 질문의 저자는 교리가 무엇을 보내는 지 상관하지 않았다고 생각한다. 사용자와 내가 알고 싶은 것은 수동으로 물음표를 매개 변수로 바꾸지 않고 붙여 넣기를 복사하고 실행할 수있는 쿼리를 얻는 방법입니다. codeigniter 에서처럼. 나는 이것을 symfony 디버거에서 찾았다 고 생각하지만 명령 줄에서 스크립트를 실행할 때 여전히 찾을 수 없습니다.
다리우스. V

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

littel 혜택을 제공합니다. SQL을 복사 할 때 여전히 수동으로 삽입 할 검색 wichi 매개 변수가 있지만 시간이 많이 걸립니다. 매개 변수가 삽입 된 쿼리를 원하는데 왜 그렇게 오래 찾을 수 없습니까? 내가 기억하는 한 codeigniter 프레임 워크에서도 프로파일 러에서 쿼리를 복사하고 수동으로 즉시 실행할 수 있습니다. 심포니도 마찬가지입니다.
Darius.V

35

mysql에 모든 쿼리를 기록하면 앱이 실행 한 쿼리를 확인할 수 있습니다.

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

당신이 찾고있는 것뿐만 아니라 더 많은 쿼리가있을 것입니다.

그러나 일반적으로 ->getSql();작동

편집하다:

내가 사용하는 모든 mysql 쿼리를 보려면

sudo vim /etc/mysql/my.cnf 

그 두 줄을 추가하십시오 :

general_log = on
general_log_file = /tmp/mysql.log

mysql을 다시 시작하십시오.


17

정확히 이것을 수행하는 Doctrine2 Logger를 만들었습니다. 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과 같은 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 ();
Fahim

모든 쿼리에서 작동하지는 않습니다. 내가 이것을 가질 때-> setParameters (array ( 'insuranceCarrier'=> $ insuranceCarrier, 'dateFrom'=> ​​$ dateFrom-> format ( 'Ym-d'), 'dateTo'=> $ dateTo-> format ( 'Ym- d '),))이 남은 것은? SQL의 마크.
다리우스. 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_valuesand 를 인쇄 $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...

나뭇 가지 파일에서 Doctrine의 나뭇 가지 도우미 필터를 사용하십시오.

// 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-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를 기반으로한다고 가정하면 현재 매우 간단한 답변이 하나 없습니다. 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();

답변에 설명을 추가하는 것을 잊지 마십시오? 설명이없는 하나의 라이너 만 허용되지 않습니다.
HaveNoDisplayName

1
Doctrine에서 SQL 쿼리를 출력하려면 $ query-> getResult ()-> getSql (); 감사합니다
Jaydeep Patel

2
commnet을 추가하는 대신 답변을 편집
HaveNoDisplayName
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.