SQL_CALC_FOUND_ROWS / FOUND_ROWS() does not work in PHP - php

I use SQL_CALC_FOUND_ROWS in Mysql SELECT statement, to get the number of lines my SELECT would return without a LIMIT clause.
$sql = new mysqli('localhost', 'root', '');
$sql->select_db('mysql');
$s1 = $sql->query('select SQL_CALC_FOUND_ROWS * from db limit 0, 3');
$s2 = $sql->query('select FOUND_ROWS()');
if($row = $s2->fetch_row()) printf('%d/%d', $s1->num_rows, $row[0]);
On my WinXP dev station it return 3/0 everytime for several weeks. When I use another MySQL server from my station it return 3/0 too.
On anothers PC the same code runs fine, and return the correct number (3/17 for example, if I have 17 records in mysql.db table). Every XP PC have the same PHP/Mysql version, and it ran fine in the past on my PC
Using Mysql Query Browser with the same SQL queries I get the right number.
Could anyone give me an idea of solution, without re-install all?
Sorry, my previous request was awfully unclear.

Thank you.
When I ran something analogous to your example on the mysql command line, it would work; but running it from php, it failed. The second query has to "know about" the first one, so I figure somehow that persistence/memory linking the two queries was getting messed up by the php.
(It turns out that Wordpress uses this type of query to do its pagination - so our larger problem was that the pagination in a wordpress install suddenly stopped working when we moved to php 5.2.6 ... eventually tracked it down to the FOUND_ROWS()).
Just for the sake of posting for people who may run into this in the future... for me it was the php setting "mysql.trace_mode" - this defaulted "on" in 5.2.6 instead of "off" like previously, and for some reason prevents the FOUND_ROWS() from working.
As a "fix", we could either put this in every php page (actually, in a common "include"):
ini_set("mysql.trace_mode", "0");
or add this to the .htaccess:
php_value mysql.trace_mode "0"
Thanks again,
Jerry

Are you using a MySQL query method that allows for multiple queries.
From MySQL documentation.
To obtain this row count, include a SQL_CALC_FOUND_ROWS option in the SELECT statement, and then invoke FOUND_ROWS() afterward
Example:
mysql> SELECT SQL_CALC_FOUND_ROWS * FROM tbl_name
-> WHERE id > 100 LIMIT 10;
mysql> SELECT FOUND_ROWS();
Also just for fun, there's a great discussion about the race condition of FOUND_ROWS()'s usage here.

Another way would be to use mysqli_multi_query as stated in the PHP manual by passing both queries containing SQL_CALC_FOUND_ROWS and FOUND_ROWS separated with a semicolon
<?php
$mysqli = new mysqli("localhost", "my_user", "my_password", "world");
/* check connection */
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
$query = "SELECT SQL_CALC_FOUND_ROWS * FROM db limit 0, 3;";
$query .= "SELECT FOUND_ROWS()";
/* execute multi query */
if ($mysqli->multi_query($query)) {
do {
/* store first result set */
if ($result = $mysqli->store_result()) {
while ($row = $result->fetch_row()) {
printf("%s\n", $row[0]);
}
$result->free();
}
/* print divider */
if ($mysqli->more_results()) {
printf("-----------------\n");
}
} while ($mysqli->next_result());
}
/* close connection */
$mysqli->close();
?>

The quickest solution is to subquery your actual query like this:
SELECT SQL_CALC_FOUND_ROWS * FROM (SELECT whatever FROM whatever WHERE whatever LIMIT whatever) ax;
select FOUND_ROWS();
Now you will get the correct results. I think the main reason being that SQL_CALC_FOUND_ROWS mainly tracks rows found (i.e. without LIMITS) not rows returned.

I had the same issue. The solution was stupid, I was using $wpdb->query instead of $wpdb->get_var. So you want to do
$wpdb->get_var('select FOUND_ROWS()');
if you're on WordPress

Well, it was a problem with mysql php extension bundled with php 5.2.6. Mysqli run fine, and another php version too.
Sorry for noise and unclear question.
If you have the same problem, my advice is to re-install PHP or change version.

Related

Is it possible to interrogate PDO to find out if queries are currently active? [duplicate]

My server runs CentOS 6.4 with MySQL 5.1.69 installed using yum with CentOS's repos, and PHP 5.4.16 installed using yum with ius's repos. Edit3 Upgraded to MySQL Server version: 5.5.31 Distributed by The IUS Community Project, and error still exists. Then changed library to mysqlnd, and seems to eliminate the error. Still, with this back and forth, need to know why this error only sometimes manifests.
When using PDO and creating the PDO object using PDO::ATTR_EMULATE_PREPARES=>false, I sometimes get the following error:
Table Name - zipcodes
Error in query:
SELECT id FROM cities WHERE name=? AND states_id=?
SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.
File Name: /var/www/initial_install/build_database.php
Line: 547
Time of Error: Tuesday July 2, 2013, 5:52:48 PDT
Line 547 is the last line of:
$stmt_check_county->execute(array($data[5],$data[4]));
if(!$county_id=$stmt_check_county->fetchColumn())
{
$stmt_counties->execute(array($data[5]));
$county_id=db::db()->lastInsertId();
}
//$stmt_check_county->closeCursor(); //This will fix the error
$stmt_check_city->execute(array($data[3],$data[4]));
I had a similar problem several years ago, but upgraded from PHP 5.1 to PHP 5.3 (and MySQL probably was updated as well), and the problem magically went away, and now I have it with PHP 5.5.
Why does it only manifest itself when PDO::ATTR_EMULATE_PREPARES=>false, and with only alternating version of PHPs?
I've also found that closeCursor() will also fix the error. Should this always be done after every SELECT query where fetchAll() is not used? Note that the error still occurs even if the query is something like SELECT COUNT(col2) which only returns one value.
Edit By the way, this is how I create my connection. I've only recently added MYSQL_ATTR_USE_BUFFERED_QUERY=>true, however, it doesn't cure the error. Also, the following script could be used as is to create the error.
function sql_error($e,$sql=NULL){return('<h1>Error in query:</h1><p>'.$sql.'</p><p>'.$e->getMessage().'</p><p>File Name: '.$e->getFile().' Line: '.$e->getLine().'</p>');}
class db {
private static $instance = NULL;
private function __construct() {} //Make private
private function __clone(){} //Make private
public static function db() //Get instance of DB
{
if (!self::$instance)
{
//try{self::$instance = new PDO("mysql:host=localhost;dbname=myDB;charset=utf8",'myUsername','myPassword',array(PDO::ATTR_EMULATE_PREPARES=>false,PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC));}
try{self::$instance = new PDO("mysql:host=localhost;dbname=myDB;charset=utf8",'myUsername','myPassword',array(PDO::ATTR_EMULATE_PREPARES=>false,PDO::MYSQL_ATTR_USE_BUFFERED_QUERY=>true,PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC));}
//try{self::$instance = new PDO("mysql:host=localhost;dbname=myDB;charset=utf8",'myUsername','myPassword',array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC));}
catch(PDOException $e){echo(sql_error($e));}
}
return self::$instance;
}
}
$row=array(
'zipcodes_id'=>'55555',
'cities_id'=>123
);
$data=array($row,$row,$row,$row);
$sql = 'CREATE TEMPORARY TABLE temp1(temp_id INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY (temp_id) )';
db::db()->exec($sql);
$sql='SELECT COUNT(*) AS valid FROM cities_has_zipcodes WHERE cities_id=? AND zipcodes_id=?';
$stmt1 = db::db()->prepare($sql);
$sql ='SELECT temp_id FROM temp1';
$stmt2 = db::db()->prepare($sql);
foreach($data AS $row)
{
try
{
$stmt1->execute(array($row['zipcodes_id'],$row['cities_id']));
$rs1 = $stmt1->fetch(PDO::FETCH_ASSOC);
//$stmt1->closeCursor();
syslog(LOG_INFO,'$rs1: '.print_r($rs1,1).' '.rand());
$stmt2->execute();
$rs2 = $stmt2->fetch(PDO::FETCH_ASSOC);
syslog(LOG_INFO,'$rs2: '.print_r($rs2,1).' '.rand());
}
catch(PDOException $e){echo(sql_error($e));}
}
echo('done');
The MySQL client protocol doesn't allow more than one query to be "in progress." That is, you've executed a query and you've fetched some of the results, but not all -- then you try to execute a second query. If the first query still has rows to return, the second query gets an error.
Client libraries get around this by fetching all the rows of the first query implicitly upon first fetch, and then subsequent fetches simply iterate over the internally cached results. This gives them the opportunity to close the cursor (as far as the MySQL server is concerned). This is the "buffered query." This works the same as using fetchAll(), in that both cases must allocate enough memory in the PHP client to hold the full result set.
The difference is that a buffered query holds the result in the MySQL client library, so PHP can't access the rows until you fetch() each row sequentially. Whereas fetchAll() immediately populates a PHP array for all the results, allowing you access any random row.
The chief reason not to use fetchAll() is that a result might be too large to fit in your PHP memory_limit. But it appears your query results have just one row anyway, so that shouldn't be a problem.
You can closeCursor() to "abandon" a result before you've fetched the last row. The MySQL server gets notified that it can discard that result on the server side, and then you can execute another query. You shouldn't closeCursor() until you're done fetching a given result set.
Also: I notice you're executing your $stmt2 over and over inside the loop, but it will return the same result each time. On the principle of moving loop-invariant code out of the loop, you should have executed this once before starting the loop, and saved the result in a PHP variable. So regardless of using buffered queries or fetchAll(), there's no need for you to nest your queries.
So I would recommend writing your code this way:
$sql ='SELECT temp_id FROM temp1';
$stmt2 = db::db()->prepare($sql);
$stmt2->execute();
$rs2 = $stmt2->fetchAll(PDO::FETCH_ASSOC);
$stmt2->closeCursor();
$sql='SELECT COUNT(*) AS valid FROM cities_has_zipcodes
WHERE cities_id=:cities_id AND zipcodes_id=:zipcodes_id';
$stmt1 = db::db()->prepare($sql);
foreach($data AS $row)
{
try
{
$stmt1->execute($row);
$rs1 = $stmt1->fetchAll(PDO::FETCH_ASSOC);
$stmt1->closeCursor();
syslog(LOG_INFO,'$rs1: '.print_r($rs1[0],1).' '.rand());
syslog(LOG_INFO,'$rs2: '.print_r($rs2[0],1).' '.rand());
}
catch(PDOException $e){echo(sql_error($e));}
}
Note I also used named parameters instead of positional parameters, which makes it simpler to pass $row as the array of parameter values. If the keys of the array match the parameter names, you can just pass the array. In older versions of PHP you had to include the : prefix in the array keys, but you don't need that anymore.
You should use mysqlnd anyway. It has more features, it's more memory-efficient, and its license is compatible with PHP.
I am hoping for a better answer than the following. While some of these solutions might "fix" the problem, they don't answer the original question regarding what causes this error.
Set PDO::ATTR_EMULATE_PREPARES=>true (I don't wish to do this)
Set PDO::MYSQL_ATTR_USE_BUFFERED_QUERY (didn't work for me)
Use PDOStatement::fetchAll() (not always desirable)
Use $stmt->closeCursor() after each $stmt->fetch() (this mostly worked, however, I still had several cases where it didn't)
Change PHP MySQL library from php-mysql to php-mysqlnd (probably what I will do if no better answer)
I have almost same problem. My first query after connection to db return empty result and drop this error. Enabling buffer doesn't help.
My connection code was:
try {
$DBH = new PDO("mysql:host=$hostname;dbname=$db_name", $username, $password,
array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET CHARACTER SET utf8; SET NAMES utf8",
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_NUM));
}
catch(PDOException $e) { echo $e->getMessage(); }
Solution in my way was to remove initial command:
PDO::MYSQL_ATTR_INIT_COMMAND => "SET CHARACTER SET utf8; SET NAMES utf8"
Here is a correct code:
try {
$DBH = new PDO("mysql:host=$hostname;dbname=$db_name", $username, $password,
array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_NUM));
}
catch(PDOException $e) { echo $e->getMessage(); }
And MYSQL_ATTR_USE_BUFFERED_QUERY is not forced to true. It's set as default.
I also experienced this problem today and noticed that I put wrong SQL statement (SELECT) into PDO's exec() method. Then I came to a conclusion that we can only put write (INSERT, UPDATE, DELETE) SQL statements instead of read (SELECT) ones to the method.
!!!
WARNING !!!
This can also happen if you are trying to fetch a non SELECT query (Eg - UPDATE/INSERT/ALTER/CREATE)
if anybody is here, with error while creating tables
this also happens if you try to execute create 2 tables in single query;
this error was thrown when i fired below query;
$q24="create table if not exists table1 like template1;create table if not exists table2 like template2;";
$s24=$link->prepare($q24);
$s24->execute();
seems tables are to be created separately;
$q1="create table if not exists table1 like template1;";
$s1=$link->prepare($q1);
$s1->execute();
//and
$q2="create table if not exists table2 like template2;";
$s2=$link->prepare($q2);
$s2->execute();
I had the same problem, I was sending results to another function mid loop. Quick fix was, save all results in an array (like Bill stated, if it's too large you have other issues to worry about), after collecting the data, I ran a separate loop to call the function one at a time.
Also, PDO::MYSQL_ATTR_USE_BUFFERED_QUERY did not work for me.
I had the same problem and solved it by removing all initial requests related to the character set.
so I started from
$con = new \PDO(self::getDriver() . ":host=" . self::getHost() . ":".self::getPort()."; dbname=" . self::getName() . ";charset=utf8", self::getUser(), self::getPassword(), array( \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,\PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8;SET SESSION time_zone ='+01:00'"));
to
$con = new \PDO(self::getDriver() . ":host=" . self::getHost() . ":".self::getPort()."; dbname=" . self::getName() . ";charset=utf8", self::getUser(), self::getPassword(), array( \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,\PDO::MYSQL_ATTR_INIT_COMMAND => "SET SESSION time_zone ='+01:00'"));
so I removed the command SET NAMES utf8;
I got this error when I accidentally called execute twice, one explicitly and one that was hidden in another call (PDO class).
After removing the first execute, the error was gone.
The main reason behind this error is that MySQL is trying to run 'exec' instead of 'execute' and vice versa.
There are two PDO statements that are there to execute queries PDO::exec() and PDO::execute(), both are not the same.
PDO::exec() is designed to execute commands and queries that do not produce a result set.
Ex: SET, UPDATE, INSERT, DELETE etc.
PDO::execute() is designed to execute commands and queries that produce a result set.
Ex: SELECT, CALL, SHOW, OPTIMIZE, EXPLAIN etc.
If you use these commands in wrong place, your will be ended up with this error.
Solution:
Be careful where to use PDO::exec() and PDO::execute()
In my case for Laravel I changed my query from
DB::select("DELETE FROM " . env('DB_PREFIX') . 'products WHERE
product_id = ' . $product->id); // Internally Laravel will run PDO::execute()
'select' method
to
DB::table('product_currency')->where('product_id',
$product->id)->delete(); // Internally Laravel will run PDO::exec()
Hope this gives some more clarification!

PHP connects but cannot query MYSQL database

I am querying a mysql database with php but cannot get it to work on my iMac. In particular, php is unable to connect to the mysql DB. It connects to mysql and selects the DB but then fails. See code below:
if (!mysql_connect($db_host, $db_user, $db_pwd)){
die("I cannot connect to database");
}
if (!mysql_select_db($database)){
die("I cannot select database");
}
$sql = "SELECT FROM ${table} ORDER BY $sql_orderBy";
$result = mysql_query($sql);
if (!$result) {
die("I cannot execute query to show fields from Table: {$table}. Query failed.");
}
For reference, I installed apache/mysql/php with macports. The same php code works on my laptop (same installations), and the query works when I invoke it from within mysql on both computers. All variables are declared. Something with the system config is my best guess, but I even went through a uninstall/install.
Any help would be appreciated!
Your issue is ${table} . This should be {$table} or better still, ".$table."
You also need to say what you are SELECTING:
So:
$sql = "SELECT * FROM ".$table." ORDER BY ".$sql_orderBy;
You can discover issues by using Mysql_error() at the end of the query, for example:
mysql_query($sqlString) or die("line: ".__LINE__.":".mysql_error());
this will output a clear error message regarding your SQL statement. This is not for production and public situations but for development.
Also:
MySQL is deprecated and is no longer supported by PHP or the wider community, it is VERY strongly recommended you take up MySQLi or PDO and use these methods as they are much stronger, less flawed and more efficient delivery of results. They will also be supported in future updates and developments whereas MySQL will not.

SQL query returns empty result

I have this SQL query:
SET #date_shift = DATE_SUB(NOW(), Interval 60 MINUTE);
SELECT hinfo.idx,
hinfo.host_idx,
hinfo.processor_load,
hinfo.memory_total,
hinfo.memory_free,
hnames.idx,
hnames.name,
disks.hostinfo_idx,
disks.id,
disks.name,
disks.size,
disks.freespace,
hinfo.probetime
FROM systeminfo.hostinfo AS hinfo
INNER JOIN systeminfo.hosts AS hnames
ON hnames.idx = hinfo.host_idx
INNER JOIN systeminfo.disksinfo AS disks
ON disks.hostinfo_idx = hinfo.idx
WHERE hinfo.probetime > #date_shift AND
hinfo.probetime = (SELECT MAX(probetime) FROM systeminfo.hostinfo AS hi
WHERE hi.probetime > #date_shift AND hi.host_idx = hinfo.host_idx)
GROUP BY disks.id,
hnames.name
ORDER BY hnames.name,
disks.id;
and i try to execute it in php code. I tried mysql, mysqli and pdo. Ma last code is following:
$db = new PDO("mysql:host=".$this->ip.";dbname=".$this->dbname.";charset=utf8", $this->username, $this->password);
$result = $db->query($query);
$fetch_data = $result->fetchAll(PDO::FETCH_ASSOC);
or
mysql_connect($this->ip, $this->username, $this->password);
mysql_query('SET NAMES utf8;');
mysql_select_db($this->dbname);
$result = mysql_query($query);
PHP connects correcly to the database. Simple queries, like select * from tablename works. But this query returns NO data but error:
1064 : You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near 'SELECT hinfo.idx, hinfo.host_idx, hinfo.processor_load,
hinfo.memory_total, ' at line 2
The same query, executed in the database client works perfectly (it's not a slow query, it takes less than 10 ms to perform it).
Moreover, when i try to print the query directly from this function on a web page and paste it into mysql client - it works perfectly as well! I can't find any special character in hex viewer i tried to force utf-encoding, nothing.
Any ideas? Test i could perform? Thanks in advance!
Try to execute this as two seperate statements. Running queries from GUI tools or command line tools differ from embedding queries in code. So it works in database tool, but not in php code.
The mysql adapter for PHP can only handle one query at a time. You should indeed run the queries separate from each other.
"#date_shift" will be available in the second query, assuming your connection to mysql does not get destroyed between the two queries.

php multiple databases issue

I've set two database conections as below
$con1 = mysql_connect("localhost", "root", "pwd") or die (mysql_error());
$con2 = mysql_connect("localhost", "wordpress", "pwd", true) or die(mysql_error());
mysql_select_db("lab_ancp", $con1) or die(mysql_error());
mysql_select_db("wordpress",$con2) or die(mysql_error());
and it works fine
so then i do some queries on a page like this:
$sql="select unome from associado where uid=$uid";
$result=mysql_query($sql,$con1) or die(mysql_error());
and it works fine, after that i do a second query like this:
$sql="select ID, post_content, post_title, post_excerpt, meta_value
from wp_posts join (
select post_id, meta_value
from wp_postmeta
join (
select post_id from wp_postmeta
where meta_key='destaque' and meta_value='s'
)as t1 using(post_id)
where meta_key='pft_widescreen'
) as t2 on (wp_posts.ID=t2.post_id)
ORDER BY RAND() LIMIT 1";
//echo $sql . "<br />";
$row=mysql_fetch_assoc(mysql_query($sql,$con2)) or die(mysql_error());
and again everything is just fine, but then....
$sql="select * from eventos where edatade>='$hoje' or edataate>='$hoje'";
$result=mysql_query($sql, $con1) or die (mysql_error());
gives this error:
**
SELECT command denied to user
'wordpress'#'localhost' for table
'eventos'
**
From the error it seems you should verify permissions for the wordpress user on the eventos table. Your code seems to be correct.
If you want to verify this, maybe try a "SELECT * from eventos" using the second connection. Do this as the first query in the script.
Well
Its solved.
Don't askme the reason but i've tried to change the order in the first two roww, i.e put $con2 before $con1 and the queries now simply work fine.
I suspect that the ..."true" parameter has something to do with that.
Thx guys.
http://se2.php.net/manual/en/function.mysql-select-db.php#39095
http://se2.php.net/manual/en/function.mysql-select-db.php#93487
Seems to be a problem with the mysql_select_db, the second link is one solution.
I would recommend using phps mysqli (MySQL Improved Extension) instead of old mysql stuff (don't know if it solves your problem, but it solves other problems you might walk in to).
I suspect that the reason is that the connection is still in memory so when you try to execute the second query, this use the last connection. I have the similar problem with Joomla two databases use issue. I am looking for the fix for this problem, I think that is a missing rule in our code.
HEY!!! I get it, the problem is that when you create the second connection, for default PHP return the same reference for the last one. So if you need a new connection you should prepare a mysql_connect with $new_link to true, like this
$myconn = #mysql_connect($this->db2_host,$this->db2_user,$this->db2_pass, true);
The last parameter means that you need a new reference and not the last one. You can find more information:
HERE and HERE
Hope it helps.

PHP's PDO Prepare Method Fails While in a Loop

Given the following code:
// Connect to MySQL up here
$example_query = $database->prepare('SELECT * FROM table2');
if ($example_query === false) die('prepare failed');
$query = $database->prepare('SELECT * FROM table1');
$query->execute();
while ($results = $query->fetch())
{
$example_query = $database->prepare('SELECT * FROM table2');
if ($example_query === false) die('prepare failed'); //it will die here
}
I obviously attempt to prepare statements to SELET everything from table2 twice. The second one (the one in the WHILE loop) always fails and causes an error.
This is only on my production server, developing locally I have no issues so it must be some kind of setting somewhere.
My immediate thought is that MySQL has some kind of max_connections setting that is set to 1 and a connection is kept open until the WHILE loop is completed so when I try to prepare a new query it says "nope too many connected already" and shits out.
Any ideas?
EDIT: Yes I know there's no need to do it twice, in my actual code it only gets prepared in the WHILE loop, but like I said that fails on my production server so after some testing I discovered that a simple SELECT * query fails in the WHILE loop but not out of it. The example code I gave is obviously very watered down to just illustrate the issue.
Problem was that I couldn't do something while an unbuffered query was running (which it was in the WHILE loop)
Solution was to add the following line to ensure buffered queries were being used:
$database->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY,true);
There shouldn't be a need to prepare $example_query more than once. Simply execute the query inside your loop.
EDIT: If you must prepare a new query in each loop iteration, an explicit $example_query->closeCursor() at the end of the loop should free any resources associated with the statement object.

Categories