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?
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')";
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.
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)."')";
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.
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.
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)";
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);
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.
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)";
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:
$galleryIds = implode(',', $galleries);
Czyli teraz należy bezpiecznie używać $query = "SELECT * FROM galleries WHERE id IN ({$galleryIds})";
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.
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.
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.
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;
}
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();
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);
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
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()
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