Могу ли я связать несколько значений как один параметр, используя MYSQLI и PHP? - PullRequest
3 голосов
/ 20 сентября 2010

Представьте, что у меня есть следующий запрос 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 знака вопроса в формате запроса? Единственная причина, по которой я не хочу этого делать, заключается в том, что количество вопросительных знаков является переменным, поэтому мне придется создавать строку запроса вручную.

Ответы [ 4 ]

5 голосов
/ 20 сентября 2010

Вы можете использовать PHP для записи заполнителей ?, используя str_repeat(), а затем просто bind_param все ваши параметры в цикле.

Просто будьте осторожны с запятой, которую возвращает str_repeat,Либо rtrim() it, либо вместо него используйте array_fill() для создания массива повторяющихся заполнителей, а затем join() для создания строки заполнителей.

$arrPlaceholders = array_fill(0, count($idList), '?') ;
$strPlaceholders = join(', ', $arrPlaceholders) ;

Тогда ваш запрос может быть:

$query = "SELECT id,name FROM user WHERE id IN ($strPlaceholders)";

И вы можете связать свои параметры в цикле.

1 голос
/ 28 мая 2011

Возможно, это немного устарело, но мне самому это было интересно. Итак, я выполнил тест. Сначала я создал простую таблицу:

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 МБ. Случайное число было создано с помощью функции php mt_rand ().

Вот "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

Параллельно мы получаем эту таблицу (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-инъекцию вообще), и, кажется, не намного медленнее в диапазоне ~ 10 элементов. Повторение теста с ~ 10 элементами иногда приводит к тому, что "Fetch.php" побеждает в тесте. В целом, Fetch2.php, конечно, победил, но они определенно близки в этом диапазоне.

Я бы сказал, что ... если у вас менее 100 элементов, просто воспользуйтесь подготовленным оператором и выполняйте его повторно. Это то, для чего подготовленные заявления предназначены в конце концов. Конечно, ничто не сравнится с односторонним обращением к базе данных, но подготовленный подход к заявлению может иметь приемлемую производительность. Конечно, тест на вашей собственной системе. Скорее всего, приведенный выше тест был слишком тривиальным (нет никаких объединений или подзапросов ... и БД находится в той же системе, что и скрипт php ...)

1 голос
/ 20 сентября 2010

Можно ли связать несколько значений как один параметр, используя MYSQLI и PHP?

Нет, вы не можете.

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

0 голосов
/ 20 сентября 2010

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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...