Невозможно выяснить, как запустить mysqli_multi_query и использовать результаты из последнего запроса

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

Вот мой код:

<?php $link = mysqli_connect("server", "user", "pass", "db"); if (mysqli_connect_errno()) { printf("Connect failed: %s\n", mysqli_connect_error()); exit(); } $agentsquery = "CREATE TEMPORARY TABLE LeaderBoard ( `agent_name` varchar(20) NOT NULL, `job_number` int(5) NOT NULL, `job_value` decimal(3,1) NOT NULL, `points_value` decimal(8,2) NOT NULL );"; $agentsquery .= "INSERT INTO LeaderBoard (`agent_name`, `job_number`, `job_value`, `points_value`) SELECT agent_name, job_number, job_value, points_value FROM jobs WHERE YEAR(booked_date) = $current_year && WEEKOFYEAR(booked_date) = $weeknum;"; $agentsquery .= "INSERT INTO LeaderBoard (`agent_name`) SELECT DISTINCT agent_name FROM apps WHERE YEAR(booked_date) = $current_year && WEEKOFYEAR(booked_date) = $weeknum;"; $agentsquery .= "SELECT agent_name, SUM(job_value), SUM(points_value) FROM leaderboard GROUP BY agent_name ORDER BY SUM(points_value) DESC"; $i = 0; $agentsresult = mysqli_multi_query($link, $agentsquery); while ($row = mysqli_fetch_array($agentsresult)){ $number_of_apps = getAgentAppsWeek($row['agent_name'],$weeknum,$current_year); $i++; ?> <tr class="tr<?php echo ($i & 1) ?>"> <td style="font-weight: bold;"><?php echo $row['agent_name'] ?></td> <td><?php echo $row['SUM(job_value)'] ?></td> <td><?php echo $row['SUM(points_value)'] ?></td> <td><?php echo $number_of_apps; ?></td> </tr> <?php } ?> 

Все, что я пытаюсь сделать, это запустить несколько запросов, а затем использовать окончательные результаты из этих 4 запросов и поместить их в мои таблицы.

код выше действительно не работает вообще, я просто получаю следующую ошибку:

Предупреждение: mysqli_fetch_array () ожидает, что параметр 1 будет mysqli_result, boolean задан в C: \ xampp \ htdocs \ hydroboard \ hydro_reporting_2010.php в строке 391

любая помощь?

Из руководства : mysqli_multi_query() возвращает bool указанием успеха.

Чтобы получить набор результатов из первого запроса, вы можете использовать mysqli_use_result () или mysqli_store_result (). Все последующие результаты запроса могут быть обработаны с помощью mysqli_more_results () и mysqli_next_result ().

Вот функция, которая возвращает последний результат многозадачного запроса:

 function mysqli_last_result($link) { while (mysqli_more_results($link)) { mysqli_use_result($link); mysqli_next_result($link); } return mysqli_store_result($link); } 

Применение:

 $link = mysqli_connect(); $query = "SELECT 1;"; $query .= "SELECT 2;"; $query .= "SELECT 3"; mysqli_multi_query($link, $query); $result = mysqli_last_result($link); $row = $result->fetch_row(); echo $row[0]; // prints "3" $result->free(); mysqli_close($link); 

Я бы упростил то, что вы пытаетесь сделать, создав хранимая процедура, которая будет создавать статистику лидера / агента, а затем просто вызовите ее из вашего php (одного вызова) следующим образом:

полный скрипт здесь: http://pastie.org/1451802

альтернативно, вы могли бы объединить свои индивидуальные запросы в один оператор select / group by.

см. здесь: http://pastie.org/1451842

 select leaders.agent_name, sum(leaders.job_value) as sum_job_value, sum(leaders.points_value) as sum_points_value from ( select agent_name, job_number, job_value, points_value from jobs where year(booked_date) = 2011 and weekofyear(booked_date) = 2 union all select distinct agent_name, 0,0,0 from apps where year(booked_date) = 2011 and weekofyear(booked_date) = 2 ) leaders group by agent_name order by sum_points_value desc; 

Хранимая процедура

 drop procedure if exists list_leaders; delimiter # create procedure list_leaders ( in p_year smallint unsigned, in p_week tinyint unsigned ) begin create temporary table tmp_leaders( agent_name varchar(20) not null, job_number int unsigned not null default 0, -- note the default values job_value decimal(3,1) not null default 0, points_value decimal(8,2) not null default 0 )engine=memory; insert into tmp_leaders (agent_name, job_number, job_value, points_value) select agent_name, job_number, job_value, points_value from jobs where year(booked_date) = p_year and weekofyear(booked_date) = p_week; insert into tmp_leaders (agent_name) -- requires default values otherwise you will get nulls select distinct agent_name from apps where year(booked_date) = p_year and weekofyear(booked_date) = p_week; select agent_name, sum(job_value) as sum_job_value, sum(points_value) as sum_points_value from tmp_leaders group by agent_name order by sum_points_value desc; drop temporary table if exists tmp_leaders; end# delimiter ; call list_leaders(year(curdate()), weekofyear(curdate())); 

PHP-скрипт

 <?php ob_start(); try { $db = new mysqli("localhost", "foo_dbo", "pass", "foo_db", 3306); if ($db->connect_errno) throw new exception(sprintf("Could not connect: %s", $db->connect_error)); $sqlCmd = "call list_leaders(2011, 2)"; $result = $db->query($sqlCmd); if(!$result) throw new exception(sprintf("Invalid query : %s", $sqlCmd)); if($db->affected_rows <= 0){ echo "no leaders found !"; } else{ $leaders = $result->fetch_all(MYSQLI_ASSOC); foreach($leaders as $ldr){ // do stuff echo $ldr["agent_name"], "<br/>"; } } } catch(exception $ex) { ob_clean(); echo sprintf("zomg borked - %s", $ex->getMessage()); } if(!$db->connect_errno) $db->close(); ob_end_flush(); ?> 

Теперь попробуй проще – надеюсь, что это поможет 🙂

Хорошо после некоторых попыток, проб и ошибок и получения ссылки с другого сообщения, которое я нашел в поиске Google, мне удалось решить мою проблему!

Вот новый код:

 <?php $link = mysqli_connect("server", "user", "pass", "db"); if (mysqli_connect_errno()) { printf("Connect failed: %s\n", mysqli_connect_error()); exit(); } $agentsquery = "CREATE TEMPORARY TABLE LeaderBoard ( `agent_name` varchar(20) NOT NULL, `job_number` int(5) NOT NULL, `job_value` decimal(3,1) NOT NULL, `points_value` decimal(8,2) NOT NULL );"; $agentsquery .= "INSERT INTO LeaderBoard (`agent_name`, `job_number`, `job_value`, `points_value`) SELECT agent_name, job_number, job_value, points_value FROM jobs WHERE YEAR(booked_date) = $current_year && WEEKOFYEAR(booked_date) = $weeknum;"; $agentsquery .= "INSERT INTO LeaderBoard (`agent_name`) SELECT DISTINCT agent_name FROM apps WHERE YEAR(booked_date) = $current_year && WEEKOFYEAR(booked_date) = $weeknum;"; $agentsquery .= "SELECT agent_name, SUM(job_value), SUM(points_value) FROM leaderboard GROUP BY agent_name ORDER BY SUM(points_value) DESC"; mysqli_multi_query($link, $agentsquery) or die("MySQL Error: " . mysqli_error($link) . "<hr>\nQuery: $agentsquery"); mysqli_next_result($link); mysqli_next_result($link); mysqli_next_result($link); if ($result = mysqli_store_result($link)) { $i = 0; while ($row = mysqli_fetch_array($result)){ $number_of_apps = getAgentAppsWeek($row['agent_name'],$weeknum,$current_year); $i++; ?> <tr class="tr<?php echo ($i & 1) ?>"> <td style="font-weight: bold;"><?php echo $row['agent_name'] ?></td> <td><?php echo $row['SUM(job_value)'] ?></td> <td><?php echo $row['SUM(points_value)'] ?></td> <td><?php echo $number_of_apps; ?></td> </tr> <?php } } ?> 

после приклеивания mysqli_next_result в несколько раз для каждого запроса он волшебным образом работал! ура! Я понимаю, почему это работает, потому что я говорю ему, чтобы перейти к следующему результату 3 раза, поэтому он пропускает результат для запроса №4, который я хочу использовать.

Кажется немного неуклюжим для меня, хотя, должна быть только команда для чего-то вроде mysqli_last_result ($ link) или что-то, если вы спросите меня …

Спасибо за помощь rik и f00, я получил там в конце концов 🙂

Сохраните результат в переменной и в конце используйте эту переменную.

  do{ if($result = $con->store_result()){ $data=$result->fetch_all(); $result->free(); } } while($con->more_results()&&$con->next_result()); echo(json_encode($data)); 

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

  • Хотя несколько строк mysqli_next_result ($ link) дают ожидаемый результат, он выглядит немного хакерским. Поэтому я собираюсь создать DO-WHILE на их месте.
  • Решение rik, связанное с созданием функции mysqli_last_result ($ link), является излишним. Мое состояние WHILE достигнет того же эффекта. Кроме того, код rik смешивает процедурные и объектно-ориентированные стили, чего следует избегать.
  • Я не буду подпрыгивать между php и html.
  • Я просто немного подберу класс.
  • Я поставлю getAgentAppsWeek () непосредственно в строке, так как мне не нравится создавать переменные, которые упоминаются только один раз; и потому, что Гарри Вайнерт хорошо поработал над тем, чтобы назвать функцию.
  • ИМО это просто читать легче.

     if(mysqli_multi_query($link,$agentsquery)){ do{ if($result=mysqli_store_result($link)){ // ignore if no record set while($row=mysqli_fetch_array($result)){ echo "<tr class=\"tr",(++$i & 1),"\">"; echo "<td style=\"font-weight:bold;\">",$row['agent_name'],"</td>"; echo "<td>",$row['SUM(job_value)'],"</td>"; echo "<td>",$row['SUM(points_value)'],"</td>"; echo "<td>",getAgentAppsWeek($row['agent_name'],$weeknum,$current_year),"</td>"; echo "</tr>"; } mysqli_free_result($result); } } while(mysqli_more_results($link) && mysqli_next_result($link)); } if($error_mess=mysqli_error($link)){ echo "<tr class=\"error\"><td colspan=\"4\">Error: $error_mess</td></tr>"; } //if any query returns false, mysqli_multi_query will stop // and the individual query error to blame will be provided. в if(mysqli_multi_query($link,$agentsquery)){ do{ if($result=mysqli_store_result($link)){ // ignore if no record set while($row=mysqli_fetch_array($result)){ echo "<tr class=\"tr",(++$i & 1),"\">"; echo "<td style=\"font-weight:bold;\">",$row['agent_name'],"</td>"; echo "<td>",$row['SUM(job_value)'],"</td>"; echo "<td>",$row['SUM(points_value)'],"</td>"; echo "<td>",getAgentAppsWeek($row['agent_name'],$weeknum,$current_year),"</td>"; echo "</tr>"; } mysqli_free_result($result); } } while(mysqli_more_results($link) && mysqli_next_result($link)); } if($error_mess=mysqli_error($link)){ echo "<tr class=\"error\"><td colspan=\"4\">Error: $error_mess</td></tr>"; } //if any query returns false, mysqli_multi_query will stop // and the individual query error to blame will be provided. 

Полагаю, я тоже наброшу свою шляпу на ринг по этому вопросу (и это все еще процедурный и весь этот джаз). Это не является доказательством дурака, но я боролся с MySQLi, и это группа веселых мужчин, вовлеченных в multi_query, и я не мог заставить ее играть так, как я хотел, или иметь необходимую мне гибкость. Я видел несколько примеров, когда некоторые программисты просто запускали explode(';', $sql_statements) которых мои глаза истекали кровью, как это ужасно неправильно.

Мое решение может не сработать для вас, но это сработало для меня. (нет, это тоже не пуленепробиваемый, но выполняет работу для моего конкретного приложения).

 <?php $file = file_get_contents('test_multiple_queries.sql'); $result = preg_split("/;(?=\s*(create|insert|update|alter|show|explain|truncate|drop|delete|replace|start|lock|commit|rollback|set|begin|declare|rename|load|begin|describe|help))/im", $file); $result = array_map('trim', $result); foreach($result as $sql_query) { // Procedural style $my_query = mysqli_query($link, $sql_query); // Now you can get errors easily, or affected_rows, or whatever // using much simpler, readable code mysqli_error($link); mysqli_affected_rows($link); // or go crazy with some other stuff $words = preg_split("/\s+/", $sql_query); switch(strtolower($words[0])) { case 'insert': // do something nifty like... echo 'New ID: '.mysqli_insert_id($link)."\n"; break; case 'drop': // obviously run this before the query, simply here for example echo 'Hey young (man|lady)! We don\'t drop anything!'; break; case 'select': // hooray for selecting stuff while($rs = mysqli_fetch_assoc($my_query)) { // have fun with data } break; } }