복붙노트

IN 절 배열로 PDO 사용하기

PHP

IN 절 배열로 PDO 사용하기

PDO를 사용하여 값의 배열을 사용하는 IN 절이있는 명령문을 실행합니다.

$in_array = array(1, 2, 3);
$in_values = implode(',', $in_array);
$my_result = $wbdb->prepare("SELECT * FROM my_table WHERE my_value IN (".$in_values.")");
$my_result->execute();
$my_results = $my_result->fetchAll();

위의 코드는 완벽하게 정상적으로 작동하지만 내 질문은 왜 이렇게되지 않습니다 :

 $in_array = array(1, 2, 3);
    $in_values = implode(',', $in_array);
    $my_result = $wbdb->prepare("SELECT * FROM my_table WHERE my_value IN (:in_values)");
    $my_result->execute(array(':in_values' => $in_values));
    $my_results = $my_result->fetchAll();

이 코드는 my_value의 항목을 $ in_array (1)의 첫 번째 항목과 같지만 배열의 나머지 항목 (2 및 3)은 반환하지 않습니다.

해결법

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

    1.

    PDO는 그런 것들로 좋지 않습니다. 물음표가있는 문자열을 동적으로 만들고 쿼리에 삽입해야합니다.

    $in  = str_repeat('?,', count($in_array) - 1) . '?';
    $sql = "SELECT * FROM my_table WHERE my_value IN ($in)";
    $stm = $db->prepare($sql);
    $stm->execute($in_array);
    $data = $stm->fetchAll();
    

    쿼리에 다른 자리 표시자가있는 경우 다음 방법을 사용할 수 있습니다. 코드는 내 PDO 자습서에서 가져옵니다.

    array_merge () 함수를 사용하여 모든 변수를 단일 배열로 결합하여 다른 변수를 배열 형식으로 쿼리에 나타나는 순서대로 추가 할 수 있습니다.

    $arr = [1,2,3];
    $in  = str_repeat('?,', count($arr) - 1) . '?';
    $sql = "SELECT * FROM table WHERE foo=? AND column IN ($in) AND bar=? AND baz=?";
    $stm = $db->prepare($sql);
    $params = array_merge([$foo], $arr, [$bar, $baz]);
    $stm->execute($params);
    $data = $stm->fetchAll();
    

    명명 된 자리 표시자를 사용하는 경우 이름이 지정된 자리 표시 자의 시퀀스를 만들어야하므로 코드가 좀 더 복잡해집니다. : id0, : id1, : id2. 따라서 코드는 다음과 같습니다.

    // other parameters that are going into query
    $params = ["foo" => "foo", "bar" => "bar"];
    
    $ids = [1,2,3];
    $in = "";
    foreach ($ids as $i => $item)
    {
        $key = ":id".$i;
        $in .= "$key,";
        $in_params[$key] = $item; // collecting values into key-value array
    }
    $in = rtrim($in,","); // :id0,:id1,:id2
    
    $sql = "SELECT * FROM table WHERE foo=:foo AND id IN ($in) AND bar=:bar";
    $stm = $db->prepare($sql);
    $stm->execute(array_merge($params,$in_params)); // just merge two arrays
    $data = $stm->fetchAll();
    

    운좋게도 지정된 자리 표시 자에 대해서는 엄격한 순서를 따를 필요가 없으므로 배열 순서에 상관없이 병합 할 수 있습니다.

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

    2.

    PDO 준비 문에서 변수 대체는 배열을 지원하지 않습니다. 그것은 하나를위한 것입니다.

    배열의 길이에 따라 필요한 자리 표시 자 수를 생성하여이 문제를 해결할 수 있습니다.

    $variables = array ('1', '2', '3');
    $placeholders = str_repeat ('?, ',  count ($variables) - 1) . '?';
    
    $query = $pdo -> prepare ("SELECT * FROM table WHERE column IN($placeholders)");
    if ($query -> execute ($variables)) {
        // ...
    }
    
  3. ==============================

    3.

    PDO가 좋은 해결책을 제공하지 않는 것처럼 보이기 때문에 대부분 PDO의 API를 따르는 DBAL을 사용하는 것도 고려할 수 있지만 유용한 기능을 추가합니다. http://docs.doctrine-project.org/projects/doctrine-dbal/ ko / latest / reference / data-retrieval-and-manipulation.html # 매개 변수 변환 목록

    $stmt = $conn->executeQuery('SELECT * FROM articles WHERE id IN (?)',
        array(array(1, 2, 3, 4, 5, 6)),
        array(\Doctrine\DBAL\Connection::PARAM_INT_ARRAY)
    );
    

    복잡성을 추가하지 않고 (대부분의 ORM처럼) 데이터베이스와의 상호 작용을 모호하게하지 않는 다른 패키지가있을 수 있지만, 동시에 작은 일반적인 작업을 좀 더 쉽게 만들 수 있습니다.

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

    4.

    PDO가 $ in_values ​​내용을 단일 항목으로 취급하므로 이에 따라 이해할 수 있습니다. PDO는 1,2,3을 단일 문자열로 보게되므로 쿼리는 다음과 같이 보일 것입니다.

    SELECT * FROM 테이블 WHERE my_value IN ( "1,2,3")

    implode가 따옴표와 쉼표로 변경되면 문제가 해결 될 것이라고 생각할 수도 있지만 그렇지는 않습니다. PDO는 따옴표를보고 문자열을 인용하는 방법을 변경합니다.

    귀하의 쿼리가 첫 번째 값과 일치하는 이유에 대해서는 설명이 없습니다.

  5. ==============================

    5.

    난 그냥이 문제에 대해 와서 작은 포장지를 코딩했습니다. 그것은 내가 확신하는 가장 예쁜 코드는 아니지만 누군가에게 도움이 될 것입니다.

    function runQuery(PDO $PDO, string $sql, array $params = [])
    {
        if (!count($params)) {
            return $PDO->query($sql);
        }
    
        foreach ($params as $key => $values) {
            if (is_array($values)) {
                // get placeholder from array, e.g. ids => [7,12,3] would be ':ids'
                $oldPlaceholder  = ':'.$key;
                $newPlaceholders = '';
                $newParams = [];
                // loop through array to create new placeholders & new named parameters
                for($i = 1; $i <= count($values); $i++) {
                    // this gives us :ids1, :ids2, :ids3 etc
                    $newKey = $oldPlaceholder.$i;
                    $newPlaceholders .= $newKey.', ';
                    // this builds an associative array of the new named parameters
                    $newParams[$newKey] = $values[$i - 1];
                }
                //trim off the trailing comma and space
                $newPlaceholders = rtrim($newPlaceholders, ', ');
    
                // remove the old parameter
                unset($params[$key]);
    
                // and replace with the new ones
                $params = array_merge($params, $newParams);
    
                // amend the query
                $sql = str_replace($oldPlaceholder, $newPlaceholders, $sql);
            }
        }
    
        $statement = $PDO->prepare($sql);
        $statement->execute($params);
        return $statement;
    }
    

    예 : 다음을 전달합니다.

    SELECT * FROM users WHERE userId IN (:ids)
    
    array(1) {
      ["ids"]=>
      array(3) {
        [0]=>
        int(1)
        [1]=>
        int(2)
        [2]=>
        int(3)
      }
    }
    

    된다.

    SELECT * FROM users WHERE userId IN (:ids1, :ids2, :ids3)
    
    array(3) {
      [":ids1"]=>
      int(1)
      [":ids2"]=>
      int(2)
      [":ids3"]=>
      int(3)
    }
    

    그것은 방탄이 아니지만, 내 요구에 대한 유일한 개발자로서, 그것은 지금까지 일을 잘, 어쨌든 않습니다.

  6. from https://stackoverflow.com/questions/14767530/php-using-pdo-with-in-clause-array by cc-by-sa and MIT lisence