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


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

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

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

Author: Braiam, 2009-05-25

18 answers

БЕРЕГИТЕСЬ! Этот ответ содержит серьезную уязвимость для SQL-инъекции . Не используйте примеры кода, представленные здесь, не убедившись, что любой внешний ввод очищен.

$ids = join("','",$galleries);   
$sql = "SELECT * FROM galleries WHERE id IN ('$ids')";
 278
Author: Flavius Stef, 2017-08-22 17:24:00

Использование 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 или выше. И снова я опустил некоторые проверки на ошибки для краткости.

 281
Author: Levi Morrison, 2015-05-15 20:58:40

Ints:

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

Строки:

$query = "SELECT * FROM `$table` WHERE `$column` IN('".implode("','",$array)."')";
 48
Author: user542568, 2014-03-19 13:30:53

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

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

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

SELECT *
FROM galleries
WHERE id IN ( $matches ) 

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

 26
Author: AvatarKava, 2009-05-25 19:38:46

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

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

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

Способ Флавия/Аватаркавы лучше, но убедитесь, что ни одно из значений массива не содержит запятых.

 10
Author: Matthew Flaschen, 2017-05-23 12:10:32

Как Ответ Флавия Стеффа , вы можете использовать intval(), чтобы убедиться, что все id являются значениями int:

$ids = join(',', array_map('intval', $galleries));  
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";
 8
Author: Van-Duyet Le, 2017-05-23 11:55:07

Для 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);
 7
Author: artoodetoo, 2017-11-08 09:28:26

Мы должны позаботиться о Инъекция 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 в ложной части троицы.

 6
Author: Izhar Aazmi, 2015-10-18 20:05:31

Безопаснее.

$galleries = array(1,2,5);
array_walk($galleries , 'intval');
$ids = implode(',', $galleries);
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";
 5
Author: Filipe, 2015-04-13 15:41:31

Мы можем использовать это предложение "ГДЕ идентификатор В", если мы правильно отфильтруем входной массив. Что-то вроде этого:

$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
Author: Supratim Roy, 2015-04-15 17:15:52

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

Например:

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

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

 4
Author: Mark Amery, 2015-05-17 19:03:03

У вас может быть таблица 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 или какой-либо другой язык программирования.

 4
Author: SERJOU, 2015-10-18 19:46:24

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

Я обнаружил, что каждая строка должна быть заключена в одинарные кавычки для работы с функцией 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
Author: RJaus, 2015-08-04 14:34:10

Помимо использования запроса IN, у вас есть два варианта сделать это, так как в запросе IN существует риск уязвимости при внедрении SQL. Вы можете использовать цикл для получения точных данных, которые вы хотите, или вы можете использовать запрос с ИЛИ случаем

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
Author: Gaurav Singh, 2015-10-18 19:49:48

Еще один пример:

$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
Author: Ricardo Canelas, 2016-08-19 10:45:30

Ниже приведен метод, который я использовал, используя 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);
 0
Author: kojow7, 2018-01-24 16:31:28

Безопасный способ без 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 Объединить все значения в выделенные
 0
Author: Lito, 2018-01-30 09:12:17

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

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

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

Вы можете генерировать запросы с помощью 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 и связанные с ним часто задаваемые вопросы для получения дополнительной информации. Альтернативы этой функции включают:

  • Строка Mysqli_real_escape_string()

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

 -2
Author: David, 2017-03-26 08:48:07