web-dev-qa-db-ja.com

WHERE句を使用してクエリに配列を渡す

IDの配列が$galleries = array(1,2,5)の場合、WHERE句で配列の値を使用するSQLクエリが必要です。

SELECT *
FROM galleries
WHERE id = /* values of array $galleries... eg. (1 || 2 || 5) */

MySQLで使用するためにこのクエリ文字列をどのように生成できますか?

288
Quinn

注意してください。 この回答には深刻な SQLインジェクション の脆弱性が含まれています。外部入力がサニタイズされていることを確認せずに、ここに示されているコードサンプルを使用しないでください。

$ids = join("','",$galleries);   
$sql = "SELECT * FROM galleries WHERE id IN ('$ids')";
302
Flavius Stef

PDOを使う: [1]

$in = join(',', array_fill(0, count($ids), '?'));
$select = <<<SQL
    SELECT *
    FROM galleries
    WHERE id IN ($in);
SQL;
$statement = $pdo->prepare($select);
$statement->execute($ids);

MySQLiを使う  [2]

$in = join(',', array_fill(0, count($ids), '?'));
$select = <<<SQL
    SELECT *
    FROM galleries
    WHERE id IN ($in);
SQL;
$statement = $mysqli->prepare($select);
$statement->bind_param(str_repeat('i', count($ids)), ...$ids);
$statement->execute();
$result = $statement->get_result();

説明:

指定されたリストに値が存在するかどうかを確認するには、SQLのIN()演算子を使用します。

一般的にそれはこのようになります:

expr IN (value,...)

配列から()の内側に配置する式を構築できます。括弧内に少なくとも1つの値がなければならないことに注意してください。そうしないと、MySQLはエラーを返します。これは、入力配列に少なくとも1つの値があることを確認することと同じです。 SQLインジェクション攻撃を防ぐために、まず各入力項目に対して?を生成してパラメータ化クエリを作成します。ここで私はあなたのIDを含む配列が$idsであると仮定します:

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

$select = <<<SQL
    SELECT *
    FROM galleries
    WHERE id IN ($in);
SQL;

3つの項目の入力配列が与えられたとすると$selectは次のようになります。

SELECT *
FROM galleries
WHERE id IN (?, ?, ?)

繰り返しになりますが、入力配列の各項目には?があります。その後、PDOまたはMySQLiを使用して、上記のようにクエリを準備して実行します。

文字列でIN()演算子を使う

バインドパラメータのため、文字列と整数を簡単に変更できます。 PDOの場合、変更は必要ありません。 MySQLiでは、文字列をチェックする必要がある場合はstr_repeat('i',str_repeat('s',に変更します。

[1]: 簡潔にするためにエラーチェックを省略しました。データベースメソッドごとに通常のエラーをチェックする必要があります(または、DBドライバが例外をスローするように設定します)。

[2]: PHP 5.6以上が必要です。簡潔さのためにエラーチェックを省略しました。

289
Levi Morrison

int:

$query = "SELECT * FROM `$table` WHERE `$column` IN(".implode(',',$array).")";

文字列:

$query = "SELECT * FROM `$table` WHERE `$column` IN('".implode("','",$array)."')";
54
user542568

事前に入力を正しくサニタイズすると仮定します。

$matches = implode(',', $galleries);

次に、クエリを調整してください。

SELECT *
FROM galleries
WHERE id IN ( $matches ) 

データセットに応じて適切に値を見積もります。

27
AvatarKava

つかいます:

select id from galleries where id in (1, 2, 5);

単純なfor eachループが機能します。

Flavius/AvatarKavaの方法 が優れていますが、配列の値にコンマが含まれていないことを確認してください。

10

Flavius Stefの答え として、すべてのidがint値であることを確認するためにintval()を使用できます。

$ids = join(',', array_map('intval', $galleries));  
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";
8
Van-Duyet Le

MySQLi の場合/エスケープ機能付き:

$ids = array_map(function($a) use($mysqli) { 
    return is_string($a) ? "'".$mysqli->real_escape_string($a)."'" : $a;
  }, $ids);
$ids = join(',', $ids);  
$result = $mysqli->query("SELECT * FROM galleries WHERE id IN ($ids)");

準備済みステートメントのあるPDOの場合

$qmarks = implode(',', array_fill(0, count($ids), '?'));
$sth = $dbh->prepare("SELECT * FROM galleries WHERE id IN ($qmarks)");
$sth->execute($ids);
8
artoodetoo

SQLインジェクションの脆弱性と空の状態に注意を払う必要があります。私は両方を以下のように扱うつもりです。

純粋な数値配列の場合は、各要素に対して適切な型変換viz intvalまたはfloatvalまたはdoublevalを使用します。文字列型の場合は mysqli_real_escape_string() 必要に応じて数値にも適用できます。 MySQLでは数字だけでなく日付の変種も文字列として使用できます

クエリに渡す前に値を適切にエスケープするには、次のような関数を作成します。

function escape($string)
{
    // Assuming $db is a link identifier returned by mysqli_connect() or mysqli_init()
    return mysqli_real_escape_string($db, $string);
}

そのような関数はあなたのアプリケーションですでに利用可能であるか、あるいは既に作成されているかもしれません。

次のように文字列配列をサニタイズします。

$values = array_map('escape', $gallaries);

適切な場合は、代わりにintvalfloatval、またはdoublevalを使用して数値配列をサニタイズできます。

$values = array_map('intval', $gallaries);

それから最後にクエリ条件を構築します

$where  = count($values) ? "`id` = '" . implode("' OR `id` = '", $values) . "'" : 0;

または

$where  = count($values) ? "`id` IN ('" . implode("', '", $values) . "')" : 0;

$galleries = array();のように配列が空になることもあるので、IN ()は空のリストを許可しないことに注意してください。代わりにORを使うこともできますが、問題は残ります。したがって、上記のチェックcount($values)は、同じことを確認することです。

そしてそれを最後のクエリに追加します。

$query  = 'SELECT * FROM `galleries` WHERE ' . $where;

_ tip _ :すべての行を隠すのではなく、空の配列の場合にすべてのレコードを表示する(フィルタリングしない)場合は、単に 0 1 に置き換えます。三項の誤った部分に。

6
Izhar Aazmi

より安全です。

$galleries = array(1,2,5);
array_walk($galleries , 'intval');
$ids = implode(',', $galleries);
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";
5
Filipe

入力配列を適切にフィルタリングすれば、この "WHERE id IN"節を使用できます。このようなもの:

$galleries = array();

foreach ($_REQUEST['gallery_id'] as $key => $val) {
    $galleries[$key] = filter_var($val, FILTER_SANITIZE_NUMBER_INT);
}

以下の例のように:enter image description here

$galleryIds = implode(',', $galleries);

すなわち今あなたは安全に$query = "SELECT * FROM galleries WHERE id IN ({$galleryIds})";を使うべきです

5
Supratim Roy

テーブルtexts(T_ID (int), T_TEXT (text))とテーブルtest(id (int), var (varchar(255)))があるかもしれません

insert into test values (1, '1,2,3') ;では、以下はテーブルテキストからT_ID IN (1,2,3)の行を出力します。

SELECT * FROM `texts` WHERE (SELECT FIND_IN_SET( T_ID, ( SELECT var FROM test WHERE id =1 ) ) AS tm) >0

こうすれば、PHPや他のプログラミング言語を使用せずに、追加のテーブルやSQLのみを使用して、単純なn2mデータベース関係を管理できます。

4
SERJOU

Shrapnel氏の SafeMySQL [PHP用ライブラリは、パラメータ化されたクエリでタイプヒント付きのプレースホルダを提供し、配列を操作するための便利なプレースホルダをいくつか含みます。 ?aプレースホルダーは、配列をエスケープ文字列*のコンマ区切りリストに展開します。

例えば:

$someArray = [1, 2, 5];
$galleries = $db->getAll("SELECT * FROM galleries WHERE id IN (?a)", $someArray);

* MySQLは自動型強制を実行するので、SafeMySQLが上記のIDを文字列に変換しても問題ありません - それでも正しい結果が得られます。

4
Mark Amery

より多くの例:

$galleryIds = [1, '2', 'Vitruvian Man'];
$ids = array_filter($galleryIds, function($n){return (is_numeric($n));});
$ids = implode(', ', $ids);

$sql = "SELECT * FROM galleries WHERE id IN ({$ids})";
// output: 'SELECT * FROM galleries WHERE id IN (1, 2)'

$statement = $pdo->prepare($sql);
$statement->execute();
3
Ricardo Canelas

INクエリを使用する以外に、INクエリでSQLインジェクションの脆弱性が発生する危険性があるため、2つの方法があります。 looping を使用して正確なデータを取得することも、 _または_ caseを使用してクエリを使用することもできます。

1. SELECT *
      FROM galleries WHERE id=1 or id=2 or id=5;


2. $ids = array(1, 2, 5);
   foreach ($ids as $id) {
      $data[] = SELECT *
                    FROM galleries WHERE id= $id;
   }
2
Gaurav Singh

元の質問は数値の配列に関するもので、文字列の配列を使用しているため、与えられた例を機能させることはできませんでした。

IN()関数を使うには、各文字列を一重引用符で囲む必要があることがわかりました。

これが私の解決策です

foreach($status as $status_a) {
        $status_sql[] = '\''.$status_a.'\'';
    }
    $status = implode(',',$status_sql);

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

ご覧のとおり、最初の関数はsingle quotes (\')内の各配列変数をラップしてから配列を分解します。

注:$statusは、SQLステートメント内に単一引用符を含みません。

引用符を追加するより良い方法がおそらくありますが、これは動作します。

2
RJaus

PDOなしで安全な方法:

$ids = array_filter(array_unique(array_map('intval', (array)$ids)));

if ($ids) {
    $query = 'SELECT * FROM `galleries` WHERE `id` IN ('.implode(',', $ids).');';
}
  • (array)$ids変数$idsを配列にキャスト
  • array_mapすべての配列値を整数に変換します
  • array_unique繰り返し値を削除
  • array_filterゼロ値を削​​除
  • implode:IN選択にすべての値を結合します
1
Lito

以下は私が使った方法で、PDOを他のデータのための名前付きプレースホルダーと共に使っています。 SQLインジェクションを克服するために、整数値のみを受け入れ、それ以外はすべて拒否するように配列をフィルタリングしています。

$owner_id = 123;
$galleries = array(1,2,5,'abc');

$good_galleries = array_filter($chapter_arr, 'is_numeric');

$sql = "SELECT * FROM galleries WHERE owner=:OWNER_ID AND id IN ($good_galleries)";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(
    "OWNER_ID" => $owner_id,
));

$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
1
kojow7