Передача массива в запрос с использованием предложения WHERE

282

Учитывая массив идентификаторов $galleries = array(1,2,5), я хочу иметь SQL-запрос, который использует значения массива в своем предложении WHERE, например:

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

Как я могу сгенерировать эту строку запроса для использования с MySQL?

Показать ещё 1 комментарий
Теги:
arrays

20 ответов

271
Лучший ответ

ОСТОРОЖНО! Этот ответ содержит серьезную уязвимость SQL injection. НЕ используйте примеры кода, представленные здесь, без необходимости очистки любого внешнего входа.

$ids = join("','",$galleries);   
$sql = "SELECT * FROM galleries WHERE id IN ('$ids')";
  • 5
    Идентификаторы все еще являются списком в кавычках, поэтому он выглядит, например, как "WHERE id IN ('1,2,3,4')". Вам нужно указывать каждый идентификатор по отдельности, или же расставлять кавычки внутри скобок.
  • 21
    Я просто добавляю предупреждение, что $galleries должен проверяться перед этим утверждением! Подготовленные операторы не могут обрабатывать массивы AFAIK, поэтому, если вы привыкли связывать переменные, вы можете легко сделать возможным внедрение SQL здесь.
Показать ещё 18 комментариев
280

Использование 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,...)

Мы можем построить выражение для размещения внутри () из нашего массива. Обратите внимание, что в скобках должно быть хотя бы одно значение, или MySQL вернет ошибку; это соответствует тому, что наш входной массив имеет хотя бы одно значение. Чтобы предотвратить предотвращение атак SQL-инъекций, сначала создайте ? для каждого элемента ввода для создания параметризованного запроса. Здесь я предполагаю, что массив, содержащий ваши идентификаторы, называется $ids:

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

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

Учитывая входной массив из трех элементов $select будет выглядеть так:

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

Снова обратите внимание, что для каждого элемента во входном массиве есть ?. Затем мы будем использовать PDO или MySQLi для подготовки и выполнения запроса, как указано выше.

Использование оператора IN() со строками

Легко изменить между строками и целыми числами из-за связанных параметров. Для PDO изменений не требуется; для MySQLi измените str_repeat('i', на str_repeat('s',, если вам нужно проверить строки.

[1]: Я пропустил некоторую проверку ошибок для краткости. Вам нужно проверить обычные ошибки для каждого метода базы данных (или установить драйвер БД для исключения исключений).

[2]: Требуется PHP 5.6 или выше. Снова я пропустил некоторую проверку ошибок для краткости.

  • 6
    Что делает ... $ идентификаторы? Я получаю «Синтаксическая ошибка, неожиданная».
  • 0
    Я вижу их, я использую MySQLi и у меня есть PHP 5.6
Показать ещё 5 комментариев
47

Интс:

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

строки:

$query = "SELECT * FROM `$table` WHERE `$column` IN('".implode("','",$array)."')";
  • 1
    Почему '\' ?? Пожалуйста, скажите мне
  • 0
    спасибо. его работа в php 5.5
Показать ещё 1 комментарий
24

Предполагая, что вы должным образом санировали свои входы заранее...

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

Затем просто настройте свой запрос:

SELECT *
FROM galleries
WHERE id IN ( $matches ) 

Введите значения, соответствующие требованиям в зависимости от вашего набора данных.

  • 0
    Да, твой лучше, чем мой был: P
10

Использование:

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

Простой цикл for each будет работать.

Flavius ​​/AvatarKava путь лучше, но убедитесь, что ни одно из значений массива не содержит запятых.

7

Для MySQLi с помощью функции escape:

$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);
  • 0
    это хорошо, коротко и позволяет избежать уязвимости вставки кода! +1
7

Как ответ Flavius ​​Stef, вы можете использовать intval(), чтобы все id были значениями int:

$ids = join(',', array_map('intval', $galleries));  
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";
6

Мы должны позаботиться об уязвимости SQL-инъекций и о пустом состоянии. Я собираюсь обрабатывать оба, как показано ниже.

Для чистого числового массива используйте соответствующее преобразование типов: 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);

Цифровой массив можно дезинфицировать с использованием intval или floatval или 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;

СОВЕТ. Если вы хотите показать все записи (без фильтрации) в случае пустого массива вместо того, чтобы скрывать все строки, просто замените 0 на 1 в тройной ложной части.

  • 0
    Чтобы сделать мое решение однострочным (и безобразным) , на всякий случай, если кому-то понадобится: $query = 'SELECT * FROM galleries WHERE ' . (count($gallaries) ? "id IN ('" . implode("', '", array_map('escape', $gallaries)) . "')" : 0);
5

Мы можем использовать это предложение WHERE id IN, если мы правильно фильтруем входной массив. Что-то вроде этого:

$galleries = array();

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

Как показано ниже: Изображение 771

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

т.е. теперь вы должны безопасно использовать $query = "SELECT * FROM galleries WHERE id IN ({$galleryIds})";

  • 0
    @ levi-morrison опубликовал гораздо лучшее решение для этого.
5

Безопаснее.

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

Col. Shrapnel SafeMySQL библиотека для PHP предоставляет типовые метки в своих параметризованных запросах и включает в себя несколько удобных заполнителей для работы с массивами. Заполнитель ?a расширяет массив в список экранированных строк *.

Например:

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

* Обратите внимание, что поскольку MySQL выполняет автоматическое принуждение типов, не имеет значения, что SafeMySQL преобразует идентификаторы выше в строки - вы все равно получите правильный результат.

4

У вас может быть таблица 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

Таким образом, вы можете управлять простым отношением базы данных n2m без дополнительной таблицы и использовать только SQL без необходимости использования PHP или другого языка программирования.

2

Еще пример:

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

Помимо использования запроса IN, у вас есть две возможности сделать это, так как в запросе IN существует риск уязвимости SQL-инъекции. Вы можете использовать цикл, чтобы получить точные данные, которые вы хотите, или вы можете использовать запрос с OR 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

Поскольку исходный вопрос относится к массиву чисел, и я использую массив строк, я не мог заставить данные примеры работать.

Я обнаружил, что каждая строка должна быть инкапсулирована в одинарные кавычки для работы с функцией 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.

Вероятно, есть лучший способ добавить кавычки, но это работает.

  • 0
    Или $filter = "'" . implode("','",$status) . "'";
  • 1
    Это уязвимо для инъекций.
Показать ещё 1 комментарий
1

Просто использование инструкции IN будет работать.

SELECT * FROM galleries WHERE id IN(1,2,3,4);

0

Безопасный путь без 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 переменную $ids в массив
  • array_map Преобразование всех значений массива в целые числа
  • array_unique Удалить повторяющиеся значения
  • array_filter Удалить нулевые значения
  • implode Присоединить все значения к выбору IN
0

Ниже приведен метод, который я использовал, используя 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);
-3
$ids = "(".implode(',',$galleries).")";  // you can remplace "implode" with "join"  
$sql = "SELECT * FROM galleries WHERE id IN $ids";
-3

Основные методы предотвращения внедрения SQL:

  • Использование подготовленных операторов и параметризованных запросов
  • Экранирование специальных символов в вашей небезопасной переменной

Использование подготовленных операторов и запросов с параметризованными запросами считается лучшей практикой, но если вы выберете метод escaping characters, вы можете попробовать мой пример ниже.

Вы можете сгенерировать запросы с помощью array_map, чтобы добавить одну цитату к каждому из элементов в $galleries:

$galleries = array(1,2,5);

$galleries_str = implode(', ',
                     array_map(function(&$item){
                                   return "'" .mysql_real_escape_string($item) . "'";
                               }, $galleries));

$sql = "SELECT * FROM gallery WHERE id IN (" . $galleries_str . ");";

Сгенерированный $sql var будет:

SELECT * FROM gallery WHERE id IN ('1', '2', '5');

Примечание: mysql_real_escape_string, как описано в его документации здесь, был устаревшим в PHP 5.5.0, и он был удален в PHP 7.0.0. Вместо этого следует использовать расширение MySQLi или PDO_MySQL. См. Также MySQL: выбирая руководство по API и связанные с ним FAQ для получения дополнительной информации. Альтернативы этой функции включают:

  • mysqli_real_escape_string()

  • PDO:: цитата()

  • 3
    Это не только не добавляет ничего нового по сравнению с другими ответами, но и уязвимо для инъекций, несмотря на то, что принятый ответ содержал предупреждения о внедрении SQL в течение многих лет.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню