web-dev-qa-db-ja.com

MySQL INステートメントのPDOバインディング値

PDOに問題があり、かなり長い間悩まされた後、私は本当に答えを得たいと思っています。

次の例をご覧ください。

MySQLのINステートメントで使用するために、IDの配列をPDOステートメントにバインドしています。

配列は次のようになります:$ values = array(1,2,3,4,5,6,7,8);

データベースに安全な変数は$ products = implode( '、' $ values);

したがって、$ productsは、次の値を持つ[〜#〜] string [〜#〜]になります。 '1,2,3,4,5,6,7,8'

ステートメントは次のようになります。

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

もちろん、$ products: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)

私が実際にこれを行うことができる唯一の方法は、値をバインドせずに文字列自体の中に配置することです。しかし、これを行うにはもっと簡単な方法が必要だと確信しています。

70
hd82

これは、この質問で尋ねられたものと同じです: 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)
_

または、3番目のオプションとして、コンマ区切りリストを分割するユーザー定義関数を作成できます(cf. http: //www.slickdev.com/2008/09/15/mysql-query-real-values-from-delimiter-separated-string-ids/ )。特にin(...)句に依存する多くのクエリがある場合、これはおそらく3つのオプションの中で最良のオプションです。

37
James McNellis

この状況に対処する良い方法は、 str_pad を使用して、SQLクエリのすべての値に?を配置することです。次に、実行する引数として値の配列(場合によっては$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に変更することをお勧めします

27
ghbarratt

このような状況で思いつく可能性のある最善の準備済みステートメントは、次のようなものです。

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

次に、値をループして準備済みステートメントにバインドし、バインドする値と同じ数の疑問符があることを確認します。

8
Asaph

?s in [〜#〜] in [〜#〜]と同じ数の$ valuesの値の数を指定する必要があります。 =配列

これは、?sの配列を次のように作成することで簡単に実行できます。

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

iN句でこの$ in配列を使用します

これは、変更する$ values配列に従って?sのテーラーメイド配列を動的に提供します

6
ashsam786

式がbindValue()で値をバインドせずにユーザー入力に基づいている場合、実験的なSQLは素晴らしい選択ではないかもしれません。 ただし、入力の構文をMySQLのREGEXPでチェックすることで安全にできます。

例えば:

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

とても簡単にできます。 IN()ステートメントの値の配列がある場合EG:

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

簡単にできます

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

不明な数のレコード列を挿入の値にバインドする例を次に示します。

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' => '[email protected]'));
0
Xeoncross