Bisakah saya mengikat array ke kondisi IN ()?

562

Saya ingin tahu apakah mungkin untuk mengikat array nilai ke placeholder menggunakan PDO. Use case di sini mencoba untuk melewatkan array nilai untuk digunakan dengan suatu IN()kondisi.

Saya ingin dapat melakukan sesuatu seperti ini:

<?php
$ids=array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(:an_array)'
);
$stmt->bindParam('an_array',$ids);
$stmt->execute();
?>

Dan minta PDO mengikat dan mengutip semua nilai dalam array.

Saat ini saya sedang melakukan:

<?php
$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
foreach($ids as &$val)
    $val=$db->quote($val); //iterate through array and quote
$in = implode(',',$ids); //create comma separated list
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN('.$in.')'
);
$stmt->execute();
?>

Yang pasti berhasil, tetapi hanya ingin tahu apakah ada solusi bawaan yang saya lewatkan?

Andru
sumber
3
Panduan lengkap tentang pengikatan array ke kondisi IN () , termasuk case ketika Anda memiliki placeholder lain dalam kueri
Your Common Sense
Pertanyaan ditutup sebagai duplikat dari pertanyaan ini . Saya membalikkan bendera duplikat karena pertanyaan ini 4 tahun lebih tua, memiliki 4 kali pandangan, 3 kali jumlah jawaban, dan 12 kali skor. Ini jelas merupakan target yang unggul.
miken32
Siapa pun yang melihatnya pada tahun 2020: Anda dapat mencoba github.com/morris/dop untuk itu.
morris4

Jawaban:

262

Saya pikir soulmerge benar. Anda harus membuat string-kueri.

<?php
$ids     = array(1, 2, 3, 7, 8, 9);
$inQuery = implode(',', array_fill(0, count($ids), '?'));

$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(' . $inQuery . ')'
);

// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();
?>

fix: dan, kamu benar memperbaiki kode (meskipun tidak mengujinya)

sunting: baik chris (komentar) dan seseorang tidak disarankan menyarankan bahwa foreach-loop ...

(...)
// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();

... mungkin berlebihan, sehingga foreachloop dan $stmt->executebisa diganti dengan hanya ...

<?php 
  (...)
  $stmt->execute($ids);
?>

(lagi, saya tidak mengujinya)

STFs
sumber
7
Itu solusi yang menarik, dan sementara saya lebih suka untuk mengulangi id dan memanggil PDO :: quote (), saya pikir indeks '?' placeholder akan kacau jika ada placeholder lain di tempat lain dalam kueri, bukan?
Andru
5
ya, itu akan menjadi masalah. tetapi dalam hal ini Anda dapat membuat parameter bernama bukan?
stefs
9
Pertanyaan lama, tapi perlu dicatat saya percaya, adalah bahwa $foreachdan bindValue()tidak diperlukan - jalankan dengan array. Misalnya:$stmt->execute($ids);
Chris
2
Menghasilkan placeholder harus dilakukan seperti inistr_repeat('?,', count($array) - 1). '?';
Xeoncross
8
Hanya tip untuk mereka yang tidak sadar, Anda tidak dapat mencampur parameter bernama dan tidak bernama. Karenanya, jika Anda menggunakan parameter bernama dalam kueri, alihkan ke? Dan kemudian tambahkan indeks bindValue Anda agar sesuai dengan posisi IN? Dengan di mana mereka relatif terhadap yang lain? params.
justinl
169

Untuk sesuatu yang cepat:

//$db = new PDO(...);
//$ids = array(...);

