복붙노트

[SQL] 여러 번 매개 변수 바인딩 사용하십시오

SQL

여러 번 매개 변수 바인딩 사용하십시오

나는 사용자가 다른 종류의 정보를 포함 할 수 내 데이터베이스에 대한 아주 기본적인 검색 엔진을 구현하기 위해 노력하고있어. 검색 자체는 결과가 항상 3 열로 병합 노조 선택의 몇 가지로 구성되어 있습니다.

복귀 된 데이터는 그러나 다른 테이블에서 인출되고있다.

각 쿼리는 중매에 대한 $ 용어를 사용하고, 나는 그것을 구속했습니다 준비된 매개 변수로 "용어".

이제 설명서는 말한다 :

용어의 매개 변수와 함께 : termX (X 임기 = N ++)는 더 나은 솔루션이 있어야 내가 대신 각 교체의 생각?

termX : 아니면 단지의 바인드 X 번호로해야합니까?

이 내 솔루션을 게시 편집 :

$query = "SELECT ... FROM table WHERE name LIKE :term OR number LIKE :term";

$term = "hello world";
$termX = 0;
$query = preg_replace_callback("/\:term/", function ($matches) use (&$termX) { $termX++; return $matches[0] . ($termX - 1); }, $query);

$pdo->prepare($query);

for ($i = 0; $i < $termX; $i++)
    $pdo->bindValue(":term$i", "%$term%", PDO::PARAM_STR);

좋아, 여기에 샘플입니다. 나는 sqlfiddle 시간이 없어하지만 필요한 경우 나중에 하나를 추가합니다.

(
    SELECT
        t1.`name` AS resultText
    FROM table1 AS t1
    WHERE
        t1.parent = :userID
        AND
        (
            t1.`name` LIKE :term
            OR
            t1.`number` LIKE :term
            AND
            t1.`status` = :flagStatus
        )
)
UNION
(
    SELECT
        t2.`name` AS resultText
    FROM table2 AS t2
    WHERE
        t2.parent = :userParentID
        AND
        (
            t2.`name` LIKE :term
            OR
            t2.`ticket` LIKE :term
            AND
            t1.`state` = :flagTicket
        )
)

