PHP-IN 절 배열과 함께 PDO 사용


82

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

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


이것을 참조하십시오 . 도움이 될 수 있는지 확인하십시오.
Bhavik Shah 2013

배열 매개 변수를 처리 할 수있는 API에 대해서는 github.com/morris/dop 에서 확인하십시오 (NULL 및 SQL 조각과 같은 그 이상).
morris4

답변:


114

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 = "";
$i = 0;// we are using an external counter 
// because the actual array keys could be dangerous
foreach ($ids as $item)
{
    $key = ":id".$i++;
    $in .= "$key,";
    $in_params[$key] = $item; // collecting values into a 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();

다행히도 명명 된 자리 표시 자의 경우 엄격한 순서를 따를 필요가 없으므로 배열을 어떤 순서로든 병합 할 수 있습니다.


1
확실한. 한동안 그 질문에 대한 답이 없었기 때문에 직접 작성해야했습니다. 내 프로필에서 링크를 찾을 수 있습니다. 사용법이나 문제에 관한 질문이 있으시면 언제든지 문의하십시오.
당신의 상식

2
당신은 무엇을 설명 할 수 -1애프터입니다 count()?
Robert Rocha 2016 년

2
@RobertRocha 나는 자리 표시 자 배열을 생성하는 더 깨끗한 방법이 될 수 있다고 생각합니다 $in = implode(',', array_fill(0, count($in_array), '?'));-이상한 off-by-one 또는 문자열 연결 오류를
피하는 것

1
@YourCommonSense는 이러한 일반적인 문제에 대한 최상의 답변입니다. 감사합니다!
digitai

1
-1없이$in = rtrim(str_repeat('?,', count($array)), ',');
Bossman

15

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)) {
    // ...
}

count ($variables) - 1) . '?';그냥 왜count($variable)
로버트 호샤

2
@RobertRocha 당신은 변수가보다 하나 적은 수의 쉼표가 필요하기 때문에
GordonM

배열을 사용하여 실행 문에 다른 변수를 추가하려고 시도했지만 작동하지 않습니다.
Usama Ahmed

4

PDO가 좋은 솔루션을 제공하지 않는 것 같으므로 대부분 PDO의 API를 따르지만 유용한 기능 http://docs.doctrine-project.org/projects/doctrine-dbal/을 추가하는 DBAL 사용을 고려하는 것이 좋습니다. en / latest / reference / data-retrieval-and-manipulation.html # list-of-parameters-conversion

$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처럼) 다른 패키지가있을 수 있지만 동시에 작은 일반적인 작업을 조금 더 쉽게 만듭니다.


2

클로저를 사용하는 PHP 망상 (@ your-common-sense)의 대체 버전 :

$filter      = ["min_price" => "1.98"];
$editions    = [1,2,10];

$editions = array_combine(
    array_map(function($i){ return ':id'.$i; }, array_keys($editions)),
    $editions
);
$in_placeholders = implode(',', array_keys($editions));
$sql = "SELECT * FROM books WHERE price >= :min_price AND edition IN ($in_placeholders)";
$stm = $pdo->prepare($sql);
$stm->execute(array_merge($filter,$editions));
$data = $stm->fetchAll();

0

내가 이해하는 것은 PDO가 $ in_values ​​내용을 단일 항목으로 취급하고 그에 따라 꽤 할 것이기 때문입니다. PDO는 1,2,3을 단일 문자열로 인식하므로 쿼리는 다음과 같습니다.

SELECT * FROM table WHERE my_value IN ( "1,2,3")

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

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


0

나는 방금이 문제에 맞서 작은 래퍼를 코딩했습니다. 내가 확신하는 가장 예쁘거나 최고의 코드는 아니지만 누군가에게 도움이 될 수 있으므로 여기에 있습니다.

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

방탄은 아니지만 내 필요에 대한 유일한 개발자로서 어쨌든 지금까지 괜찮습니다.


0

다음은 이름없는 자리 표시 자 (?)에 대한 솔루션입니다. "A =? AND B IN (?)" 과 같은 물음표와 함께 $ sql을 전달 하고 일부 요소가 [1, [1,2,3]]과 같은 배열 인 $ args를 전달하면 적절한 숫자의 SQL 문자열이 반환됩니다. 자리 표시 자 수- "A =? AND B IN (?,?,?)" . $ args 매개 변수는 배열 요소와 필요한 자리 표시 자 수를 찾는 데만 필요합니다. 쿼리를 실행할이 메서드를 사용하여 작은 PDO 확장 클래스를 찾을 수 있습니다. https://github.com/vicF/pdo/blob/master/src/PDO.php

