Tôi có thể liên kết một mảng với điều kiện IN () không?


562

Tôi tò mò muốn biết liệu có thể liên kết một mảng các giá trị với một trình giữ chỗ bằng PDO hay không. Trường hợp sử dụng ở đây đang cố gắng truyền một mảng các giá trị để sử dụng với một IN()điều kiện.

Tôi muốn có thể làm một cái gì đó như thế này:

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

Và có PDO liên kết và trích dẫn tất cả các giá trị trong mảng.

Hiện tại tôi đang làm:

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

Đó chắc chắn là công việc, nhưng chỉ tự hỏi nếu tôi có một giải pháp tích hợp sẵn?


3
Hướng dẫn đầy đủ về liên kết một mảng với điều kiện IN () , bao gồm cả trường hợp khi bạn có các trình giữ chỗ khác trong truy vấn
Ý thức chung của bạn

Câu hỏi đã được đóng lại như là một bản sao của câu hỏi này . Tôi đảo ngược cờ trùng lặp vì câu hỏi này lớn hơn 4 tuổi, có 4 lần lượt xem, gấp 3 lần số câu trả lời và 12 lần điểm. Nó rõ ràng là mục tiêu vượt trội.
miken32

Bất cứ ai nhìn vào điều này vào năm 2020: Bạn có thể thử github.com/morris/dop cho điều đó.
morris4

Câu trả lời:


262

Tôi nghĩ soulmerge là đúng. bạn sẽ phải xây dựng chuỗi truy vấn.

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

sửa: dan, bạn đã đúng. đã sửa mã (không kiểm tra mặc dù)

chỉnh sửa: cả chris (bình luận) và someoneisintrouble đều gợi ý rằng vòng lặp foreach ...

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

$stmt->execute();

... có thể là dư thừa, do đó, foreachvòng lặp và $stmt->executecó thể được thay thế bằng chỉ ...

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

(một lần nữa, tôi đã không kiểm tra nó)


7
Đó là một giải pháp thú vị, và trong khi tôi thích nó hơn là lặp lại các id và gọi PDO :: quote (), tôi nghĩ chỉ số của '?' giữ chỗ sẽ bị rối nếu có bất kỳ chỗ dành sẵn nào khác xảy ra ở nơi khác trong truy vấn trước, phải không?
Andru

5
vâng, đó sẽ là một vấn đề nhưng trong trường hợp này bạn có thể tạo các tham số được đặt tên thay vì?
stefs

9
Câu hỏi cũ, nhưng đáng chú ý tôi tin, là $foreachbindValue()không bắt buộc - chỉ cần thực hiện với mảng. Ví dụ:$stmt->execute($ids);
Chris

2
Tạo trình giữ chỗ nên được thực hiện như thế nàystr_repeat('?,', count($array) - 1). '?';
Xeoncross

8
Chỉ là một mẹo cho những người không biết, bạn không thể trộn các tham số được đặt tên và không tên. Do đó, nếu bạn sử dụng các tham số được đặt tên trong truy vấn của mình, hãy chuyển chúng ra? Và sau đó tăng bù offset indexValue của bạn để khớp với vị trí của IN? thông số
justinl

169

Đối với một cái gì đó nhanh chóng:

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

$qMarks = str_repeat('?,', count($ids) - 1) . '?';
$sth = $db->prepare("SELECT * FROM myTable WHERE id IN ($qMarks)");
$sth->execute($ids);

7
Tuyệt vời, tôi đã không nghĩ sẽ sử dụng đối số input_parameter theo cách này. Đối với những người có truy vấn có nhiều tham số hơn danh sách IN, bạn có thể sử dụng Array_unshift và Array_push để thêm các đối số cần thiết vào phía trước và cuối của mảng. Ngoài ra, tôi thích $input_list = substr(str_repeat(',?', count($ids)), 1);
orca

7
Bạn cũng có thể thử str_repeat('?,', count($ids) - 1) . '?'. Một cuộc gọi ít chức năng.
orca

5
@erfling, đây là một tuyên bố đã được chuẩn bị, việc tiêm thuốc sẽ đến từ đâu? Tôi sẽ rất vui khi thực hiện bất kỳ chỉnh sửa nào nếu bạn có thể sao lưu điều đó với một số bằng chứng thực tế về điều đó.
DonVaughn

5
@erfling, vâng, điều đó là chính xác và ràng buộc các thông số chính xác là những gì chúng ta đang làm trong ví dụ này bằng cách gửi executemột mảng id
DonVaughn

5
Ồ thật đấy. Bằng cách nào đó đã bỏ lỡ thực tế là bạn đã vượt qua mảng. Điều này thực sự có vẻ là an toàn và một câu trả lời tốt. Lời xin lỗi của tôi.
xóa sổ

46

Nó rất quan trọng để sử dụng INtuyên bố? Cố gắng sử dụng FIND_IN_SETop.

Ví dụ: có một truy vấn trong PDO như thế

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

Sau đó, bạn chỉ cần liên kết một mảng các giá trị, được mã hóa bằng dấu phẩy, như thế này

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

và nó đã được thực hiện.

CẬP NHẬT: Như một số người đã chỉ ra trong các bình luận cho câu trả lời này, có một số vấn đề cần được nêu rõ.

  1. FIND_IN_SETkhông sử dụng chỉ mục trong một bảng và nó vẫn chưa được triển khai - xem bản ghi này trong trình theo dõi lỗi MYSQL . Cảm ơn @BillKarwin đã thông báo.
  2. Bạn không thể sử dụng một chuỗi có dấu phẩy bên trong làm giá trị của mảng để tìm kiếm. Không thể phân tích chuỗi như vậy theo đúng cách sau implodekhi bạn sử dụng biểu tượng dấu phẩy làm dấu phân cách. Cảm ơn @VaL đã ghi chú.

Tốt thôi, nếu bạn không phụ thuộc nhiều vào chỉ mục và không sử dụng chuỗi có dấu phẩy để tìm kiếm, giải pháp của tôi sẽ dễ dàng hơn, đơn giản hơn và nhanh hơn các giải pháp được liệt kê ở trên.


25
IN () có thể sử dụng một chỉ mục và được tính là quét phạm vi. FIND_IN_SET () không thể sử dụng một chỉ mục.
Bill Karwin

1
Đó là một điểm. Tôi không biết điều này. Nhưng bất kỳ cách nào không có bất kỳ yêu cầu cho hiệu suất trong câu hỏi. Đối với các bảng không quá lớn, nó tốt hơn và sạch hơn nhiều so với lớp riêng biệt để tạo truy vấn với số lượng giữ chỗ khác nhau.
Tim Tonkonogov

11
Vâng, nhưng những người có một bảng không quá lớn những ngày này? ;-)
Bill Karwin

3
Một vấn đề khác với cách tiếp cận này là nếu có chuỗi có dấu phẩy bên trong thì sao? Ví dụ ... FIND_IN_SET(description,'simple,search')sẽ làm việc, nhưng FIND_IN_SET(description,'first value,text, with coma inside')sẽ thất bại. Vì vậy, chức năng sẽ tìm kiếm "first value", "text", "with coma inside" thay vì mong muốn"first value", "text, with coma inside"
VaL 17/2/2015

33

Vì tôi thực hiện rất nhiều truy vấn động, đây là một hàm trợ giúp siêu đơn giản mà tôi đã thực hiện.

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

Sử dụng nó như thế này:

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

Trả về một chuỗi :id1,:id2,:id3và cũng cập nhật $bindArraycác ràng buộc mà bạn sẽ cần khi đến lúc chạy truy vấn của bạn. Dễ dàng!


6
Đây là một giải pháp tốt hơn nhiều, vì nó không phá vỡ sự ràng buộc của quy tắc tham số. Điều này an toàn hơn nhiều so với việc có sql nội tuyến như đề xuất của một số người khác ở đây.
Dimitar Darazhanski

1
Tuyệt vời. Thanh lịch. Hoàn hảo.
Ricalsin

17

Giải pháp từ EvilRygy đã không làm việc cho tôi. Trong Postgres, bạn có thể thực hiện một cách giải quyết khác:


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

Điều này không hoạt động : ERROR: operator does not exist: integer = text. Ít nhất bạn cần thêm đúc rõ ràng.
collimarco

17

cách rất sạch cho postgres là sử dụng mảng postgres ("{}"):

$ids = array(1,4,7,9,45);
$param = "{".implode(', ',$ids)."}";
$cmd = $db->prepare("SELECT * FROM table WHERE id = ANY (?)");
$result = $cmd->execute(array($param));

nó chặn sql tiêm?
Fábio Zangirolami

1
@ FábioZangirolami đó là PDO, nên có.
ESCOBAR

CÓ, PDO! sau 4 năm. Phản hồi của bạn là tốt cho tôi, rất đơn giản và hiệu quả. cảm ơn bạn!!!
Fábio Zangirolami

14

Đây là giải pháp của tôi:

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

Lưu ý việc sử dụng mảng_values. Điều này có thể khắc phục các vấn đề đặt hàng quan trọng.

Tôi đã hợp nhất các mảng id và sau đó loại bỏ các mục trùng lặp. Tôi đã có một cái gì đó như:

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

Và điều đó đã thất bại.


Đây là một giải pháp tuyệt vời. Nó không giống như xây dựng chuỗi truy vấn, điều này sử dụng ràng buộc chính xác. Tôi thực sự khuyên bạn nên cái này.
Lindylead

Lưu ý: Sử dụng giải pháp của bạn, bạn có thể thêm các mục vào phía trước hoặc phía sau của mảng, để bạn có thể bao gồm các ràng buộc khác. $original_array Thêm các mục trước mảng ban đầu: array_unshift($original_array, new_unrelated_item); Thêm các mục sau mảng ban đầu: array_push($original_array, new_unrelated_item); Khi các giá trị bị ràng buộc, các mục new_unrelated sẽ được đặt ở vị trí chính xác. Điều này cho phép bạn trộn các mục Array và không mảng. `
Lindylead

12

Tôi đã mở rộng PDO để làm một cái gì đó tương tự như những gì stefs gợi ý và về lâu dài đối với tôi dễ dàng hơn:

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

Bạn có thể sử dụng nó như thế này:

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

1
Tôi đã giải quyết phần đầu tiên của bình luận của Mark, nhưng như anh ấy đã chỉ ra, vẫn không an toàn nếu một mã thông báo giống như :arraytrong một chuỗi trong truy vấn.
Chris

4
Một lưu ý cho tất cả các độc giả trong tương lai: Giải pháp này không bao giờ nên được sử dụng. Các xác nhận không dành cho mã sản xuất
Ý thức chung của bạn

1
YCS: cảm ơn phản hồi, quan tâm đến ý kiến ​​của bạn về cách tiếp cận bên ngoài sự phù hợp của các khẳng định.
Chris

1
Ý tưởng này khá giống nhau nhưng chỉ cần không có khẳng định và cách đơn giản và rõ ràng hơn - không phải là một ngoại lệ cho chỉ một trường hợp duy nhất mà là một cách chung để xây dựng mọi truy vấn. Mỗi chỗ dành sẵn được đánh dấu bằng loại của nó. Nó làm cho việc phỏng đoán (như if (is_array($data))một) không cần thiết nhưng làm cho cách xử lý dữ liệu chính xác hơn.
Ý thức chung của bạn

1
Đối với bất kỳ người nào đang đọc các bình luận: vấn đề được đề cập bởi @Your Common Sense đã được sửa trong phiên bản 4 .
dùng2428118

11

Nhìn vào PDO: Các hằng số được xác định trước không có PDO :: PARAM_ARRAY mà bạn sẽ cần như được liệt kê trên PDOStatement-> bindParam

bool PDOStatement :: bindParam (hỗn hợp $ tham số, hỗn hợp & $ biến [, int $ data_type [, int $ length [, hỗn hợp $ driver_options]]])

Vì vậy, tôi không nghĩ rằng nó có thể đạt được.


1
Tôi không biết nếu nó hoạt động. Tôi đoán rằng chuỗi đã mã hóa được trích dẫn.
soulmerge

2
Bạn đã đúng, các trích dẫn được thoát để chiến thắng. Tôi đã loại bỏ mã đó.

11

Khi bạn có tham số khác, bạn có thể làm như thế này:

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

Cảm ơn câu trả lời tuyệt vời. Đây là người duy nhất thực sự làm việc cho tôi. Tuy nhiên, tôi đã thấy 1 sai lầm. Biến $ rs phải là $ stmt
Piet

11

Đối với tôi, giải pháp hấp dẫn hơn là xây dựng một mảng kết hợp động và sử dụng nó

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

Khó có thể chắc chắn mà không thử nó trong kịch bản thực tế, nhưng có vẻ ổn. + 1
Anant Singh --- Sống đến

9

Tôi cũng nhận ra luồng này đã cũ nhưng tôi gặp một vấn đề duy nhất, trong khi chuyển đổi trình điều khiển mysql sắp bị loại bỏ sang trình điều khiển PDO, tôi phải tạo một chức năng có thể xây dựng, một cách linh hoạt, cả hai thông số và IN thông thường từ cùng mảng param. Vì vậy, tôi đã nhanh chóng xây dựng điều này:

/**
 * 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;
}

Nó vẫn chưa được kiểm tra tuy nhiên logic dường như ở đó.

Hy vọng nó sẽ giúp ai đó ở vị trí tương tự,

Chỉnh sửa: Sau một số thử nghiệm, tôi phát hiện ra:

  • PDO không thích '.' trong tên của họ (thật là ngu ngốc nếu bạn hỏi tôi)
  • bindParam là hàm sai, bindValue là hàm đúng.

Mã chỉnh sửa thành phiên bản làm việc.


8

Một chút chỉnh sửa về mã của 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

1
Tôi đã phải xóa -1 sau khi đếm ($ ids) để nó hoạt động cho tôi hoặc sẽ luôn thiếu một người giữ chỗ.
Marcel Burkhard

7

Bạn đang dùng gói dữ liệu nào vậy? Trong PostgreSQL tôi thích sử dụng BẤT K (mảng nào). Vì vậy, để sử dụng lại ví dụ của bạn:

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

Thật không may, điều này là không di động.

Trên các cơ sở dữ liệu khác, bạn sẽ cần tạo ra phép thuật của riêng mình như những người khác đã đề cập. Bạn sẽ muốn đưa logic đó vào một lớp / hàm để làm cho nó có thể sử dụng lại trong suốt chương trình của bạn. Hãy xem các bình luận trên mysql_querytrang trên PHP.NET để biết thêm suy nghĩ về chủ đề và các ví dụ về kịch bản này.


4

Sau khi trải qua cùng một vấn đề, tôi đã đi đến một giải pháp đơn giản hơn (mặc dù vẫn không thanh lịch như mong PDO::PARAM_ARRAYmuốn):

đưa ra mảng $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();

... và như thế

Vì vậy, nếu bạn đang sử dụng một mảng giá trị hỗn hợp, bạn sẽ cần nhiều mã hơn để kiểm tra các giá trị của mình trước khi gán kiểu param:

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

Nhưng tôi chưa thử cái này.


4

Như tôi biết, không có bất kỳ khả năng nào để liên kết một mảng vào câu lệnh PDO.

Nhưng tồn tại 2 giải pháp chung:

  1. Sử dụng Trình giữ chỗ vị trí (?,?,?,?) Hoặc Trình giữ chỗ được đặt tên (: id1 ,: id2 ,: id3)

    $ whereIn = implode (',', Array_fill (0, Count ($ ids), '?'));

  2. Trích dẫn mảng trước

    $ whereIn = Array_map (mảng ($ db, 'quote'), $ ids);

Cả hai lựa chọn đều tốt và an toàn. Tôi thích cái thứ hai vì nó ngắn hơn và tôi có thể thay đổi các tham số nếu tôi cần. Sử dụng trình giữ chỗ, bạn phải liên kết các giá trị và cuối cùng mã SQL của bạn sẽ giống nhau.

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

Và điều cuối cùng và quan trọng đối với tôi là tránh lỗi "số lượng biến bị ràng buộc không khớp với số lượng mã thông báo"

Học thuyết là ví dụ tuyệt vời của việc sử dụng trình giữ chỗ vị trí, chỉ vì nó có quyền kiểm soát nội bộ đối với các tham số đến.


4

Nếu cột chỉ có thể chứa số nguyên, bạn có thể thực hiện việc này mà không cần giữ chỗ và chỉ cần đặt id trực tiếp vào truy vấn. Bạn chỉ cần truyền tất cả các giá trị của mảng thành số nguyên. Như thế này:

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

Điều này không nên dễ bị tổn thương đối với bất kỳ SQL SQL nào.


3

đây là giải pháp của tôi Tôi cũng đã mở rộng lớp 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);
        }
    }
}

Đây là một cách sử dụng mẫu cho mã trên:

$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'];
}

Cho tôi biết bạn nghĩ gì


Quên đề cập rằng điều này dựa trên câu trả lời của user2188977, bên dưới.
Lippai Zoltan

Tôi không chắc chắn getOne () là gì, nó dường như không phải là một phần của PDO. Tôi chỉ thấy nó trong PEAR. Làm gì chính xác?
Lippai Zoltan

@YourCommonSense bạn có thể đăng chức năng do người dùng xác định làm câu trả lời không?
nullability

Tôi sẽ đề nghị chuyển kiểu dữ liệu BindArrayParamvào mảng kết hợp vì dường như bạn đang giới hạn điều này với số nguyên.
Ian Brindley

2

Không thể sử dụng một mảng như thế trong PDO.

Bạn cần xây dựng một chuỗi với một tham số (hoặc sử dụng?) Cho từng giá trị:

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

Đây là một ví dụ:

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

Nếu bạn muốn tiếp tục sử dụng bindParam, bạn có thể làm điều này thay vào đó:

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

Nếu bạn muốn sử dụng ?trình giữ chỗ, bạn có thể làm như thế này:

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

Nếu bạn không biết nếu $idstrống, bạn nên kiểm tra nó và xử lý trường hợp đó cho phù hợp (trả về một mảng trống hoặc trả về một đối tượng Null hoặc ném ngoại lệ, ...).


0

Tôi đã đưa nó thêm một chút để có được câu trả lời gần hơn với câu hỏi ban đầu về việc sử dụng trình giữ chỗ để ràng buộc các thông số.

Câu trả lời này sẽ phải thực hiện hai vòng lặp thông qua mảng được sử dụng trong truy vấn. Nhưng nó giải quyết vấn đề có các trình giữ chỗ cột khác cho các truy vấn chọn lọc hơn.

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

0

lần đầu tiên bạn đặt số "?" trong truy vấn và sau đó bằng cách gửi "cho" các tham số như thế này:

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);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.