해결법

  1. ==============================

    1.나는 몇 번 지금 같은 문제가 달려 있고 난 내가 꽤 간단하고 좋은 해결책을 발견했다 생각합니다. 경우 내가 매개 변수를 여러 번 사용하려면, 난 그냥 MySQL의 사용자 정의 변수에 저장합니다. 이 코드가 훨씬 더 쉽게 읽을 당신은 PHP의 추가 기능이 필요하지 않습니다 :

    나는 몇 번 지금 같은 문제가 달려 있고 난 내가 꽤 간단하고 좋은 해결책을 발견했다 생각합니다. 경우 내가 매개 변수를 여러 번 사용하려면, 난 그냥 MySQL의 사용자 정의 변수에 저장합니다. 이 코드가 훨씬 더 쉽게 읽을 당신은 PHP의 추가 기능이 필요하지 않습니다 :

    $sql = "SET @term = :term";
    
    try
    {
        $stmt = $dbh->prepare($sql);
        $stmt->bindValue(":term", "%$term%", PDO::PARAM_STR);
        $stmt->execute();
    }
    catch(PDOException $e)
    {
        // error handling
    }
    
    
    $sql = "SELECT ... FROM table WHERE name LIKE @term OR number LIKE @term";
    
    try
    {
        $stmt = $dbh->prepare($sql);
        $stmt->execute();
        $stmt->fetchAll();
    }
    catch(PDOException $e)
    {
        //error handling
    }
    

    유일한 단점은 추가로 MySQL의 쿼리를 할 필요가있을 수 있습니다 -하지만 그것은 완전히 가치가 이럴. 사용자 정의 변수는 세션 바인딩의 MySQL에 있기 때문에 다중 사용자 환경에서 부작용을 일으키는 원인이되는 변수 @term 걱정할 필요도 없다.

  2. ==============================

    2.나는 두 번 사용하는 용어의 이름을 변경하여 문제를 해결하기 위해 두 가지 기능을 만들었습니다. 바인딩의 이름을 변경하기위한 SQL 하나의 이름을 변경 한 가지.

    나는 두 번 사용하는 용어의 이름을 변경하여 문제를 해결하기 위해 두 가지 기능을 만들었습니다. 바인딩의 이름을 변경하기위한 SQL 하나의 이름을 변경 한 가지.

        /**
         * Changes double bindings to seperate ones appended with numbers in bindings array
         * example: :term will become :term_1, :term_2, .. when used multiple times.
         *
         * @param string $pstrSql
         * @param array $paBindings
         * @return array
         */
        private function prepareParamtersForMultipleBindings($pstrSql, array $paBindings = array())
        {
            foreach($paBindings as $lstrBinding => $lmValue)
            {
                // $lnTermCount= substr_count($pstrSql, ':'.$lstrBinding);
                preg_match_all("/:".$lstrBinding."\b/", $pstrSql, $laMatches);
    
                $lnTermCount= (isset($laMatches[0])) ? count($laMatches[0]) : 0;
    
                if($lnTermCount > 1)
                {
                    for($lnIndex = 1; $lnIndex <= $lnTermCount; $lnIndex++)
                    {
                        $paBindings[$lstrBinding.'_'.$lnIndex] = $lmValue;
                    }
    
                    unset($paBindings[$lstrBinding]);
                }
            }
    
            return $paBindings;
        }
    
        /**
         * Changes double bindings to seperate ones appended with numbers in SQL string
         * example: :term will become :term_1, :term_2, .. when used multiple times.
         *
         * @param string $pstrSql
         * @param array $paBindings
         * @return string
         */
        private function prepareSqlForMultipleBindings($pstrSql, array $paBindings = array())
        {
            foreach($paBindings as $lstrBinding => $lmValue)
            {
                // $lnTermCount= substr_count($pstrSql, ':'.$lstrBinding);
                preg_match_all("/:".$lstrBinding."\b/", $pstrSql, $laMatches);
    
                $lnTermCount= (isset($laMatches[0])) ? count($laMatches[0]) : 0;
    
                if($lnTermCount > 1)
                {
                    $lnCount= 0;
                    $pstrSql= preg_replace_callback('(:'.$lstrBinding.'\b)', function($paMatches) use (&$lnCount) {
                        $lnCount++;
                        return sprintf("%s_%d", $paMatches[0], $lnCount);
                    } , $pstrSql, $lnLimit = -1, $lnCount);
                }
            }
    
            return $pstrSql;
        }
    

    사용 예 :

    $lstrSqlQuery= $this->prepareSqlForMultipleBindings($pstrSqlQuery, $paParameters);
    $laParameters= $this->prepareParamtersForMultipleBindings($pstrSqlQuery, $paParameters);
    $this->prepare($lstrSqlQuery)->execute($laParameters);
    

    변수 이름에 대한 설명 : P : 파라미터, L : 기능 현지 STR : 문자열 이하, N : 숫자, A : 배열 m : 혼합

  3. ==============================

    3.나는 그것이 변경된 경우 문제가 게시 된 이후 알지만, 지금은 설명서를 확인하지 않습니다, 그것은 말합니다 :

    나는 그것이 변경된 경우 문제가 게시 된 이후 알지만, 지금은 설명서를 확인하지 않습니다, 그것은 말합니다 :

    http://php.net/manual/en/pdo.prepare.php - (강조 광산.)

    그래서, 기술적으로, 에뮬레이션 허용 $ PDO_obj->의 setAttribute (사실 PDO :: ATTR_EMULATE_PREPARES)를 사용하여 준비; 너무 작동합니다; 에뮬레이트 준비된 문 끄기이 답변에서 설명한대로 (좋은 생각되지 않을 수 있지만 특정 주입 공격으로부터 보호하기위한 한 가지 방법은, 일부는 반대로 쓴 비록 에뮬레이트되지 않았거나 준비합니다 여부를 보안에 아무런 차이가 없다 . (나도 몰라,하지만 난 후자를 염두에 전 언급 한 공격을 한 것으로 생각하지 않습니다.)

    나는 완전성을 위해서이 답변을 추가 해요; 나는이 사이트에 내가 일하고 있어요 오프 emulate_prepares을 설정하고, 비슷한 쿼리 (SELECT를 사용했다대로 TBL FROM ..., 휴식을 검색 원인으로 WHERE (필드 1 LIKE : 용어 또는 필드 2 LIKE : 용어) ... ), 내가 명시 적으로 false로 PDO :: ATTR_EMULATE_PREPARES을 설정할 때까지하고, 잘 작동했다, 그것은 실패 시작했다.

    (PHP 5.4.38, MySQL은 5.1.73 FWIW)

    이 질문은 당신이 (오 잘 나에게 반 직관적 보인다,하지만) 동일한 쿼리에 두 번 명명 된 매개 변수를 사용할 수 없습니다 저를 밀고 것입니다. (I 해당 페이지 여러 번 보았다하지만 어떻게 든 나는 매뉴얼도에 그것을 놓쳤다.)

  4. ==============================

    4.실제로 동작하는 솔루션 :

    실제로 동작하는 솔루션 :

    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, TRUE);
    $query = "SELECT * FROM table WHERE name LIKE :term OR number LIKE :term";
    $term  = "hello world";
    $stmt  = $pdo->prepare($query);
    $stmt->execute(array('term' => "%$term%"));
    $data  = $stmt->fetchAll();
    
  5. ==============================

    5.사용자 정의 변수를 이동하고 쿼리에 값을 바인딩에 동일한 변수를 여러 번 사용하고 그래 그게 잘 작동하는 그것의 한 방법입니다.

    사용자 정의 변수를 이동하고 쿼리에 값을 바인딩에 동일한 변수를 여러 번 사용하고 그래 그게 잘 작동하는 그것의 한 방법입니다.

    //Setting this doesn't work at all, I tested it myself 
    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, TRUE);
    

    나는 솔루션 중 하나가 여기에 게시처럼 모두에서 사용자 정의 변수를 사용하고 싶어하지 않았다. 나는 다른 솔루션은 여기에 게시처럼 PARAM 이름 변경을 할 수도 싶어하지 않았다. 그래서 여기 내 솔루션은 사용자 정의 변수를 사용하지 않고 적은 코드를 사용하여 쿼리에 어떤 이름을 변경하지 않고 작품과는 PARAM이 쿼리에 사용되는 횟수에 대해 걱정하지 않습니다이다. 내 모든 프로젝트에이를 사용하고있어 잘 작동합니다.

    //Example values
    var $query = "select * from test_table where param_name_1 = :parameter and param_name_2 = :parameter";
    var param_name = ":parameter";
    var param_value = "value";
    
    //Wrap these lines of codes in a function as needed sending 3 params $query, $param_name and $param_value. 
    //You can also use an array as I do!
    
    //Lets check if the param is defined in the query
    if (strpos($query, $param_name) !== false)
    {
        //Get the number of times the param appears in the query
        $ocurrences = substr_count($query, $param_name);
        //Loop the number of times the param is defined and bind the param value as many times needed
        for ($i = 0; $i < $ocurrences; $i++) 
        {
            //Let's bind the value to the param
            $statement->bindValue($param_name, $param_value);
        }
    }
    

    그리고 여기에 간단한 작업 솔루션입니다!

    이 가까운 미래에 누군가가 도움이되기를 바랍니다.

  6. from https://stackoverflow.com/questions/18511645/use-bound-parameter-multiple-times by cc-by-sa and MIT license