public function replaceArrayPlaceholders($sql, $args)
{
    $num = 0;
    preg_match_all('/\?/', $sql, $matches, PREG_OFFSET_CAPTURE);  // Captures positions of placeholders
    //echo $matches[0][1][1];
    $replacements = [];
    foreach($args as $arg) {
        if(is_array($arg)) {
            $replacements[$matches[0][$num][1]] = implode(',',array_fill(0, count($arg), '?')); // Create placeholders string
        }
        $num++;
    }
    krsort($replacements);
    foreach($replacements as $position => $placeholders) {
        $sql = substr($sql, 0, $position).$placeholders.substr($sql, $position+1); // Replace single placeholder with multiple
    }
    return $sql;
}

0

내 어리석은 코딩, 잘못된 구조 및 주석 행에 대해 죄송합니다. 누군가 내 어리석은 코드를 다시 코딩 할 수도 있습니다. :)

수업에 보내기 :

$veri_sinifi = new DB_Connect;
                          $veri_sorgu_degerleri=array(
                            "main_user_id" => (int)$_SESSION['MM_UserId'],
                            "cari_grup_id" => [71,72,73],
                            "cari_grup_adi" => ['fatih','ahmet','ali']                       
                          );                              
                          $siteler =$veri_sinifi->query("Select cari_grup_adi,cari_grup_id FROM m_cari_gruplar WHERE main_user_id= :main_user_id and cari_grup_id in (:cari_grup_id) and cari_grup_adi in (:cari_grup_adi)",$veri_sorgu_degerleri) ; 

클래스는이 SQL을 얻습니다.

Select cari_grup_adi,cari_grup_id FROM m_cari_gruplar WHERE main_user_id= :main_user_id and cari_grup_id in (:cari_grup_id) and cari_grup_adi in (:cari_grup_adi) 

클래스는이 SQL을 이것으로 변환합니다.

Select cari_grup_adi,cari_grup_id FROM m_cari_gruplar WHERE main_user_id= :main_user_id and cari_grup_id in (:cari_grup_id0,:cari_grup_id1,:cari_grup_id2) and cari_grup_adi in (:cari_grup_adi0,:cari_grup_adi1,:cari_grup_adi2)

클래스 바인딩 매개 변수 :

 Select cari_grup_adi,cari_grup_id FROM m_cari_gruplar WHERE main_user_id= 1 and cari_grup_id in (71,72,73) and cari_grup_adi in ('fatih','ahmet','ali')

암호:

class DB_Connect{
var $dbh;
function __construct(){
    $host = "";
    $db = "";
    $user = "";
    $password = "";
    $this -> dbh = $this -> db_connect($host, $db, $user, $password);
}
public function getDBConnection(){
    return $this -> dbh;
}
protected function db_connect($host, $db, $user, $password){
    //var_dump($host, $db, $user, $password);exit();
    try {
        $dbh = new PDO("mysql:host=$host;dbname=$db", $user, $password);
    }
    catch(PDOException $err) {
        echo "Error: ".$err->getMessage()."<br/>";
        die();
    }
    return $dbh;
}
public function query($statement,$bind_params){
    $keyword = substr(strtoupper($statement), 0, strpos($statement, " ")); // sql in en başındaki kelimeye bakıyor SELECT UPDATE vs gibi ordaki ilk boşluğa kadar olan kelimeyi alıyor.
  //echo $keyword;
    $dbh = $this->getDBConnection();        
    if($dbh){
        try{
            $sql = $statement;
            /*GELEN PARAMETRELERE BAKIP İÇİNDE ARRAY VAR İSE SQL STATEMENT KISMINI ONA GÖRE DEĞİŞTİRİYORUZ.
            Alttaki döngünün yaptığı işlem şu. Eğer alttaki gibi bir sorgu değerleri gönderilirse
            $veri_sorgu_degerleri=array(
                                    "main_user_id" => (int)$_SESSION['MM_UserId'],
                                    "cari_grup_id" => [71,72,73],
                                    "cari_grup_adi" => ['fatih','ahmet','ali']                       
                                  );    
            burada main_user_id tek bir değer diğerleri sise array olarak gönderiliyor. Where IN sorgusu birden fazla değer alabileceği için bunları PDO kabul ederken string olarak kabul ediyor yani yukardaki 71,72,73 değerini tırnak içine tek değermiş gib '71,72,73' şeklinde alıyor yapılması gereken sorgunun değiştirilmesi. bu döngü ile 

            Select cari_grup_adi,cari_grup_id FROM m_cari_gruplar WHERE main_user_id= :main_user_id and cari_grup_id in (:cari_grup_id) and cari_grup_adi in (:cari_grup_adi) 

            şeklindeki sorgu in kısımları değiştirilerek

            Select cari_grup_adi,cari_grup_id FROM m_cari_gruplar WHERE main_user_id= :main_user_id and cari_grup_id in (:cari_grup_id0,:cari_grup_id1,:cari_grup_id2) and cari_grup_adi in (:cari_grup_adi0,:cari_grup_adi1,:cari_grup_adi2)

            halini alıyor bir sonraki foreach de ise yine benzer yapı ile arary olarak gelen değerler inin tek tek bind ediliyor, normal gelen değerler ise normal bind yapılıyor.


            */
            foreach($bind_params as $paramkey => $param_value) {
              //echo "Key=" . $paramkey ."-".($param_value)."-, Value=" . $param_value;
              //echo "<br>";                 
              //echo "is_numeric($param_value)>".is_numeric($param_value)."<br>";
              //echo "is_string($param_value)>".is_string($param_value)."<br>";
              //echo "is_array($param_value)>".is_array($param_value)."<br>";
              $in_key="";
              $in_parameters="";
              if (is_array($param_value)) // Gelan parametre array ise yeniden yapılandır.
              {
              foreach ($param_value as $i => $item)
                {
                    $in_key = ":$paramkey".$i;
                    //echo "<br>$in_key = ".$paramkey.".$i";
                    $in_parameters .= "$in_key,";
                    //echo "<br>$in_parameters = ".$in_key;
                }
                $in_parameters = rtrim($in_parameters,","); // :id0,:id1,:id2
                //echo "<br>in_parameters>$in_parameters";
                $sql = str_replace(":".$paramkey, $in_parameters,$sql);
                //echo "<br>oluşan sql>".$sql."<br>"; 
               }
            }
            $sql = $dbh->prepare($sql);
            foreach($bind_params as $paramkey => $param_value) {
              //echo "Key=" . $paramkey ."-".($param_value)."-, Value=" . $param_value;
              //echo "<br>";                 
              //echo "is_numeric($param_value)>".is_numeric($param_value)."<br>";
              //echo "is_string($param_value)>".is_string($param_value)."<br>";
              //echo "is_array($param_value)>".is_array($param_value)."<br>";
              if (is_numeric($param_value)==1)  // gelen veri  numerik ise
                {
                    $param_value = (int)$param_value;
                    $pdo_param_type = PDO::PARAM_INT;
                } 
              elseif (is_string($param_value)==1)// gelen veri  string ise
                {$pdo_param_type = PDO::PARAM_STR; }
              if (is_array($param_value)) // gelen veri array tipinde ise
              {
              foreach ($param_value as $i => $param_array_value) // bu döngünün açıklaması yukardaki döngü için yazılan açıklama içinde mevcut
                {
                    $in_key = ":$paramkey".$i;
                                if (is_numeric($param_array_value)==1)  // gelen veri  numerik ise
                                    {
                                        $param_array_value = (int)$param_array_value;
                                        $pdo_param_type = PDO::PARAM_INT;
                                    } 
                                  elseif (is_string($param_array_value)==1)// gelen veri  string ise
                                    {$pdo_param_type = PDO::PARAM_STR; }
                                    $sql->bindValue($in_key, $param_array_value, $pdo_param_type );
                    //echo "<br>oldu1";
                    //echo "<br> -$in_key-";
                }
                //$sql = str_replace(":".$paramkey, $in_parameters, $sql);
                //echo "oluşan sql>".$sql."<br>"; 
               }
              else // array değilse aşağıdaki şekilde bind yap.
                {
                  //echo "<br>oldu2";
                  $sql->bindValue(":$paramkey", $param_value, $pdo_param_type ); // bindparam foreach içinde kullanılmaz çünkü execute esnasında bind yaptığı için yani anlık olarak değerleri atamaddığı için for döngüsünde en sonda value değişkeni neyse tüm parametrelere onu atıyor, bu sebeple bindvalue kullanıyoruz.PDO::PARAM_INT
                }
            } // foreach                
            $exe = $sql->execute();
            $sql->debugDumpParams();
            //echo $exe;

        }
        catch(PDOException $err){
            return $err->getMessage();
        }

        //BU KISMA AİT AÇIKLAMA AŞAĞIDAIR.
        switch($keyword){ // sorgu çalıştıktan sonra sorgu sonucuna göre gerekli işlemler yapılıyor.
            case "SELECT":  // Sorgu select sorgusu ise                
                $result = array(); //sonuçları diziye aktaracak.
                while($row = $sql->fetch(PDO::FETCH_ASSOC)){     // sonuç satırlarını tek tek okuyup                   
                    //echo $row;
                    $result[] = $row; // her bir satırı dizinin bir elemanına aktarıyor.bu değer diziden nasıl okunur açıklaması aşağıda                      
                }
                return $result; // sorgudan dönen diziyi doğrudan ana programa aktarıyor orada dizi olarak okunabilir.                
                break;
            default: 
                return $exe;
                break;
        }
    }
    else{
        return false;
    }
}

}

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