developer tip

MySQL IN 문의 PDO 바인딩 값

copycodes 2020. 11. 2. 08:07
반응형

MySQL IN 문의 PDO 바인딩 값


나는 PDO에 꽤 오랫동안 괴로워 한 후 답변을 얻고 싶은 PDO에 문제가 있습니다.

이 예를 보자 :

MySQL IN 문에서 사용하기 위해 ID 배열을 PDO 문에 바인딩하고 있습니다.

배열은 다음과 같습니다. $ values ​​= array (1,2,3,4,5,6,7,8);

데이터베이스 안전 변수는 $ products = implode ( ','$ values);

따라서 $ products값이 '1,2,3,4,5,6,7,8'STRING이 됩니다.

문은 다음과 같습니다.

SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN (:products)

물론, $ 제품 과 문에 바인딩 할 것 : 제품 .

그러나 문이 컴파일되고 값이 바인딩되면 실제로는 다음과 같습니다.

SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN ('1,2,3,4,5,6,7,8')

문제는 명령문에 바인딩하기 위해 쉼표로 구분 된 값으로 준비 했으므로 IN 문 내부의 모든 것을 단일 문자열로 실행하는 것입니다.

내가 실제로 필요한 것은 :

SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN (1,2,3,4,5,6,7,8)

실제로이를 수행 할 수있는 유일한 방법은 값을 바인딩하지 않고 문자열 자체 내에 배치하는 것입니다.하지만이 작업을 수행하는 더 쉬운 방법이 있어야한다는 것을 확신합니다.


이것은이 질문에서 물었던 것과 같습니다. 배열을 IN () 조건에 바인딩 할 수 있습니까?

답은 in에있는 가변 크기 목록의 경우 쿼리를 직접 구성해야한다는 것입니다.

그러나 를 사용하여 따옴표로 묶인 쉼표로 구분 된 목록을 사용할 수 find_in_set있지만, 대규모 데이터 세트의 경우 테이블의 모든 값을 char 유형으로 캐스트해야하므로 성능에 상당한 영향을 미칩니다.

예를 들면 :

select users.id
from users
join products
on products.user_id = users.id
where find_in_set(cast(products.id as char), :products)

또는 세 번째 옵션으로 쉼표로 구분 된 목록을 분할하는 사용자 정의 함수를 만들 수 있습니다 (참조 : http://www.slickdev.com/2008/09/15/mysql-query-real-values- 구분 기호로 구분 된 문자열 ID / ). 이는 특히 in(...)절에 의존하는 쿼리가 많은 경우 세 가지 중에서 가장 좋은 옵션 일 것입니다 .


이 상황을 처리하는 좋은 방법 은 SQL 쿼리의 모든 값에 대해 를 사용 str_pad하는 것 ?입니다. 그런 다음 $values실행할 인수로 값 배열 (귀하의 경우 )을 전달할 수 있습니다 .

$sql = '
SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products.id IN ('.str_pad('',count($values)*2-1,'?,').')
';

$sth = $dbh->prepare($sql);
$sth->execute($values);
$user_ids = $sth->fetchAll();

이렇게하면 값을 SQL에 직접 삽입하는 대신 준비된 명령문을 사용하여 더 많은 이점을 얻을 수 있습니다.

PS-주어진 ID를 가진 제품이 사용자 ID를 공유하는 경우 결과는 중복 된 사용자 ID를 반환합니다. 고유 한 사용자 ID 만 원하는 경우 쿼리의 첫 번째 줄을 다음과 같이 변경하는 것이 좋습니다.SELECT DISTINCT users.id


다음과 같은 상황에서 가장 잘 준비된 진술은 다음과 같습니다.

SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN (?,?,?,?,?,?,?,?)

그런 다음 값을 반복하고이를 준비된 문에 바인딩하여 바인딩하는 값과 동일한 수의 물음표가 있는지 확인합니다.


$ values 배열 의 값 수 와 동일한 수의 ?IN 에 제공해야합니다.

이것은?의 배열을 다음과 같이 생성하여 쉽게 수행 할 수 있습니다.

 $in = join(',', array_fill(0, count($values), '?'));

이 $ in 배열을 IN 절에 사용하십시오.

THis will dynamically provide you with tailor made array of ?s as per your changiing $values array


You can do so very easily. If you have an array of values for your IN() statement EG:

$test = array(1,2,3);

You can simply do

$test = array(1,2,3);
$values = count($test);
$criteria = sprintf("?%s", str_repeat(",?", ($values ? $values-1 : 0)));
//Returns ?,?,?
$sql = sprintf("DELETE FROM table where column NOT IN(%s)", $criteria);
//Returns DELETE FROM table where column NOT IN(?,?,?)
$pdo->sth = prepare($sql);
$pdo->sth->execute($test);

If the expression is based on user input without binding the values with bindValue(), experimental SQL might not be a great choice. But, you can make it safe by checking the syntax of the input with MySQL's REGEXP.

For example:

SELECT *
FROM table
WHERE id IN (3,156)
AND '3,156' REGEXP '^([[:digit:]]+,?)+$'

Here is an example of binding an unknown number of record columns to values for an insert.

public function insert($table, array $data)
{
    $sql = "INSERT INTO $table (" . join(',', array_keys($data)) . ') VALUES ('
        . str_repeat('?,', count($data) - 1). '?)';

    if($sth = $this->db->prepare($sql))
    {
        $sth->execute(array_values($data));
        return $this->db->lastInsertId();
    }
}

$id = $db->insert('user', array('name' => 'Bob', 'email' => 'foo@example.com'));

Please try like this:

WHERE products IN(REPLACE(:products, '\'', ''))

Regards

참고URL : https://stackoverflow.com/questions/1586587/pdo-binding-values-for-mysql-in-statement

반응형