Przekazywanie tablicy do zapytania za pomocą klauzuli WHERE

Biorąc pod uwagę tablicę identyfikatorów $galleries = array(1,2,5) chcę mieć zapytanie SQL, które używa wartości tablicy w klauzuli WHERE jak:

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

Jak mogę wygenerować ciąg zapytania do użycia z MySQL?

Author: Braiam, 2009-05-25

18 answers

Uważaj! Ta odpowiedź zawiera poważną lukę SQL injection . Nie używaj próbek kodu przedstawionych tutaj, bez upewnienia się, że jakiekolwiek zewnętrzne wejście jest dezynfekowane.

$ids = join("','",$galleries);   
$sql = "SELECT * FROM galleries WHERE id IN ('$ids')";
 278
Author: Flavius Stef,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-22 17:24:00

Używanie 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);

Korzystanie Z 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();

Wyjaśnienie:

Użyj operatora SQL IN(), aby sprawdzić, czy wartość istnieje na danej liście.

Ogólnie wygląda to tak:

expr IN (value,...)

Możemy zbudować wyrażenie do umieszczenia wewnątrz () z naszej tablicy. Zauważ, że w nawiasie musi być co najmniej jedna wartość lub MySQL zwróci błąd; oznacza to upewnienie się, że nasz tablica wejściowa ma co najmniej jedną wartość. Aby zapobiec atakom SQL injection, najpierw Wygeneruj ? dla każdego elementu wejściowego, aby utworzyć sparametryzowane zapytanie. Tutaj zakładam, że tablica zawierająca Twoje ID nazywa się $ids:

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

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

Podana tablica wejściowa trzech elementów $select będzie wyglądać następująco:

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

Ponownie zauważ, że dla każdego elementu w tablicy wejściowej jest ?. Następnie użyjemy PDO lub MySQLi, aby przygotować i wykonać zapytanie zgodnie z zaznaczeniem powyżej.

Używanie operatora IN() z łańcuchami

Łatwo jest zmieniać między łańcuchami i liczbami całkowitymi ze względu na powiązane parametry. Dla PDO nie jest wymagana zmiana; dla MySQLi Zmień str_repeat('i', na str_repeat('s', Jeśli chcesz sprawdzić ciągi.

[1]: pominąłem pewne sprawdzanie błędów pod kątem zwięzłości. Musisz sprawdzić typowe błędy dla każdej metody bazy danych (lub ustawić sterownik DB, aby rzucał wyjątki).

[2]: wymaga PHP 5.6 lub nowszy. Ponownie pominąłem pewne sprawdzanie błędów pod kątem zwięzłości.

 281
Author: Levi Morrison,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-15 20:58:40

Ints:

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

Ciągi:

$query = "SELECT * FROM `$table` WHERE `$column` IN('".implode("','",$array)."')";
 48
Author: user542568,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-19 13:30:53

Zakładając, że odpowiednio wyczyścisz swoje wejścia wcześniej...

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

Następnie po prostu dostosuj swoje zapytanie:

SELECT *
FROM galleries
WHERE id IN ( $matches ) 

Cytować wartości odpowiednio w zależności od zestawu danych.

 26
Author: AvatarKava,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-25 19:38:46

Użycie:

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

Prosta pętla for each będzie działać.

Sposób Flavius / AvatarKava jest lepszy, ale upewnij się, że żadna z wartości tablicy nie zawiera przecinków.

 10
Author: Matthew Flaschen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:32

Jako odpowiedź Flavius Stef, możesz użyć intval(), aby upewnić się, że wszystkie id są wartościami int:

$ids = join(',', array_map('intval', $galleries));  
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";
 8
Author: Van-Duyet Le,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:07

Dla MySQLi z funkcją 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)");

Dla PDO z przygotowanym oświadczeniem:

$qmarks = implode(',', array_fill(0, count($ids), '?'));
$sth = $dbh->prepare("SELECT * FROM galleries WHERE id IN ($qmarks)");
$sth->execute($ids);
 7
Author: artoodetoo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-08 09:28:26

Powinniśmy zadbać o SQL injection luki i pusty warunek . Mam zamiar obsłużyć oba, jak poniżej.

Dla czystej tablicy liczbowej, użyj odpowiedniej konwersji typu viz intval lub floatval lub doubleval nad każdym elementem. Dla typów ciągów mysqli_real_escape_string() które mogą być również stosowane do wartości liczbowych, jeśli chcesz. MySQL umożliwia zarówno numery, jak i warianty daty jako ciąg znaków .

Aby odpowiednio uciec wartości przed po przejściu do zapytania, Utwórz funkcję podobną do:

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

Taka funkcja najprawdopodobniej byłaby już dostępna w Twojej aplikacji, a może już ją stworzyłeś.

Wyczyść tablicę łańcuchów jak:

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

Tablica numeryczna może być uporządkowana za pomocą intval lub floatval lub doubleval jako odpowiednie:

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

Następnie w końcu zbuduj warunek zapytania

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

Lub

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

Ponieważ tablica może być również pusta czasami, np. $galleries = array(); powinniśmy zauważyć, że IN () nie pozwala na utworzenie pustej listy. Można również użyć OR zamiast tego, ale problem pozostaje. Tak więc powyższa Kontrola, count($values), ma zapewnić to samo.

I dodać go do zapytania końcowego:

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

Wskazówka: jeśli chcesz pokazać wszystkie rekordy (bez filtrowania) w przypadku pustej tablicy, zamiast ukrywać wszystkie wiersze, po prostu zastąp 0 Z 1 w fałszywej części trójki.

 6
Author: Izhar Aazmi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-18 20:05:31

Bezpieczniej.

$galleries = array(1,2,5);
array_walk($galleries , 'intval');
$ids = implode(',', $galleries);
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";
 5
Author: Filipe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-13 15:41:31

Możemy użyć klauzuli "WHERE id IN", jeśli odpowiednio filtrujemy tablicę wejściową. Coś takiego:

$galleries = array();

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

Jak w przykładzie poniżej: Tutaj wpisz opis obrazka

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

Czyli teraz należy bezpiecznie używać $query = "SELECT * FROM galleries WHERE id IN ({$galleryIds})";

 5
Author: Supratim Roy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-15 17:15:52

Biblioteka

Col.Shrapnel ' s SafeMySQL dla PHP dostarcza podpowiedzi typu w parametryzowanych zapytaniach i zawiera kilka wygodnych elementów zastępczych do pracy z tablicami. Symbol zastępczy ?a rozszerza tablicę do oddzielonej przecinkami listy ciągów znaków*.

Na przykład:

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

* zauważ, że ponieważ MySQL wykonuje automatyczny przymus typu, nie ma znaczenia, że SafeMySQL przekonwertuje powyższe identyfikatory na ciągi znaków-nadal otrzymasz poprawne wynik.

 4
Author: Mark Amery,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-17 19:03:03

Możesz mieć tabelę texts (T_ID (int), T_TEXT (text)) i Tabela test (id (int), var (varchar(255)))

W insert into test values (1, '1,2,3') ; następujące wiersze będą wyświetlane z tekstów tabeli gdzie 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

W ten sposób można zarządzać prostą relacją bazy danych n2m bez dodatkowej tabeli i używać tylko SQL bez konieczności używania PHP lub innego języka programowania.

 4
Author: SERJOU,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-18 19:46:24

Ponieważ oryginalne pytanie odnosi się do tablicy liczb i używam tablicy łańcuchów, nie mogłem sprawić, by podane przykłady działały.

Odkryłem, że każdy ciąg znaków musi być zamknięty w pojedynczych cudzysłowach, aby pracować z funkcją IN().

Oto moje rozwiązanie

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

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

Jak widać pierwsza funkcja zawija każdą zmienną tablicy w single quotes (\'), a następnie imploduje tablicę.

Uwaga: $status nie zawiera pojedynczych cudzysłowów w instrukcji SQL.

Jest prawdopodobnie ładniejszy sposób na dodanie cytatów, ale to działa.

 2
Author: RJaus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-04 14:34:10

Oprócz korzystania z zapytania IN, masz dwie opcje, aby to zrobić, ponieważ w zapytaniu IN istnieje ryzyko luki w SQL injection. Możesz użyć looping , aby uzyskać dokładne dane lub możesz użyć zapytania z lub 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
Author: Gaurav Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-18 19:49:48

Więcej przykład:

$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,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-19 10:45:30

Poniżej znajduje się metoda, której użyłem, używając PDO z nazwanymi symbolami zastępczymi dla innych danych. Aby przezwyciężyć SQL injection filtruję tablicę tak, aby akceptowała tylko wartości, które są liczbami całkowitymi i odrzucała wszystkie inne.

$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,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-24 16:31:28

Bezpieczny sposób bez PDO:

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

if ($ids) {
    $query = 'SELECT * FROM `galleries` WHERE `id` IN ('.implode(',', $ids).');';
}
  • (array)$ids Cast $ids zmienna do tablicy
  • array_map Przekształć wszystkie wartości tablicy w liczby całkowite
  • array_unique Usuń powtarzające się wartości
  • array_filter Usuń wartości zerowe
  • implode Dołącz wszystkie wartości do zaznaczenia
 0
Author: Lito,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-30 09:12:17

Podstawowe metody zapobiegania SQL injection to:

  • użyj gotowych instrukcji i parametryzowanych zapytań
  • unikanie znaków specjalnych w zmiennej unsafe

Używanie gotowych instrukcji i parametryzowanych zapytań zapytanie jest uważane za lepszą praktykę, ale jeśli wybierzesz metodę znaków specjalnych, możesz wypróbować mój przykład poniżej.

Możesz wygenerować zapytania za pomocą array_map, aby dodać pojedynczy cytat do każdego z elementów w $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 . ");";

Wygenerowany $ SQL var będzie wynosił:

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

Uwaga: mysql_real_escape_string , jak opisano w jego dokumentacja tutaj, została przestarzała w PHP 5.5.0, a została usunięta w PHP 7.0.0. Zamiast tego należy użyć rozszerzenia MySQLi lub PDO_MySQL. Zobacz także MySQL: wybór przewodnika API i powiązane FAQ, aby uzyskać więcej informacji. Alternatywy dla tej funkcji obejmują:

  • Mysqli_real_escape_string()

  • PDO:: cytat()

 -2
Author: David,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-26 08:48:07