簡體   English   中英

PHP - 使用 PDO 和 IN 子句數組

[英]PHP - Using PDO with IN clause array

我正在使用 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)。

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 .= ($in ? "," : "") . $key; // :id0,:id1,:id2
    $in_params[$key] = $item; // collecting values into a key-value array
}

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

幸運的是,對於命名占位符,我們不必遵循嚴格的順序,因此我們可以按任何順序合並我們的數組。

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

由於 PDO 似乎沒有提供好的解決方案,您不妨考慮使用 DBAL,它主要遵循 PDO 的 API,但也添加了一些有用的功能http://docs.doctrine-project.org/projects/doctrine-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 所做的那樣),但同時使小型典型任務更容易一些。

使用閉包的 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();

我經常使用 FIND_IN_SET 而不是 IN,如下所示:

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

這不是最好的解決方案性能,但如果 $in_array 的可能數量選項有限,則通常不是問題。 我經常將它用於 my_value 是枚舉字段的狀態。 從來沒有任何問題。

我能夠做到這一點。 這個想法是一個價值來自搜索表單。 他們正在尋找東西,值可能在以下兩個字段之一中:thisField,thatField或等於someField。

$randomValue = "whatever";
$query = "SELECT * FROM myTable WHERE ((:randomValue in(thisField) or :randomValue in(thatField)) or (someField = :randomValue));";
                $statement = $this->mySQL_db->prepare($query);
                $statement->bindparam(":randomValue", $randomValue, PDO::PARAM_STR);
                $statement->bindparam(":randomValue", $randomValue, PDO::PARAM_STR);
                $statement->bindparam(":randomValue", $randomValue, PDO::PARAM_STR);

我剛剛遇到了這個問題並編寫了一個小包裝器。 我敢肯定,這不是最漂亮或最好的代碼,但它可能對某人有所幫助,所以這里是:

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

它不是萬無一失的,但作為滿足我需求的唯一開發人員,它可以很好地完成工作,無論如何。

這是未命名占位符(?)的解決方案。 如果你傳遞帶有問號的 $sql 像"A=? AND B IN(?) "和 $args 其中一些元素是像 [1, [1,2,3]] 這樣的數組,它將返回帶有適當數字的 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;
}

據我了解,這是因為 PDO 會將 $in_values 內容視為單個項目並相應地進行處理。 PDO 會將 1,2,3 視為單個字符串,因此查詢看起來像

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

您可能認為將 implode 更改為帶有引號和逗號會修復它,但事實並非如此。 PDO 將看到引號並更改它引用字符串的方式。

至於為什么您的查詢與第一個值匹配,我沒有解釋。

我使用PDO使用OR而不是IN() ,如下所示:

對於這樣的SELECT * FROM table WHERE id IN(1,2,3);

您可以在SELECT * FROM table WHERE id = 1 OR id = 3 OR id = 3;執行SELECT * FROM table WHERE id = 1 OR id = 3 OR id = 3;

因此,此代碼為:

$in_array  = array(1, 2, 3);  
$where     = '';
$pdo_param = [];

// looping through array 
foreach ($in_array as $ikey => $ival) {
    // building SQL query where
    $where .= 'id = :p_'.$ikey.' OR ' ;
    // filling param array
    $pdo_param_array[':p_'.$ikey] = $ival ;
} // end foreach 
// remove trailing OR
$where = rtrim($where, ' OR ');

$my_result = $wbdb->prepare("SELECT * FROM my_table WHERE ".$where);
$my_result->execute($pdo_param_array);
$my_results = $my_result->fetchAll();

這是我的完整代碼,對於我愚蠢的編碼、糟糕的結構和注釋行感到抱歉。 也許有人重新編碼了我的愚蠢代碼:)

發送到課堂:

$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 in için 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;
    }
}

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM