배열을 IN () 조건에 바인딩 할 수 있습니까?


562

PDO를 사용하여 값 배열을 자리 표시 자에 바인딩 할 수 있는지 궁금합니다. 여기서 유스 케이스는 IN()조건 과 함께 사용할 값 배열을 전달하려고합니다 .

다음과 같이 할 수 있기를 바랍니다.

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

그리고 PDO가 배열의 모든 값을 바인딩하고 인용하도록하십시오.

현재 내가하고있는 일은 :

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

확실히 어떤 일을 할 수 있지만 내가 놓친 솔루션이 있는지 궁금하십니까?


3
쿼리에 다른 자리 표시자가있는 경우를 포함하여 배열을 IN () 조건에 바인딩하는 방법에 대한 전체 안내서
— Common Sense

질문 이이 질문 의 복제본으로 마감되었습니다 . 이 질문이 4 년 전이고 조회수는 4 배, 답변 수는 3 배, 점수는 12 배이므로 중복 플래그를 취소했습니다. 분명히 최고의 목표입니다.
— miken32

2020 년에 이것을보고있는 사람이라면 누구나 github.com/morris/dop 을 사용해보십시오 .
— morris4

답변:


262

나는 soulmerge가 옳다고 생각합니다. query-string을 구성해야합니다.

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

수정 : 댄, 당신이 옳았다. 코드를 수정했습니다 (테스트하지는 않았습니다)

편집 : chris (댓글)와 누군가가 문제가되지 않아 foreach-loop ...

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

$stmt->execute();

...이 중복 될 수 있으므로 foreach루프와를 $stmt->execute대체 할 수 있습니다 ...

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

(다시 테스트하지 않았다)


7
그것은 흥미로운 해결책이며, ID를 반복하고 PDO :: quote ()를 호출하는 것을 선호하지만 '?'의 색인을 생각합니다. 다른 자리 표시자가 쿼리의 다른 곳에서 먼저 발생하면 자리 표시자가 엉망이됩니까?
— Andru

5
예, 문제가 될 것입니다. 그러나이 경우? 대신에 명명 된 매개 변수를 만들 수 있습니다.
— stefs

9
오래된 질문이지만 필자가 생각할 가치가있는 것은 $foreach및 bindValue()필수가 아니라는 것입니다-배열로 실행하십시오. 예 :$stmt->execute($ids);
— Chris

2
자리 표시 자 생성은 다음과 같이 수행해야합니다.str_repeat('?,', count($array) - 1). '?';
— Xeoncross

8
알지 못하는 사람들을위한 팁, 명명 된 매개 변수와 명명되지 않은 매개 변수를 혼합 할 수 없습니다. 따라서 쿼리에서 명명 된 매개 변수를 사용하는 경우 매개 변수를?로 바꾸고 bindValue 인덱스 오프셋을 보강하여 IN?의 위치가 다른? 매개 변수.
— justinl

169

빠른 것을 위해 :

//$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
훌륭합니다. 나는 이런 식으로 input_parameters 인수를 사용하려고 생각하지 않았습니다. 쿼리에 IN 목록보다 많은 매개 변수가있는 사용자의 경우 array_unshift 및 array_push를 사용하여 필요한 인수를 배열의 앞과 끝에 추가 할 수 있습니다. 또한, 나는$input_list = substr(str_repeat(',?', count($ids)), 1);
— orca

7
시도해 볼 수도 있습니다 str_repeat('?,', count($ids) - 1) . '?'. 하나의 적은 함수 호출.
— orca

5
@erfling, 이것은 준비된 진술입니다. 주사는 어디에서 오는가? 실제 증거로 백업 할 수 있다면 기꺼이 정정 해 드리겠습니다.
— DonVaughn

5
@erfling, 그렇습니다. 맞습니다. 그리고 params를 바인딩하는 것은이 execute배열에서 id 배열을 보내서 하는 것입니다
— DonVaughn

5
아 참으로 어쨌든 배열을 전달하고 있다는 사실을 놓쳤습니다. 이것은 실제로 안전하고 좋은 대답으로 보입니다. 사과드립니다.
— erfling

46

IN진술 을 사용하는 것이 중요 합니까? FIND_IN_SETop 를 사용하십시오 .

예를 들어 PDO에는 이와 같은 쿼리가 있습니다.

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

그런 다음 쉼표로 묶인 값 배열 만 바인딩하면됩니다.

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

그리고 끝났어.

UPD :이 답변에 대한 의견에서 일부 사람들이 지적한 바와 같이, 몇 가지 문제점을 명시 적으로 언급해야합니다.

  1. FIND_IN_SET테이블에서 인덱스를 사용하지 않고 아직 구현되지 않았습니다 . MYSQL 버그 추적기에서이 레코드를 참조하십시오 . 통지를 해준 @BillKarwin에게 감사합니다.
  2. 검색 할 배열 값으로 쉼표가있는 문자열을 사용할 수 없습니다. implode쉼표 기호를 구분 기호로 사용하므로 올바른 문자열을 구문 분석 할 수 없습니다. 참고로 @VaL에게 감사합니다.

인덱스에 크게 의존하지 않고 검색에 쉼표로 문자열을 사용하지 않으면 위의 솔루션보다 훨씬 쉽고 간단하며 빠릅니다.


25
IN ()은 인덱스를 사용할 수 있으며 범위 스캔으로 계산됩니다. FIND_IN_SET ()은 인덱스를 사용할 수 없습니다.
— Bill Karwin

1
그게 요점입니다. 나는 이것을 몰랐다. 그러나 어떤 식 으로든 문제의 성능에 대한 요구 사항이 없습니다. 그렇게 큰 테이블이 아닌 경우 다른 수의 자리 표시 자로 쿼리를 생성하는 별도의 클래스보다 훨씬 낫고 깨끗합니다.
— Tim Tonkonogov

11
예, 그러나 요즘 누가 그렇게 큰 테이블을 가지고 있습니까? ;-)
— Bill Karwin

3
이 접근법의 또 다른 문제는 쉼표가있는 문자열이 있으면 어떻게됩니까? 예를 들어 ... FIND_IN_SET(description,'simple,search')작동하지만 FIND_IN_SET(description,'first value,text, with coma inside')실패합니다. 따라서이 함수는 "first value", "text", "with coma inside" 원하는 대신 검색 됩니다"first value", "text, with coma inside"
— VaL

33

나는 많은 동적 쿼리를 수행하기 때문에 내가 만든 매우 간단한 도우미 함수입니다.

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

다음과 같이 사용하십시오.

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

문자열을 반환하고 쿼리를 실행할 때 필요한 바인딩을 :id1,:id2,:id3업데이트합니다 $bindArray. 쉬운!


6
매개 변수 바인딩 규칙을 위반하지 않기 때문에 훨씬 더 나은 솔루션입니다. 이것은 다른 사람들이 제안한 것처럼 인라인 SQL을 사용하는 것보다 훨씬 안전합니다.
— Dimitar Darazhanski

1
신난다. 우아한. 완전한.
— 칼신

17

EvilRygy의 솔루션이 효과가 없었습니다. Postgres에서 다른 해결 방법을 수행 할 수 있습니다.


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

작동하지 않습니다 : ERROR: operator does not exist: integer = text. 최소한 명시적인 캐스팅을 추가해야합니다.
— collimarco

17

postgres를위한 매우 깨끗한 방법은 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));

그것은 SQL 주입을 차단합니까?
— Fábio Zangirolami

1
@ FábioZangirolami 그것은 PDO이므로 그렇습니다.
— ESCOBAR

예, PDO! 4 년 후. 귀하의 답변은 매우 간단하고 효과적이었습니다. 감사합니다!!!
— Fábio Zangirolami

14

내 해결책은 다음과 같습니다.

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

array_values ​​사용에 유의하십시오. 이를 통해 주요 주문 문제를 해결할 수 있습니다.

ID 배열을 병합 한 다음 중복 항목을 제거했습니다. 나는 다음과 같은 것을 가지고 있었다 :

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

그리고 그것은 실패했다.


이것은 훌륭한 솔루션이었습니다. 쿼리 문자열을 작성하는 것과 달리 바인딩을 올바르게 사용합니다. 나는 이것을 강력히 추천합니다.
— Lindylead

참고 : 솔루션을 사용하여 배열의 앞면 또는 뒷면에 항목을 추가 할 수 있으므로 다른 바인딩을 포함 할 수 있습니다. $original_array 원래 배열 앞에 항목 추가 : 원래 배열 array_unshift($original_array, new_unrelated_item); 뒤에 항목 추가 : array_push($original_array, new_unrelated_item); 값이 바인딩되면 new_unrelated 항목이 올바른 위치에 배치됩니다. 이를 통해 배열 및 비 배열 항목을 혼합 할 수 있습니다. `
— Lindylead

12

PDO가 stefs가 제안한 것과 비슷한 것을 수행하도록 확장했으며 장기적으로 더 쉬웠습니다.

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

다음과 같이 사용할 수 있습니다.

$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
나는 Mark의 의견의 첫 부분을 언급했지만 그가 지적했듯이 같은 토큰 :array이 쿼리의 문자열에 있으면 여전히 안전하지 않습니다 .
— Chris

4
미래의 모든 독자를위한 참고 사항 :이 솔루션은 절대 사용해서는 안됩니다. 어설 션은 프로덕션 코드를위한 것이 아닙니다.
— Common Sense

1
YCS : 주장의 적합성 이외의 접근법에 대한 귀하의 의견에 관심이있는 피드백에 감사드립니다.
— Chris

1
아이디어는 동일하지만 단언이없고 더 간단하고 명시적인 방법이 없습니다. 단 하나의 경우에 대한 예외가 아니라 모든 쿼리를 작성하는 일반적인 방법입니다. 모든 자리 표시 자에는 유형이 표시되어 있습니다. 추측과 같은 작업은 if (is_array($data))불필요하지만 데이터 처리 방법이 더 정확합니다.
— 당신의 상식

1
의견을 읽는 모든 사람에게 : @Your Common Sense가 언급 한 문제는 개정판 4 에서 수정 되었습니다 .
— user2428118

11

보고되지 PDO : 미리 정의 된 상수 가되어 당신이에 나와있는대로해야 더 PDO :: PARAM_ARRAY PDOStatement-> bindParam은

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

그래서 나는 그것이 달성 가능하다고 생각하지 않습니다.


1
그게 효과가 있는지 모르겠습니다. 묵시적 인 문자열이 인용되는 것 같습니다.
— soulmerge

2
당신은 맞습니다, 따옴표는 이스케이프 처리되지 않습니다. 그 코드를 제거했습니다.

11

다른 매개 변수가 있으면 다음과 같이 할 수 있습니다.

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

큰 답변 주셔서 감사합니다. 이것은 실제로 나를 위해 일한 것 중 유일한 사람이었습니다. 그러나 나는 1 실수를 보았다. 변수 $ rs는 $ stmt 여야합니다
— Piet

11

나를 위해 더 섹시한 솔루션은 동적 연관 배열을 구성하고 사용하는 것입니다

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

하드 실제 시나리오에서 그것을 시도하지 않고 특정 수 있지만 보인다 미세 + 1.
— 아난 싱 --- 살아 죽을

9

또한이 스레드가 오래되었다는 것을 알고 있지만 곧 사용되지 않을 mysql 드라이버를 PDO 드라이버로 변환하는 동안 동일한 일반 매개 변수와 IN을 동적으로 빌드 할 수있는 기능을 만들어야하는 고유 한 문제가 발생했습니다. 매개 변수 배열. 그래서 나는 이것을 빨리 만들었습니다.

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

아직 테스트되지 않았지만 논리가있는 것 같습니다.

그것이 같은 위치에있는 누군가를 돕기를 바랍니다.

편집 : 테스트를 마친 후 다음을 발견했습니다.

  • PDO는 '.'을 좋아하지 않습니다. 그들의 이름으로 (당신이 나에게 물어 보면 다소 바보입니다)
  • bindParam이 잘못된 함수이고 bindValue가 올바른 함수입니다.

코드가 작업 버전으로 편집되었습니다.


8

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
count ($ ids) 후에 -1을 제거해야 작동하지 않거나 항상 자리 표시자가 하나 누락되었습니다.
— Marcel Burkhard

7

어떤 데이터베이스를 사용하고 있습니까? PostgreSQL에서는 ANY (array)를 사용하는 것이 좋습니다. 따라서 예제를 재사용하려면

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

불행히도 이것은 이식성이 거의 없습니다.

다른 데이터베이스에서는 다른 사람들이 언급했듯이 자신의 마법을 구성해야합니다. 이 논리를 클래스 / 함수에 넣어서 프로그램 전체에서 재사용 할 수 있도록하려고합니다. mysql_query이 시나리오의 주제와 예제에 대한 자세한 내용은 PHP.NET 페이지 의 주석을 살펴보십시오 .


4

같은 문제를 겪은 후에는 더 간단한 해결책을 찾았습니다 (아직 우아하지는 않지만 PDO::PARAM_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();

... 등등

따라서 혼합 값 배열을 사용하는 경우 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 ));

그러나 나는 이것을 테스트하지 않았습니다.


4

내가 아는 것처럼 배열을 PDO 문에 바인딩 할 가능성은 없습니다.

그러나 두 가지 일반적인 솔루션이 있습니다.

  1. 위치 자리 표시 자 (?,?,?,?) 또는 명명 된 자리 표시 자 (: id1, : id2, : id3)를 사용하십시오.

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

  2. 배열을 먼저 인용

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

두 옵션 모두 좋고 안전합니다. 나는 더 짧기 때문에 두 번째 것을 선호하며 필요한 경우 var_dump 매개 변수를 사용할 수 있습니다. 자리 표시자를 사용하면 값을 바인딩해야하며 결국 SQL 코드는 동일합니다.

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

마지막으로 중요한 것은 "바운드 변수 수가 토큰 수와 일치하지 않습니다"라는 오류를 피하는 것입니다.

Doctrine 위치 매개 변수를 사용하는 훌륭한 예입니다. 들어오는 매개 변수를 내부적으로 제어 할 수 있기 때문입니다.


4

열에 정수만 포함될 수있는 경우 자리 표시 자없이이 작업을 수행하고 쿼리에 ID를 직접 넣을 수 있습니다. 배열의 모든 값을 정수로 캐스트하면됩니다. 이처럼 :

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

이것은 SQL 주입에 취약하지 않아야합니다.


3

여기 내 해결책이 있습니다. 또한 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);
        }
    }
}

위 코드의 샘플 사용법은 다음과 같습니다.

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

당신이 무슨 생각을하는지 제게 알려주세요


이것은 아래 user2188977의 답변을 기반으로한다는 것을 잊었습니다.
— Lippai Zoltan

getOne ()이 무엇인지 잘 모르겠습니다 .PDO의 일부가 아닌 것 같습니다. 나는 PEAR에서만 그것을 보았습니다. 정확히 무엇을합니까?
— Lippai Zoltan

@YourCommonSense 사용자 정의 함수를 답변으로 게시 할 수 있습니까?
— 무효 성

데이터 형식을 전달하는 것이 좋습니다. BindArrayParam정수로 제한하는 것처럼 연관 배열에서 .
— Ian Brindley

2

PDO에서와 같은 배열을 사용할 수 없습니다.

각 값에 대해 매개 변수를 사용하여 문자열을 작성하거나?를 사용해야합니다.

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

예를 들면 다음과 같습니다.

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

을 계속 사용 bindParam하려면 대신 다음을 수행하십시오.

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

?자리 표시자를 사용하려면 다음과 같이하십시오.

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

$ids비어 있는지 모르는 경우 테스트하고 그에 따라 처리해야합니다 (빈 배열을 반환하거나 Null 객체를 반환하거나 예외를 throw하십시오 ...).


0

자리 표시자를 사용하여 매개 변수를 바인딩하는 원래 질문에 더 가깝게 답변하기 위해 조금 더 가져갔습니다.

이 대답은 배열을 통해 두 개의 루프를 만들어 쿼리에 사용해야합니다. 그러나 더 선택적 쿼리를 위해 다른 열 자리 표시자를 갖는 문제를 해결합니다.

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

먼저 "?"의 수를 설정하십시오 쿼리에서 "for"에 의해 다음과 같은 매개 변수를 보냅니다.

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