$qMarks = str_repeat('?,', count($ids) - 1) . '?';
$sth = $db->prepare("SELECT * FROM myTable WHERE id IN ($qMarks)");
$sth->execute($ids);
DonVaughn
sumber
7
Luar biasa, saya tidak berpikir untuk menggunakan argumen input_parameters dengan cara ini. Bagi mereka yang kueri memiliki lebih banyak parameter daripada daftar IN, Anda bisa menggunakan array_unshift dan array_push untuk menambahkan argumen yang diperlukan ke bagian depan dan akhir array. Juga, saya lebih suka $input_list = substr(str_repeat(',?', count($ids)), 1);
orca
7
Anda juga bisa mencoba str_repeat('?,', count($ids) - 1) . '?'. Satu panggilan fungsi kurang.
orca
5
@ erling, ini adalah pernyataan yang disiapkan, dari mana injeksi akan datang? Saya akan dengan senang hati melakukan koreksi jika Anda dapat mendukungnya dengan beberapa bukti nyata tentang itu.
DonVaughn
5
@ erling, ya, itu benar, dan mengikat params adalah persis apa yang kita lakukan dalam contoh ini dengan mengirimkan executearray id
DonVaughn
5
Oh memang. Entah bagaimana, melewatkan fakta bahwa Anda melewatkan array. Tampaknya ini memang aman dan jawaban yang bagus. Permintaan maaf saya.
erfling
46

Apakah penting menggunakan INpernyataan? Coba gunakan FIND_IN_SETop.

Misalnya, ada permintaan di PDO seperti itu

SELECT * FROM table WHERE FIND_IN_SET(id, :array)

Maka Anda hanya perlu mengikat array nilai, meledak dengan koma, seperti ini

$ids_string = implode(',', $array_of_smth); // WITHOUT WHITESPACES BEFORE AND AFTER THE COMMA
$stmt->bindParam('array', $ids_string);

dan sudah selesai.

UPD: Seperti yang ditunjukkan beberapa orang di komentar atas jawaban ini, ada beberapa masalah yang harus dinyatakan secara eksplisit.

  1. FIND_IN_SETtidak menggunakan indeks dalam tabel, dan masih belum diimplementasikan - lihat catatan ini di pelacak bug MYSQL . Terima kasih kepada @BillKarwin untuk pemberitahuannya.
  2. Anda tidak dapat menggunakan string dengan koma di dalam sebagai nilai array untuk pencarian. Tidak mungkin mengurai string seperti itu dengan benar setelah implodeAnda menggunakan simbol koma sebagai pemisah. Terima kasih kepada @VaL untuk catatannya.

Baik, jika Anda tidak terlalu bergantung pada indeks dan tidak menggunakan string dengan koma untuk pencarian, solusi saya akan jauh lebih mudah, lebih sederhana, dan lebih cepat daripada solusi yang tercantum di atas.

Tim Tonkonogov
sumber
25
IN () dapat menggunakan indeks, dan dihitung sebagai pemindaian rentang. FIND_IN_SET () tidak dapat menggunakan indeks.
Bill Karwin
1
Itu intinya. Saya tidak tahu ini. Namun bagaimanapun juga tidak ada persyaratan untuk kinerja dalam pertanyaan tersebut. Untuk tabel yang tidak terlalu besar, ini jauh lebih baik dan lebih bersih daripada kelas terpisah untuk menghasilkan kueri dengan jumlah placeholder yang berbeda.
Tim Tonkonogov
11
Ya, tetapi siapa yang memiliki meja yang tidak terlalu besar hari ini? ;-)
Bill Karwin
3
Masalah lain dengan pendekatan ini adalah bagaimana jika akan ada string dengan koma di dalam? Misalnya ... FIND_IN_SET(description,'simple,search')akan berfungsi, tetapi FIND_IN_SET(description,'first value,text, with coma inside')akan gagal. Jadi fungsi akan mencari "first value", "text", "with coma inside" bukannya yang diinginkan"first value", "text, with coma inside"
VaL
33

Karena saya melakukan banyak pertanyaan dinamis, ini adalah fungsi pembantu super sederhana yang saya buat.

public static function bindParamArray($prefix, $values, &$bindArray)
{
    $str = "";
    foreach($values as $index => $value){
        $str .= ":".$prefix.$index.",";
        $bindArray[$prefix.$index] = $value;
    }
    return rtrim($str,",");     
}

Gunakan seperti ini:

$bindString = helper::bindParamArray("id", $_GET['ids'], $bindArray);
$userConditions .= " AND users.id IN($bindString)";

Mengembalikan string :id1,:id2,:id3dan juga memperbarui $bindArrayikatan yang Anda perlukan saat saatnya menjalankan kueri. Mudah!

