Is there a way to handle multiple result sets from a single prepared query when the result sets have different columns?
I have a procedure like this:
CREATE PROCEDURE usp_CountAndList (in_SomeValue int)
BEGIN
SELECT COUNT(*) AS ListCount FROM Table WHERE SomeValue = in_SomeValue;
SELECT
Name, Cost, Text
FROM Table WHERE SomeValue = in_SomeValue
ORDER BY
Name
LIMIT 25;
END
And my PHP code looks like this:
$some_value = $_POST["SomeValue"];
if($some_value != null) {
$dbh = mysqli_connect(...connection stuff...) or die ('I cannot connect to the database.');
$query = $dbh->prepare("CALL usp_CountAndList( ? );");
$query->bind_param("i", $some_value);
if($query->execute() == true) {
$meta = $query->result_metadata();
$fields = $meta->fetch_fields();
var_dump($fields);
$query->store_result();
$query->bind_result($list_count);
while($query->fetch()) {
print_r("<TR>");
print_r("<TD>" . $list_count ."</TD>");
print_r("</TR>\n");
}
$query->free_result();
$dbh->next_result();
$meta = $query->result_metadata();
$fields = $meta->fetch_fields();
var_dump($fields);
$query->store_result();
$query->bind_result($name, $cost, $text);
while($query->fetch()) {
print_r("<TR>");
print_r("<TD>" . $name . "</TD>");
print_r("</TR>\n");
}
$query->free_result();
$dbh->next_result();
}
else {
print_r("Query failed: " . $query->error . "<BR>\n");
exit(0);
}
$query->close();
$dbh->close();
}
The issue I'm running into is that it looks like I'm getting the same meta-data for the second result set, even though it is returning a completely different set of columns, which means that my second bind_result call results in the following error:
PHP Warning: mysqli_stmt::bind_result() [<a href='mysqli-stmt.bind-result'>mysqli-stmt.bind-result</a>]: Number of bind variables doesn't match number of fields in prepared statement
I've banged my head against this for a while and am just not clear on what I'm doing wrong...it almost seems like a mysqli bug. Does anyone have some example code to show how to do what I'm attempting?
Basically there are a few requirements to make this work properly...
MYSQL 5.5.3 or higher
PHP 5.3 for mysqlnd support
This is a compile time setting so if you are using shared hosting you probably cannot change this.
Basically in your example, use $query->next_result() instead of $dbh->next_result().
mysqli_stmt::next_result
I've only found one SO example of some else attempting to do this.
Related
I am trying to migrate to Mysqli and I got my Mysql code to search for parameters like this:
$querySt = "SELECT userID FROM myTable";
if (isset($_POST["UserID"])) {
if (ctype_digit($_POST["UserID"])) {
addWhereIfNoHave();
$in_userID = mysql_real_escape_string($_POST["UserID"]);
$querySt .= " UserID = '$in_userID'";
}
}
if (isset($_POST["name"])) {
addWhereIfNoHave();
$in_name = mysql_real_escape_string($_POST["name"]);
$querySt .= " imgName LIKE LOWER('%$in_name%')";
}
if (isset($_POST["ScoreLessThan"])) {
if (ctype_digit($_POST["ScoreLessThan"])) {
addWhereIfNoHave();
$in_ScoreLessThan = mysql_real_escape_string($_POST["ScoreLessThan"]);
$querySt .= " Score < '$in_ScoreLessThan'";
}
}
...
...
there are other if statements here looking for other post data, and
they keep on adding parameters into mysql query string just like above.
...
...
//this function is called in those if statements above. It either adds "WHERE" or "AND".
function addWhereIfNoHave(){
global $querySt;
if (strpos($querySt, 'WHERE') !== false){
$querySt .= " OR";
return true;
}else{
$querySt .= " WHERE";
return false;
}
}
This function works ok looking for all the parameters input from PHP post. However, I am migrating this to Mysqli, and I have a bit of trouble converting this code to Mysqli version. For example,
$stmt = $conn->prepare("SELECT userID FROM myTable WHERE UserID = ? AND name= ?");
$stmt->bind_param('ss', $userid, $name);
Suppose, I wanna search the table using 2 variables, I bind 2 variables like above, but in the case of my Mysql above, I keep on extending additional parameters into the string before executing the mysql query.
But for Mysqli, how can we do this? Is it possible to bind additional parameters and extending the string for prepare statement like Mysql code above? How should this problem be approach for Mysqli?
My current problem is mainly with the bind_param. I could concatenate the search query further and add all the '?' into the prepare statement, but with different variable types and number variables needed to be specified in bind_param, this is where I am stuck.
So, first off, I know there are certain rules you have to follow when preparing a LIKE statement with PDO. I have already looked these up and I'm trying my best to follow them, but the query consistently returns no results even though I know the query itself is legitimate (MySQL command line client works correctly with the query).
This is for a school project; I need to make a website with a MySQL/php backend for a fictional bookstore.
I have a class in a php script called DBConnection. It is in a separate namespace (hence the backslashes for PDO objects and functions). This is part of it:
<?php
class DBConnection {
// ...
public function prepAndExecute($sql, $args) {
try {
$stmt = $this->conn->prepare($sql);
for($i = 1; $i <= count($args); $i++) {
$stmt->bindValue($i, $args[$i-1], \PDO::PARAM_STR);
}
$stmt->execute();
return $stmt;
} catch(\PDOException $e) {
return false;
}
}
}
?>
The actual MySQL query I am trying to run:
SELECT ISBN, Title, Author, Price FROM Book WHERE Title LIKE "%rich%";
My attempt at using a PDO Prepared Statement to run this on the website:
<?php
// based on the search form from the previous page
// (all values are set correctly by the form, already tested)
$criteria = $_POST["searchCriteria"]; // "Title" (from a <select> element)
$term = $_POST["searchTerm"]; // "rich" (from the text box)
$conn = new DBConnection(); // uses namespace correctly, just didn't
// include here for simplicity
$sql = "SELECT ISBN, Title, Author, Price FROM Book WHERE ? LIKE ?";
$stmt = $conn->prepAndExecute($sql, array($criteria, "%" . $term . "%"));
// I have also tried $term = "%" . $term . "%", still no luck
echo $stmt->rowCount(); // 0
?>
I ran the above query in the MySQL command line, and got 1 result as expected. I know the class/functions work because I use that same function to run all other SELECT and INSERT queries, and have had no problems until I try to run this LIKE statement.
Am I doing something wrong? Because I double and triple checked everything and could have sworn I was doing this right.
http://php.net/manual/en/pdostatement.bindparam.php
$sth = $dbh->prepare('SELECT * FROM `users` WHERE `firstname` LIKE :keyword');
// Put the percentage sing on the keyword
$keyword = "%".$keyword."%";
// Bind the parameter
$sth->bindParam(':keyword', $keyword, PDO::PARAM_STR);
I'm trying to implement pagination using PHP. I found that calling exec to the connected database prevents the further query calls from working.
The piece of code at hand:
<?php
// Pagination logic
//Here we count the number of results
$query = "SELECT COUNT(*) as num FROM gig";
$total_pages = $db->exec($query);
$total_pages = $total_pages[num];
?>
After it if I try to use a query such as:
<?php>
foreach ($db->query("SELECT sname, start, venue FROM gig WHERE start = '0000-00-00 00:00:00'") as $a) {
$row="<tr><td>$a[sname]</td><td>To be announced</td><td>$a[venue]</td></tr>\n";
print $row;
}
?>
it returns
Warning: Invalid argument supplied for foreach()
As soon as the first code block is removed, the query works fine. When I check the value of $total_pages, it's 0, so something must be going wrong along the way. As far as I know, I use it in the same way as the query(which works on its own), so is there any reason why it doesn't work?
The PDO is initialized in the following way:
try {
$db = new PDO("mysql:dbname=$db_name;host=$db_server", $db_user, $db_pw);
} catch (PDOException $e) {
die('Connection failed: ' . $e->getMessage());
}
session_start();
From Manual
PDO::exec() does not return results from a SELECT statement. For a
SELECT statement that you only need to issue once during your program,
consider issuing PDO::query(). For a statement that you need to issue
multiple times, prepare a PDOStatement object with PDO::prepare() and
issue the statement with PDOStatement::execute().
Used a function of the STATEMENT object had after using querying to count the rows instead of exec:
$dbq = $db->query("SELECT * FROM gig");
$rows = $dbq->rowCount();
About the latter code block not working because of the exec failing - it seems to just be the way php queries work, if one fails, all fail. The foreach() error is for the object it's provided is not an array, for it failed.
i have written a basic stored procedure using mysql
DELIMITER //
CREATE PROCEDURE `sp_sel_test`()
BEGIN
SELECT * FROM category c;
END//
DELIMITER ;
now i m calling it from php
the php code is:
<?php
$txt = $_GET['id'];
$name = $_GET['name'];
$con = mysql_connect("localhost","four","password");
if (!$con)
{
die('Could not connect: ' . mysql_error());
}
mysql_select_db("fourthes_a", $con);
//$result = mysql_query("select * from new_c where name like %". $name ."% or c_name like %" . $name . "% order by name asc;");
$result = mysql_query("call sp_sel_test()");
if ($result === FALSE) {
die(mysql_error());
}
while($row = mysql_fetch_array($result))
{
echo $row['category_id'] . " " . $row['c_name'];
?>
<br />
<?php
}
mysql_close($con);
echo $txt;
?>
now its giving the error
PROCEDURE fourthes_a.sp_sel_test can't return a result set in the given context
mysql_query() returns false when the query fails. You didn't check if your sproc query succeeded, so most likely you're passing that boolean FALSE to the fetch function, which is rightfully complaining.
Rewrite your code like this, as a bare mininum, for proper error handling:
$res = mysql_query('call sp_sel_test()');
if ($res === FALSE) {
die(mysql_error());
}
Never ever assume a query succeeded. Even if the SQL syntax is perfect, there's far too many other reasons for a query to fail to NOT check if it worked.
You need to set client flags while connecting for using stored procedures with php. Use this:
mysql_connect($this->h,$this->u,$this->p,false,65536);
See MySQL Client Flags for more details. PHP MySQL does not allow you to run multiple statements in single query. To overcome this you must tell PHP to allow such queries by setting CLIENT_MULTI_STATEMENTS flag in your connection.
I know last answer was a year ago, but...
CREATE PROCEDURE sp_sel_test(OUT yourscalarvariable INT/TEXT...)
Statements that return a result set cannot be used within a stored function. This includes SELECT statements that do not use INTO to fetch column values into variables, SHOW statements, and other statements such as EXPLAIN. For statements that can be determined at function definition time to return a result set, a Not allowed to return a result set from a function error occurs (ER_SP_NO_RETSET_IN_FUNC). For statements that can be determined only at runtime to return a result set, a PROCEDURE %s can't return a result set in the given context error occurs (ER_SP_BADSELECT).
So, your select shoould be like this:
SELECT * FROM category **INTO** c;
http://www.cs.duke.edu/csl/docs/mysql-refman/stored-procedures.html
Got the following simple query which works fine through phpmyadmin but when I add it to my php website no results are returned and no error/warning messages either. If I remove "SET #N=-1;" then it works fine.
<?php
$db_connect = mysql_connect(DB_HOST, DB_USER, DB_PASSWORD, true);
mysql_select_db(DB_NAME, $db_connect);
$test_query = mysql_query("SET #N=-1;SELECT `id`, (#N:=#N+1) AS `mycount` FROM `mydb`;");
for ($i = 0; $i <= mysql_num_rows($test_query)-1; $i++) {
echo mysql_result($db_directorymap, $i, 0) . " " . mysql_result($db_directorymap, $i, 1) . "<br />";
}
?>
UPDATE: I just moved to mysqli but of course I'm still having a problem with the mysql statement and mysqli_multi_query. It seems when it runs the first part of the query the results returned are empty thus a boolean error is given. I'm guessing I have to skip the first set of results but I don't know how to do that?
It's because the mysql_query function will only accept one query, but you've given it two, separated by a semicolon. Try either:
Running each query separately (don't know if this will work):
mysql_query( "SET #N=-1" );
mysql_query( "SELECT `id`, (#N:=#N+1) AS `mycount` FROM `mydb`" );
Using mysqli with the multi_query function (or a PDO equivalent if there is one).
To answer your updated question: check the PHP manual page for multi_query. I think you'll want to use mysqli::next_result. Something like this, using procedural style:
mysqli_multi_query($link, $query);
mysqli_next_result($link);
if ($result = mysqli_store_result($link)) {
while ($row = mysqli_fetch_row($result)) {
printf("%s\n", $row[0]);
}
mysqli_free_result($result);
}
Multiple queries via mysql_query aren't supported, so I'd guess that it's only executing the SET command, and not the subsequent SELECT command.