Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot figure out how to run a mysqli_multi_query and use the results from the last query

I've never used mysqli_multi_query before and it's boggling my brain, any examples I find on the net aren't helping me to figure out exactly what it is I want to do.

Here is my code:

<?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

    }
?>

All I'm trying to do is run a multiple query and then use the final results from those 4 queries and put them into my tables.

the code above really doesn't work at all, I just get the following error:

Warning: mysqli_fetch_array() expects parameter 1 to be mysqli_result, boolean given in C:\xampp\htdocs\hydroboard\hydro_reporting_2010.php on line 391

any help?

like image 256
Harry Weinert Avatar asked Jan 12 '11 10:01

Harry Weinert


3 Answers

From the manual: mysqli_multi_query() returns a bool indicating success.

To retrieve the resultset from the first query you can use mysqli_use_result() or mysqli_store_result(). All subsequent query results can be processed using mysqli_more_results() and mysqli_next_result().

Here is a function that returns the last result of a multi-query:

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

Usage:

$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);
like image 139
rik Avatar answered Oct 09 '22 05:10

rik


Okay after some fiddling around, trial and error and taking reference from another post that I came across in a Google search I've managed to solve my problem!

Here's the new code:

<?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

        }
    }
?>

after sticking mysqli_next_result in there multiple times for each query it magically worked! yay! I understand why it works, because i'm telling it to skip to the next result 3 times, so it skips to the result for query #4 which is the one i want to use.

Seems a bit clunky to me though, there should just be a command for something like mysqli_last_result($link) or something if you ask me...

Thanks for the help rik and f00, I got there eventually :)

like image 30
Harry Weinert Avatar answered Oct 09 '22 04:10

Harry Weinert


I would simplify what you're trying to do by creating a stored procedure which will produce the leader/agent stats and then just call it from your php (single call) as follows:

full script here : http://pastie.org/1451802

alternatively, you could probably combine your individual queries into a single select/group by statement.

see here : 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;

Stored procedure

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 script

<?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();
?>

Kinda simpler now - hope it helps :)

like image 21
Jon Black Avatar answered Oct 09 '22 05:10

Jon Black