prograhammer
sumber
6
Ini adalah solusi yang jauh lebih baik, karena tidak memutus pengikatan aturan parameter. Ini jauh lebih aman daripada memiliki sql sebaris seperti yang diusulkan oleh beberapa orang lain di sini.
Dimitar Darazhanski
1
Keangkeran. Anggun. Sempurna.
Ricalsin
17

Solusi dari EvilRygy tidak berhasil untuk saya. Di Postgres Anda dapat melakukan solusi lain:


$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id = ANY (string_to_array(:an_array, ','))'
);
$stmt->bindParam(':an_array', implode(',', $ids));
$stmt->execute();
Sergey Galkin
sumber
Ini tidak bekerja: ERROR: operator does not exist: integer = text. Setidaknya Anda perlu menambahkan casting eksplisit.
collimarco
17

cara yang sangat bersih untuk postgres menggunakan postgres-array ("{}"):

$ids = array(1,4,7,9,45);
$param = "{".implode(', ',$ids)."}";
$cmd = $db->prepare("SELECT * FROM table WHERE id = ANY (?)");
$result = $cmd->execute(array($param));
ESCOBAR
sumber
itu memblokir injeksi sql?
Fábio Zangirolami
1
@ FábioZangirolami itu PDO, jadi ya.
ESCOBAR
YA, PDO! setelah 4 tahun. Respons Anda baik untuk saya, sangat sederhana dan efektif. Terima kasih!!!
Fábio Zangirolami
14

Inilah solusi saya:

$total_items = count($array_of_items);
$question_marks = array_fill(0, $total_items, '?');
$sql = 'SELECT * FROM foo WHERE bar IN (' . implode(',', $question_marks ). ')';

$stmt = $dbh->prepare($sql);
$stmt->execute(array_values($array_of_items));

Perhatikan penggunaan array_values. Ini dapat memperbaiki masalah pemesanan utama.

Saya menggabungkan array id dan kemudian menghapus item duplikat. Saya punya sesuatu seperti:

$ids = array(0 => 23, 1 => 47, 3 => 17);

Dan itu gagal.

