Представьте, что у меня есть следующий SQL-запрос:
SELECT id,name FROM user WHERE id IN ('id1','id2','id3')
Теперь представьте, что мне нужен массив идентификаторов, предоставляемый PHP. Поэтому у меня есть что-то вроде этого:
$idList = array('id1','id2','id3'); $query = "SELECT id,name FROM user WHERE id IN (?)"; $stmt = $db->prepare($query); $stmt->bind_param(/*Something*/);
Что я могу заменить /*Something*/
чтобы получить те же результаты, что и исходный запрос? Или мне нужно поставить 3 вопроса в формате запроса? Единственная причина, по которой я не хочу этого делать, состоит в том, что количество вопросительных знаков является переменным, поэтому мне придется строить строку запроса вручную.
Можно ли связать несколько значений как один параметр с помощью MYSQLI и PHP?
Нет, ты не можешь.
Для вашей ситуации вы должны строить строку запроса программно. Если вам гарантировано, это всегда будет три значения, вы можете добавить три маркера в SQL, а затем привязать их через цикл.
Вы можете использовать PHP для записи заполнителей ?
используя str_repeat()
, а затем просто bind_param
все ваши параметры в цикле.
Просто будьте осторожны с конечной запятой, которую str_repeat вернет. Либо rtrim()
либо вместо этого используйте array_fill()
для создания массива повторяющихся заполнителей, а затем join()
для создания вашей строки-заполнители.
$arrPlaceholders = array_fill(0, count($idList), '?') ; $strPlaceholders = join(', ', $arrPlaceholders) ;
Тогда ваш запрос может быть:
$query = "SELECT id,name FROM user WHERE id IN ($strPlaceholders)";
И вы можете привязать свои параметры в цикле.
Это может быть немного старым, но мне это было интересно. Поэтому я выполнил бенчмарк. Во-первых, я создал простую таблицу:
SELECT * from random LIMIT 10; +----+------------+ | id | rand_stuff | +----+------------+ | 1 | 1988585319 | | 2 | 1926594853 | | 3 | 820681972 | | 4 | 950331574 | | 5 | 540721998 | | 6 | 1284256353 | | 7 | 12804417 | | 8 | 2130482967 | | 9 | 2018786156 | | 10 | 285818156 | +----+------------+ SELECT count(id) from random; +-----------+ | count(id) | +-----------+ | 3365586 | +-----------+ /var/lib/mysql/benchmark# ls -laFh total 101M drwx------ 2 mysql mysql 4.0K 2011-05-28 00:06 ./ drwxr-xr-x 7 mysql mysql 4.0K 2011-05-27 23:53 ../ -rw-rw---- 1 mysql mysql 65 2011-05-27 23:53 db.opt -rw-rw---- 1 mysql mysql 8.4K 2011-05-28 00:06 random.frm -rw-rw---- 1 mysql mysql 55M 2011-05-28 00:32 random.MYD -rw-rw---- 1 mysql mysql 47M 2011-05-28 00:32 random.MYI
Его тривиальная структура, которая весит около 100 МБ. Случайное число было создано с помощью функции mt_rand () php.
Вот «Fetch.php»:
<?php $loops = $argv[1]; $mysqli = new mysqli("localhost", "bench", "bench", "benchmark"); if(mysqli_connect_errno()){ printf("Connect Failed: %s\n", mysqli_connect_error()); exit(); } if($stmt = $mysqli->prepare("SELECT rand_stuff FROM random WHERE id = ?")){ for($i=1; $i<$loops; $i++){ $stmt->bind_param("i", $i) or die; $stmt->execute() or die; $stmt->bind_result($value) or die; $stmt->fetch(); echo "$i \t $value\n"; } $stmt->close(); }
И некоторые ориентиры:
$time php fetch.php 10 > /dev/null real 0m0.043s user 0m0.024s sys 0m0.012s $ time php fetch.php 100 > /dev/null real 0m0.057s user 0m0.044s sys 0m0.000s $ time php fetch.php 1000 > /dev/null real 0m0.166s user 0m0.080s sys 0m0.012s $ time php fetch.php 10000 > /dev/null real 0m1.083s user 0m0.412s sys 0m0.124s
Вот fetch2.php
<?php $loops = $argv[1]; $mysqli = new mysqli("localhost", "bench", "bench", "benchmark"); if(mysqli_connect_errno()){ printf("Connect Failed: %s\n", mysqli_connect_error()); exit(); } $array = array(); for($i=1; $i<$loops; $i++){ $array[] = $i; } $joined_array = join($array, ','); $results = $mysqli->query("SELECT id, rand_stuff FROM random WHERE id IN ($joined_array)"); while($row = $results->fetch_row()){ $val1 = $row[0]; $val2 = $row[1]; echo "$val1\t$val2\n"; }
И вот его связанные тесты.
$time php fetch2.php 10 > /dev/null real 0m0.037s user 0m0.028s sys 0m0.008s $time php fetch2.php 100 > /dev/null real 0m0.044s user 0m0.032s sys 0m0.008s $ time php fetch2.php 1000 > /dev/null real 0m0.050s user 0m0.036s sys 0m0.016s $ time php fetch2.php 10000 > /dev/null real 0m0.117s user 0m0.088s sys 0m0.024s
Side By Side, мы получаем эту таблицу (Fetch.php – это «WHERE id =?» И подготовленные операторы, а Fetch2.php использует синтаксис «WHERE x IN ()» для тупого запроса):
+--------+-----------+------------+ | Loop | Fetch.php | Fetch2.php | +--------+-----------+------------+ | 10 | .043s | .037s | | 100 | .057s | .044s | | 1000 | .116s | .050s | | 10000 | 1.083s | .117s | +--------+-----------+------------+
Очевидно, что «Fetch2.php» более эффективен, но в этом тесте … это, похоже, не имеет особого значения, пока вы не попадете в диапазон 100+ элементов. Итерация над подготовленным оператором проста и безопасна (никаких шансов на SQL Injection вообще) и, похоже, не намного медленнее в диапазоне ~ 10 элементов. Повторение теста с ~ 10 элементами иногда может иметь «Fetch.php». В целом, Fetch2.php выиграл, конечно, но они определенно близки к этому диапазону.
Я был бы склонен сказать, что … если у вас есть менее 100 элементов, просто используйте подготовленный оператор и выполните его повторно. Вот что готовые заявления предназначены в конце концов. Разумеется, ничто не сравнится с однократной поездкой в базу данных, но подготовленный подход к заявлению может иметь приемлемую производительность. Конечно, бенчмарк в вашей собственной системе. Скорее всего, вышеприведенный тест был слишком тривиальным (нет никаких соединений или подзапросов … и db находится в той же системе, что и скрипт php …)
implode
будет самым простым решением
$idList = array('id1','id2','id3'); $query = "SELECT id,name FROM user WHERE id IN (?)"; $stmt = $db->prepare($query); $_param = is_array($idList) ? implode(',',$idList) : $idList; $stmt->bind_param(1, $_param);