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)
私が実際にこれを行うことができる唯一の方法は、値をバインドせずに文字列自体の中に配置することです。しかし、これを行うにはもっと簡単な方法が必要だと確信しています。
これは、この質問で尋ねられたものと同じです: 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つのオプションの中で最良のオプションです。
この状況に対処する良い方法は、 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
に変更することをお勧めします
このような状況で思いつく可能性のある最善の準備済みステートメントは、次のようなものです。
SELECT users.id
FROM users
JOIN products
ON products.user_id = users.id
WHERE products IN (?,?,?,?,?,?,?,?)
次に、値をループして準備済みステートメントにバインドし、バインドする値と同じ数の疑問符があることを確認します。
?s in [〜#〜] in [〜#〜]と同じ数の$ valuesの値の数を指定する必要があります。 =配列
これは、?sの配列を次のように作成することで簡単に実行できます。
$in = join(',', array_fill(0, count($values), '?'));
iN句でこの$ in配列を使用します
これは、変更する$ values配列に従って?sのテーラーメイド配列を動的に提供します
式がbindValue()で値をバインドせずにユーザー入力に基づいている場合、実験的なSQLは素晴らしい選択ではないかもしれません。 ただし、入力の構文をMySQLのREGEXPでチェックすることで安全にできます。
例えば:
SELECT *
FROM table
WHERE id IN (3,156)
AND '3,156' REGEXP '^([[:digit:]]+,?)+$'
とても簡単にできます。 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);
不明な数のレコード列を挿入の値にバインドする例を次に示します。
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]'));