Progrock
sumber
Ini adalah solusi yang sangat baik. Ini tidak seperti membangun string kueri, ini menggunakan pengikatan dengan benar. Saya sangat merekomendasikan yang ini.
Lindylead
Catatan: Menggunakan solusi Anda, Anda dapat menambahkan item ke depan atau belakang array, sehingga Anda dapat menyertakan binding lainnya. $original_array Tambahkan item sebelum array asli: array_unshift($original_array, new_unrelated_item); Tambahkan item setelah array asli: array_push($original_array, new_unrelated_item); Ketika nilai-nilai terikat, item new_unrelated akan ditempatkan di lokasi yang benar. Ini memungkinkan Anda untuk mencampur item Array dan non-array. `
Lindylead
12

Saya memperpanjang PDO untuk melakukan sesuatu yang mirip dengan yang disarankan stefs, dan itu lebih mudah bagi saya dalam jangka panjang:

class Array_Capable_PDO extends PDO {
    /**
     * Both prepare a statement and bind array values to it
     * @param string $statement mysql query with colon-prefixed tokens
     * @param array $arrays associatve array with string tokens as keys and integer-indexed data arrays as values 
     * @param array $driver_options see php documention
     * @return PDOStatement with given array values already bound 
     */
    public function prepare_with_arrays($statement, array $arrays, $driver_options = array()) {

        $replace_strings = array();
        $x = 0;
        foreach($arrays as $token => $data) {
            // just for testing...
            //// tokens should be legit
            //assert('is_string($token)');
            //assert('$token !== ""');
            //// a given token shouldn't appear more than once in the query
            //assert('substr_count($statement, $token) === 1');
            //// there should be an array of values for each token
            //assert('is_array($data)');
            //// empty data arrays aren't okay, they're a SQL syntax error
            //assert('count($data) > 0');

            // replace array tokens with a list of value tokens
            $replace_string_pieces = array();
            foreach($data as $y => $value) {
                //// the data arrays have to be integer-indexed
                //assert('is_int($y)');
                $replace_string_pieces[] = ":{$x}_{$y}";
            }
            $replace_strings[] = '('.implode(', ', $replace_string_pieces).')';
            $x++;
        }
        $statement = str_replace(array_keys($arrays), $replace_strings, $statement);
        $prepared_statement = $this->prepare($statement, $driver_options);

        // bind values to the value tokens
        $x = 0;
        foreach($arrays as $token => $data) {
            foreach($data as $y => $value) {
                $prepared_statement->bindValue(":{$x}_{$y}", $value);
            }
            $x++;
        }

        return $prepared_statement;
    }
}

Anda bisa menggunakannya seperti ini:

$db_link = new Array_Capable_PDO($dsn, $username, $password);

$query = '
    SELECT     *
    FROM       test
    WHERE      field1 IN :array1
     OR        field2 IN :array2
     OR        field3 = :value
';

$pdo_query = $db_link->prepare_with_arrays(
    $query,
    array(
        ':array1' => array(1,2,3),
        ':array2' => array(7,8,9)
    )
);

$pdo_query->bindValue(':value', '10');

$pdo_query->execute();
Chris
sumber
1
Saya sudah membahas bagian pertama dari komentar Markus, tetapi ketika dia menunjukkan, masih tidak aman jika token like :arrayada dalam string dalam kueri.
Chris
4
Catatan untuk semua pembaca di masa depan: Solusi ini tidak boleh digunakan. Pemberitahuan tidak dimaksudkan untuk kode produksi
Your Common Sense
1
YCS: terima kasih atas umpan baliknya, tertarik dengan pendapat Anda tentang pendekatan di luar kesesuaian pernyataan.
Chris
1
Idenya hampir sama tetapi hanya tanpa penegasan dan cara yang lebih mudah dan eksplisit - bukan sebagai pengecualian untuk hanya satu kasus tetapi sebagai cara umum membangun setiap permintaan. Setiap placeholder ditandai dengan tipenya. Itu membuat menebak (seperti if (is_array($data))satu) tidak perlu namun membuat cara pemrosesan data lebih akurat.
Akal Sehat Anda
1
Kepada siapa pun yang membaca komentar: masalah yang disebutkan oleh @Anda Common Sense telah diperbaiki dalam revisi 4 .
user2428118
11

Melihat PDO: Konstanta yang Ditentukan sebelumnya tidak ada PDO :: PARAM_ARRAY yang Anda perlukan seperti yang tercantum di PDOStatement-> bindParam

bool PDOStatement :: bindParam (mixed $ parameter, mixed & $ variable [, int $ data_type [, int $ length [, mixed $ driver_options]]])

Jadi saya pikir itu tidak mungkin.


sumber
1
Saya tidak tahu apakah itu berhasil. Saya akan menebak bahwa string yang meledak akan dikutip.
soulmerge
2
Anda benar, tanda kutip lolos sehingga tidak akan berfungsi. Saya telah menghapus kode itu.
11

Ketika Anda memiliki parameter lain, Anda dapat melakukan ini:

$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
$query = 'SELECT *
            FROM table
           WHERE X = :x
             AND id IN(';
$comma = '';
for($i=0; $i<count($ids); $i++){
  $query .= $comma.':p'.$i;       // :p0, :p1, ...
  $comma = ',';
}
$query .= ')';

$stmt = $db->prepare($query);
$stmt->bindValue(':x', 123);  // some value
for($i=0; $i<count($ids); $i++){
  $stmt->bindValue(':p'.$i, $ids[$i]);
}
$stmt->execute();
Daniel Miloca - Brasil
sumber
Terima kasih atas jawabannya. Ini adalah satu-satunya yang benar-benar bekerja untuk saya. Namun, saya melihat 1 kesalahan. Variabel $ rs harus $ stmt
Piet
11

Bagi saya solusi yang lebih seksi adalah dengan membangun array asosiatif yang dinamis & menggunakannya

// A dirty array sent by user
$dirtyArray = ['Cecile', 'Gilles', 'Andre', 'Claude'];

// we construct an associative array like this
// [ ':name_0' => 'Cecile', ... , ':name_3' => 'Claude' ]
$params = array_combine(
    array_map(
        // construct param name according to array index
        function ($v) {return ":name_{$v}";},
        // get values of users
        array_keys($dirtyArray)
    ),
    $dirtyArray
);

// construct the query like `.. WHERE name IN ( :name_1, .. , :name_3 )`
$query = "SELECT * FROM user WHERE name IN( " . implode(",", array_keys($params)) . " )";
// here we go
$stmt  = $db->prepare($query);
$stmt->execute($params);
RousseauAlexandre
sumber
Sulit dipastikan tanpa mencobanya dalam skenario nyata, tetapi tampaknya baik-baik saja. +1
Anant Singh --- Alive to Die
9

Saya juga menyadari bahwa utas ini sudah lama tetapi saya memiliki masalah unik di mana, ketika mengubah driver mysql yang akan segera ditinggalkan ke driver PDO saya harus membuat fungsi yang dapat membangun, secara dinamis, baik param normal dan INs dari yang sama. array param. Jadi saya cepat-cepat membangun ini:

/**
 * mysql::pdo_query('SELECT * FROM TBL_WHOOP WHERE type_of_whoop IN :param AND siz_of_whoop = :size', array(':param' => array(1,2,3), ':size' => 3))
 *
 * @param $query
 * @param $params
 */
function pdo_query($query, $params = array()){

    if(!$query)
        trigger_error('Could not query nothing');

    // Lets get our IN fields first
    $in_fields = array();
    foreach($params as $field => $value){
        if(is_array($value)){
            for($i=0,$size=sizeof($value);$i<$size;$i++)
                $in_array[] = $field.$i;

            $query = str_replace($field, "(".implode(',', $in_array).")", $query); // Lets replace the position in the query string with the full version
            $in_fields[$field] = $value; // Lets add this field to an array for use later
            unset($params[$field]); // Lets unset so we don't bind the param later down the line
        }
    }

    $query_obj = $this->pdo_link->prepare($query);
    $query_obj->setFetchMode(PDO::FETCH_ASSOC);

    // Now lets bind normal params.
    foreach($params as $field => $value) $query_obj->bindValue($field, $value);

    // Now lets bind the IN params
    foreach($in_fields as $field => $value){
        for($i=0,$size=sizeof($value);$i<$size;$i++)
            $query_obj->bindValue($field.$i, $value[$i]); // Both the named param index and this index are based off the array index which has not changed...hopefully
    }

    $query_obj->execute();

    if($query_obj->rowCount() <= 0)
        return null;

    return $query_obj;
}

Ini masih belum teruji namun nampaknya logika ada di sana.

Semoga ini membantu seseorang di posisi yang sama,

Sunting: Setelah beberapa pengujian saya menemukan:

  • PDO tidak suka '.' atas nama mereka (yang agak bodoh jika Anda bertanya kepada saya)
  • bindParam adalah fungsi yang salah, bindValue adalah fungsi yang tepat.

Kode diedit ke versi yang berfungsi.

Sammaye
sumber
8

Sedikit mengedit tentang kode Schnalle

<?php
$ids     = array(1, 2, 3, 7, 8, 9);
$inQuery = implode(',', array_fill(0, count($ids)-1, '?'));

$db   = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(' . $inQuery . ')'
);

foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();
?>

//implode(',', array_fill(0, count($ids)-1), '?')); 
//'?' this should be inside the array_fill
//$stmt->bindValue(($k+1), $in); 
// instead of $in, it should be $id
Aaron Angelo Vicuna
sumber
1
Saya harus menghapus -1 setelah hitungan ($ id) agar bisa berfungsi untuk saya atau akan selalu ada satu placeholder yang hilang.
Marcel Burkhard
7

Database apa yang Anda gunakan? Di PostgreSQL saya suka menggunakan ANY (array). Jadi, gunakan kembali contoh Anda:

<?php
$ids=array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id = ANY (:an_array)'
);
$stmt->bindParam('an_array',$ids);
$stmt->execute();
?>

Sayangnya ini cukup non-portabel.

Pada basis data lain, Anda harus membuat keajaiban sendiri seperti yang disebutkan orang lain. Anda tentu ingin menempatkan logika itu ke dalam kelas / fungsi untuk membuatnya dapat digunakan kembali di seluruh program Anda, tentu saja. Lihatlah komentar pada mysql_queryhalaman di PHP.NET untuk beberapa pemikiran lebih lanjut tentang subjek dan contoh skenario ini.

Ryan Bair
sumber
4

Setelah mengalami masalah yang sama, saya pergi ke solusi yang lebih sederhana (meskipun masih tidak seanggun PDO::PARAM_ARRAY):

diberikan array $ids = array(2, 4, 32):

$newparams = array();
foreach ($ids as $n => $val){ $newparams[] = ":id_$n"; }

try {
    $stmt = $conn->prepare("DELETE FROM $table WHERE ($table.id IN (" . implode(", ",$newparams). "))");
    foreach ($ids as $n => $val){
        $stmt->bindParam(":id_$n", intval($val), PDO::PARAM_INT);
    }
    $stmt->execute();

... dan seterusnya

Jadi jika Anda menggunakan array nilai campuran, Anda akan memerlukan lebih banyak kode untuk menguji nilai Anda sebelum menetapkan param tipe:

// inside second foreach..

$valuevar = (is_float($val) ? floatval($val) : is_int($val) ? intval($val) :  is_string($val) ? strval($val) : $val );
$stmt->bindParam(":id_$n", $valuevar, (is_int($val) ? PDO::PARAM_INT :  is_string($val) ? PDO::PARAM_STR : NULL ));

Tetapi saya belum menguji yang ini.

alan_mm
sumber
4

Seperti yang saya tahu tidak ada kemungkinan untuk mengikat array ke pernyataan PDO.

Tetapi ada 2 solusi umum:

  1. Gunakan Placeholder Posisional (?,?,?,?) Atau Namholder Placeholder (: id1,: id2,: id3)

    $ whereIn = implode (',', array_fill (0, count ($ ids), '?'));

  2. Array kutipan sebelumnya

    $ whereIn = array_map (array ($ db, 'quote'), $ ids);

Kedua opsi itu baik dan aman. Saya lebih suka yang kedua karena lebih pendek dan saya bisa parameter var_dump jika saya membutuhkannya. Menggunakan placeholder Anda harus mengikat nilai dan pada akhirnya kode SQL Anda akan sama.

$sql = "SELECT * FROM table WHERE id IN ($whereIn)";

Dan yang terakhir dan penting bagi saya adalah menghindari kesalahan "jumlah variabel terikat tidak cocok dengan jumlah token"

Doktrin ini adalah contoh bagus menggunakan placeholder placeholder, hanya karena memiliki kontrol internal atas parameter yang masuk.

Oleg Matei
sumber
4

Jika kolom hanya bisa berisi bilangan bulat, Anda mungkin bisa melakukan ini tanpa placeholder dan langsung memasukkan id dalam kueri. Anda hanya perlu membuang semua nilai array ke integer. Seperti ini:

$listOfIds = implode(',',array_map('intval', $ids));
$stmt = $db->prepare(
    "SELECT *
     FROM table
     WHERE id IN($listOfIds)"
);
$stmt->execute();

Ini seharusnya tidak rentan terhadap injeksi SQL.

Kodos Johnson
sumber
3

ini solusinya. Saya juga telah memperluas kelas PDO:

class Db extends PDO
{

    /**
     * SELECT ... WHERE fieldName IN (:paramName) workaround
     *
     * @param array  $array
     * @param string $prefix
     *
     * @return string
     */
    public function CreateArrayBindParamNames(array $array, $prefix = 'id_')
    {
        $newparams = [];
        foreach ($array as $n => $val)
        {
            $newparams[] = ":".$prefix.$n;
        }
        return implode(", ", $newparams);
    }

    /**
     * Bind every array element to the proper named parameter
     *
     * @param PDOStatement $stmt
     * @param array        $array
     * @param string       $prefix
     */
    public function BindArrayParam(PDOStatement &$stmt, array $array, $prefix = 'id_')
    {
        foreach($array as $n => $val)
        {
            $val = intval($val);
            $stmt -> bindParam(":".$prefix.$n, $val, PDO::PARAM_INT);
        }
    }
}

Berikut ini adalah contoh penggunaan untuk kode di atas:

$idList = [1, 2, 3, 4];
$stmt = $this -> db -> prepare("
  SELECT
    `Name`
  FROM
    `User`
  WHERE
    (`ID` IN (".$this -> db -> CreateArrayBindParamNames($idList)."))");
$this -> db -> BindArrayParam($stmt, $idList);
$stmt -> execute();
foreach($stmt as $row)
{
    echo $row['Name'];
}

Biarkan aku tahu apa yang Anda pikirkan

Lippai Zoltan
sumber
Lupa menyebutkan bahwa ini didasarkan pada jawaban user2188977, di bawah ini.
Lippai Zoltan
Saya tidak yakin apa itu getOne (), sepertinya tidak menjadi bagian dari PDO. Saya sudah melihatnya hanya di PEAR. Apa fungsinya sebenarnya?
Lippai Zoltan
@YourCommonSense, bisakah Anda memposting fungsi yang ditentukan pengguna sebagai jawaban?
nullability
Saya akan menyarankan meneruskan tipe data ke BindArrayParamdalam array asosiatif karena Anda tampaknya membatasi ini untuk bilangan bulat.
Ian Brindley
2

Tidak mungkin menggunakan array seperti itu di PDO.

Anda perlu membuat string dengan parameter (atau gunakan?) Untuk setiap nilai, misalnya:

:an_array_0, :an_array_1, :an_array_2, :an_array_3, :an_array_4, :an_array_5

Ini sebuah contoh:

<?php
$ids = array(1,2,3,7,8,9);
$sqlAnArray = join(
    ', ',
    array_map(
        function($index) {
            return ":an_array_$index";
        },
        array_keys($ids)
    )
);
$db = new PDO(
    'mysql:dbname=mydb;host=localhost',
    'user',
    'passwd'
);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN('.$sqlAnArray.')'
);
foreach ($ids as $index => $id) {
    $stmt->bindValue("an_array_$index", $id);
}

Jika Anda ingin terus menggunakan bindParam, Anda dapat melakukan ini sebagai gantinya:

foreach ($ids as $index => $id) {
    $stmt->bindParam("an_array_$index", $ids[$id]);
}

Jika Anda ingin menggunakan ?placeholder, Anda dapat melakukannya seperti ini:

<?php
$ids = array(1,2,3,7,8,9);
$sqlAnArray = '?' . str_repeat(', ?', count($ids)-1);
$db = new PDO(
    'mysql:dbname=dbname;host=localhost',
    'user',
    'passwd'
);
$stmt = $db->prepare(
    'SELECT *
     FROM phone_number_lookup
     WHERE country_code IN('.$sqlAnArray.')'
);
$stmt->execute($ids);

Jika Anda tidak tahu apakah $idskosong, Anda harus mengujinya dan menangani kasing yang sesuai (mengembalikan array kosong, atau mengembalikan Objek Null, atau melempar pengecualian, ...).

Pedro Amaral Couto
sumber
0

Saya mengambil sedikit lebih jauh untuk mendapatkan jawaban lebih dekat ke pertanyaan awal tentang menggunakan placeholder untuk mengikat params.

Jawaban ini harus membuat dua loop melalui array yang akan digunakan dalam kueri. Tapi itu memecahkan masalah memiliki penampung kolom lain untuk pertanyaan yang lebih selektif.

//builds placeholders to insert in IN()
foreach($array as $key=>$value) {
    $in_query = $in_query . ' :val_' . $key . ', ';
}

//gets rid of trailing comma and space
$in_query = substr($in_query, 0, -2);

$stmt = $db->prepare(
    "SELECT *
     WHERE id IN($in_query)";

//pind params for your placeholders.
foreach ($array as $key=>$value) {
    $stmt->bindParam(":val_" . $key, $array[$key])
}

$stmt->execute();
Joseph_J
sumber
0

pertama-tama Anda menetapkan jumlah "?" dalam kueri dan kemudian oleh parameter "untuk" kirim parameter seperti ini:

require 'dbConnect.php';
$db=new dbConnect();
$array=[];
array_push($array,'value1');
array_push($array,'value2');
$query="SELECT * FROM sites WHERE kind IN (";

foreach ($array as $field){
    $query.="?,";
}
$query=substr($query,0,strlen($query)-1);
$query.=")";
$tbl=$db->connection->prepare($query);
for($i=1;$i<=count($array);$i++)
    $tbl->bindParam($i,$array[$i-1],PDO::PARAM_STR);
$tbl->execute();
$row=$tbl->fetchAll(PDO::FETCH_OBJ);
var_dump($row);
Ali Chegini
sumber