PDO equivalent of mysql_num_rows or mssql_num_rows [duplicate] - php

This question already has answers here:
Row count with PDO
(21 answers)
Closed 2 years ago.
I know this question has been asked before but it seems like the solutions have been specific to the problem presented.
I have a codebase with hundreds of instances where mssql_num_rows is used.
Code example:
$db->execute($sql);
if ($db->getRowsAffected() > 0) {
$total = $db->fetch();
In db class:
$this->rowsaffected = mssql_num_rows($this->query_result);
I can't create generic SELECT count(*) FROM table queries as I have too many specific select statements.
I could run a preg_replace to remove everything between SELECT and FROM and then replace with a COUNT(*) and run a second query but this assumes all queries are setup a certain way.
I could fetchAll first then count() the results but that means upgrading all instances of the if statements.
So what is the best all around replacement to a *_num_rows function if people are updating their code to PDO. Not something that solves a specific problem, but something that replaces the functionality of *_num_rows. If that's not possible what allowed it to be possible before?

If you want to count the rows you can do this with PDO:
$sql = 'select * from users';
$data = $conn->query($sql);
$rows = $data->fetchAll();
$num_rows = count($rows);
There is no way to directly count rows when using a SELECT statement with PDO as stated in the docs.
PDOStatement::rowCount() returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement executed by the corresponding PDOStatement object.
Only do a row count if you absolutely need the count, otherwise you can verify that the query worked with other methods. You should also not use this method if you expect to be returning thousands of rows from a table, instead, use the COUNT() function in a query for just performing the count.

So with everyone's help this is what I built.
function getRowsAffected() {
$rawStatement = explode(" ", $this->query);
$statement = strtoupper($rawStatement[0]);
if ($statement == 'SELECT' || $statement == 'SHOW') {
$countQuery = preg_replace('/(SELECT|SHOW)(.*)FROM/i', "SELECT count(*) FROM", $this->query);
$countsth = $this->pdo->prepare($countQuery);
if ($countsth->execute()) {
$this->rowsaffected = $countsth->fetchColumn();
} else {
$this->rowsaffected = 0;
}
}
return $this->rowsaffected;
}
$this->rowsaffected is already being updated in the execute phase if the statement is an INSERT, UPDATE, or DELETE with $sth->rowCount() so I only needed to run this second query on SELECT and SHOWS.
if ($statement == 'INSERT' || $statement == 'UPDATE' || $statement == 'DELETE') {
$this->rowsaffected = $this->sth->rowCount();
}
I feel bad though, because just as I mentioned in my question, that I was looking for an overall solution I seem to have stumbled onto a specific solution that works for me since the code already asks for the number of rows using a function. If the code was doing this:
if (mysql_num_rows($result) > 0) {
then this solution would still create work updating all instances to use that custom function.

Related

(PHP) How do I return regular MySQL result resources when using prepared statements?

This is a bit of a clueless-beginner type of question, so I apologise. But I've not managed to find a website or video that helps me understand working with MySQL prepared statements, so I'm hoping for some direct advice.
For the last couple of weeks of my learning, I've been using procedural MySQLi calls to make database queries, and working through them with while loops. Things like
$query = mysqli_query("SELECT * FROM `Shoes` WHERE `Color` = 'Red'");
while ($row = mysqli_fetch_assoc($query)) {
echo $row['size'];
}
Things like that. It makes sense to me that I would be execute a query, get returned a special results resource, and then use a 'fetch_something' function to turn it into an array I can pick from and loop over, regardless of its size.
Now I'm trying to learn about prepared statements. What's tripping me up is the 'bind_result' function that seems to get used all the time, at least in every book and tutorial I've consulted so far. It tells me to provide one variable per column, to which the result for that column will be bound. Like
$db = new mysqli(server,user,pass,database);
$stmt = $db->prepare("select `Temperature` from `BuildingDetails` where `HouseNumber` = ?");
$stmt->bind_param("i",$_GET['num']);
$stmt->execute();
$stmt->bind_result($x);
$stmt->fetch();
echo "The temperature in this house is {$x}.";
Simple enough when I'm retrieving a single value, or a single row. But what if I want to loop over a table with 20 columns and 5,000 rows? Is there a way I can just return a regular old MySQL result resource and use fetch_assoc or something on it?
Is there a way I can just return a regular old MySQL result resource and use fetch_assoc or something on it?
You can use mysqli_stmt_get_result
Procedural
$result = mysqli_stmt_get_result($stmt);
while ($row = mysqli_fetch_assoc($result))
{
print_r($row);
}
OOP
$result = $stmt->get_result();
while ($row = $result->fetch_assoc())
{
print_r($row);
}
This way you do not have to bind all the variables to get results.

checking if SQL query was excuted in PDO [duplicate]

This question already has answers here:
How do I check db query returned results using PHP's PDO
(3 answers)
Closed 9 years ago.
in mysql_query we can check if the query was executed or not by doing this:
$query = $yourdbconnection->fetch_array(mysql_query("SELECT * FROM tbl_name"));
if ($query){ // query is working }
else { // query is not working }
in PDO, I am doing something like this:
$query = $yourdbconnection->query("SELECT * FROM tbl_name");
$fetchquery = $query->fetchAll();
if ($fetchquery) { // query is working}
else { // query not working}
Is my code effective? what exactly the if statement doing? Is it doing the same thing that mysql_query was doing? How can I check if the query is returning 0 rows or not?
[EDIT]
I have found those solutions as a workaround to the problem
using $stmt->fetch()
prepare($sql);
$stmt->execute();
if ($data = $stmt->fetch()) {
do {
echo $data['model'] . '<br>';
} while ($data = $stmt->fetch());
} else {
echo 'Empty Query';}
?>
adding another query to count the number of rows see this answer
However, I am still looking for better solutions
To see if your query was executed, I would suggest setting your PDO in exception mode like Your Common Sense suggested. You can do it like this:
$dbh = new PDO('mysql:host=localhost;dbname=db', 'user', 'pass');
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Concerning the best way to check if a query returned values or not in PDO, I suggest just doing a SELECT COUNT(*) FROM table query, like this:
<?php
$query = $dbh->query('SELECT COUNT(*) FROM table');
if ($query->fetchColumn() != 0) {
/* Query has result(s) */
$query = $dbh->query('SELECT * FROM table');
/* ... */
} else {
/* Query has no results */
}
?>
Let me know if you have any other questions.
there are 2 questions in your post.
1) what is the best way to check if the query returned values or not in PDO?
check returned values
2) how to check if SQL query was excuted in PDO
set PDO in exception mode as described in the tag wiki
By the way, for the "SELECT * FROM tbl_name" query you may wish to use fetchAll() instead of fetch().

How to get the total number of rows of a GROUP BY query?

From the PDO manual:
PDOStatement::rowCount() returns the
number of rows affected by the last
DELETE, INSERT, or UPDATE statement
executed by the corresponding
PDOStatement object.
If the last SQL statement executed by
the associated PDOStatement was a
SELECT statement, some databases may
return the number of rows returned by
that statement. However, this
behaviour is not guaranteed for all
databases and should not be relied on
for portable applications.
I found that out only very recently. I had just changed my db abstraction layer to not use SELECT COUNT(1) ... anymore, because just quering the actual rows and then counting the result would be much more efficient. And now PDO doesn't support that!?
I don't use PDO for MySQL and PgSQL, but I do for SQLite. Is there a way (without completely changing the dbal back) to count rows like this in PDO? In MySQL, this would be something like this:
$q = $db->query('SELECT a, b, c FROM tbl WHERE oele = 2 GROUP BY boele');
$rows = $q->num_rows;
// and now use $q to get actual data
With the MySQLi and PgSQL drivers, this is possible. With all PDO it isn't!?
PS. My initial solution was to extend the SQLResult->count method (my own) to replace SELECT ... FROM by SELECT COUNT(1) FROM and just return that number (very inefficient, but only for SQLite PDO). That's not good enough though, because in the example query above is a GROUP BY, which would change the meaning/function of the COUNT(1).
Here is the solution for you
$sql="SELECT count(*) FROM [tablename] WHERE key == ? ";
$sth = $this->db->prepare($sql);
$sth->execute(array($key));
$rows = $sth->fetch(PDO::FETCH_NUM);
echo $rows[0];
It's a little memory-inefficient but if you're using the data anyway, I use this frequently:
$rows = $q->fetchAll();
$num_rows = count($rows);
The method I ended up using is very simple:
$query = 'SELECT a, b, c FROM tbl WHERE oele = 2 GROUP BY boele';
$nrows = $db->query("SELECT COUNT(1) FROM ($query) x")->fetchColumn();
Might not be the most efficient, but it seems to be foolproof, because it actually counts the original query's results.
I don't use PDO for MySQL and PgSQL, but I do for SQLite. Is there a way (without completely changing the dbal back) to count rows like this in PDO?
Accordingly to this comment, the SQLite issue was introduced by an API change in 3.x.
That said, you might want to inspect how PDO actually implements the functionality before using it.
I'm not familiar with its internals but I'd be suspicious at the idea that PDO parses your SQL (since an SQL syntax error would appear in the DB's logs) let alone tries to make the slightest sense of it in order to count rows using an optimal strategy.
Assuming it doesn't indeed, realistic strategies for it to return a count of all applicable rows in a select statement include string-manipulating the limit clause out of your SQL statement, and either of:
Running a select count() on it as a subquery (thus avoiding the issue you described in your PS);
Opening a cursor, running fetch all and counting the rows; or
Having opened such a cursor in the first place, and similarly counting the remaining rows.
A much better way to count, however, would be to execute the fully optimized query that will do so. More often than not, this means rewriting meaningful chunks of the initial query you're trying to paginate -- stripping unneeded fields and order by operations, etc.
Lastly, if your data sets are large enough that counts any kind of lag, you might also want to investigate returning the estimate derived from the statistics instead, and/or periodically caching the result in Memcache. At some point, having precisely correct counts is no longer useful...
Keep in mind that a PDOStatement is Traversable. Given a query:
$query = $dbh->query('
SELECT
*
FROM
test
');
It can be iterated over:
$it = new IteratorIterator($query);
echo '<p>', iterator_count($it), ' items</p>';
// Have to run the query again unfortunately
$query->execute();
foreach ($query as $row) {
echo '<p>', $row['title'], '</p>';
}
Or you can do something like this:
$it = new IteratorIterator($query);
$it->rewind();
if ($it->valid()) {
do {
$row = $it->current();
echo '<p>', $row['title'], '</p>';
$it->next();
} while ($it->valid());
} else {
echo '<p>No results</p>';
}
If you're willing to give up a hint of abstraction, then you could use a custom wrapper class which simply passes everything through to the PDO. Say, something like this: (Warning, code untested)
class SQLitePDOWrapper
{
private $pdo;
public function __construct( $dns, $uname = null, $pwd = null, $opts = null )
{
$this->pdo = new PDO( $dns, $unam, $pwd, $opts );
}
public function __call( $nm, $args )
{
$ret = call_user_func_array( array( $this->pdo, $nm ), $args );
if( $ret instanceof PDOStatement )
{
return new StatementWrapper( $this, $ret, $args[ 0 ] );
// I'm pretty sure args[ 0 ] will always be your query,
// even when binding
}
return $ret;
}
}
class StatementWrapper
{
private $pdo; private $stat; private $query;
public function __construct( PDO $pdo, PDOStatement $stat, $query )
{
$this->pdo = $pdo;
$this->stat = $stat;
this->query = $query;
}
public function rowCount()
{
if( strtolower( substr( $this->query, 0, 6 ) ) == 'select' )
{
// replace the select columns with a simple 'count(*)
$res = $this->pdo->query(
'SELECT COUNT(*)' .
substr( $this->query,
strpos( strtolower( $this->query ), 'from' ) )
)->fetch( PDO::FETCH_NUM );
return $res[ 0 ];
}
return $this->stat->rowCount();
}
public function __call( $nm, $args )
{
return call_user_func_array( array( $this->stat, $nm ), $args );
}
}
Maybe this will do the trick for you?
$FoundRows = $DataObject->query('SELECT FOUND_ROWS() AS Count')->fetchColumn();
You have to use rowCount — Returns the number of rows affected by the last SQL statement
$query = $dbh->prepare("SELECT * FROM table_name");
$query->execute();
$count =$query->rowCount();
echo $count;
What about putting the query results in an array, where you can do a count($array) and use the query resulting rows after? Example:
$sc='SELECT * FROM comments';
$res=array();
foreach($db->query($sc) as $row){
$res[]=$row;
}
echo "num rows: ".count($res);
echo "Select output:";
foreach($res as $row){ echo $row['comment'];}
That's yet another question, which, being wrongly put, spawns A LOT of terrible solutions, all making things awfully complicated to solve a non-existent problem.
The extremely simple and obvious rule for any database interaction is
Always select the only data you need.
From this point of view, the question is wrong and the accepted answer is right. But other proposed solutions are just terrible.
The question is "how to get the count wrong way". One should never answer it straightforward, but instead, the only proper answer is "One should never select the rows to count them. Instead, ALWAYS ask the database to count the rows for you." This rule is so obvious, that it's just improbable to see so many tries to break it.
After learning this rule, we would see that this is an SQL question, not even PDO related. And, were it asked properly, from SQL perspective, the answer would appeared in an instant - DISTINCT.
$num = $db->query('SELECT count(distinct boele) FROM tbl WHERE oele = 2')->fetchColumn();
is the right answer to this particular question.
The opening poster's own solution is also acceptable from the perspective of the aforementioned rule, but would be less efficient in general terms.
There are two ways you can count the number of rows.
$query = "SELECT count(*) as total from table1";
$prepare = $link->prepare($query);
$prepare->execute();
$row = $prepare->fetch(PDO::FETCH_ASSOC);
echo $row['total']; // This will return you a number of rows.
Or second way is
$query = "SELECT field1, field2 from table1";
$prepare = $link->prepare($query);
$prepare->execute();
$row = $prepare->fetch(PDO::FETCH_NUM);
echo $rows[0]; // This will return you a number of rows as well.

PDO mySql query not executing in for loop the second time up while calling

I have an issue, I'm looping threw a set of values and then creating a PDO mySql query with every loop, now the problem is the first query is executing and returning results, but the second upwards aren't returning results. If I manually execute the queries on the server they return results. This is weird, maybe I'm doing something wrong here. My code below
if($num_results > 0){
for($i=0;$i<$num_results;$i++){
$sql_sub = "SELECT * FROM menu_config WHERE client_id =".$client_id ." AND id =".$data[$i]['root_menu_id'];
$results_s = $pdo->query($sql_sub);
$data_s = $results_s->fetchAll(PDO::FETCH_ASSOC);
$sub_menu_title = "<strong>".$data[$i]['title']."</strong>";
if(empty($data_s[0]['title'])){
$main_menu_title = '<span style="color:#FF0000;font-weight:bold;">No Main Menu Assigned to Sub Menu</span>';
}else{
$main_menu_title = $data_s[0]['title'];
}
$men_title = $data[$i]['title']
}
}
(this may be a little more than you asked for)
You seem to be missing out on some good things that prepared statements do.
First off, you don't usually want to pass the values directly into the query. (sometime's it's necessary, but not here). By doing that, you take out all the good stuff that protects from sql injection. Instead you want to send them as parameters after you've prepared the query.
Secondly, when in a loop, you can save yourself time and resources if you're running the same query over and over by preparing the statement, and then only changing the values you send to to that prepared statement using the PDOStatement::bindParam() function.
Thirdly, fetchAll() does not take a 'fetch_style' of PDO::FETCH_ASSOC. fetch() does. But I think you can get by with the default or none using fetchAll. You'll have to check into that and see what you need. Here are the fetchAll docs
$sql_sub = "SELECT * FROM menu_config WHERE client_id = :client_id AND id = :id ";
$query = $pdo->prepare($sql_sub);
for($i=0;$i<$num_results;$i++){
$query->bindParam(':client_id', $client_id);
$query->bindParam(':id', $data[$i]['root_menu_id']);
$query->execute();
$data_s = $query->fetchAll();
$sub_menu_title = "<strong>".$data[$i]['title']."</strong>";
if(empty($data_s[0]['title'])){
$main_menu_title = '<span style="color:#FF0000;font-weight:bold;">
No Main Menu Assigned to Sub Menu</span>';
}else{
$main_menu_title = $data_s[0]['title'];
}
$men_title = $data[$i]['title'];
}

Why doesn't this prepare statement work in MYSQLI?

I created this code:
$statement = $db->prepare("SELECT * FROM phptech_contact");
$statement->execute();
$result = $statement->result_metadata();
$object = $result->fetch_object();
print_r( $object );
When I run it, it doesn't work. Can anybody tell me why it doesn't work?
I have 20 rows in this table so data should be returned.
From http://ch.php.net/manual/en/mysqli-stmt.result-metadata.php
Note: The result set returned by mysqli_stmt_result_metadata() contains only metadata. It does not contain any row results. The rows are obtained by using the statement handle with mysqli_stmt_fetch().
As long as you don't need this meta data you don't need to call this method.
$statement = $db->prepare("SELECT fld1, fld2 FROM phptech_contact");
$statement->execute();
$stmt->bind_result($fld1, $fld2);
while ($stmt->fetch()) {
echo "$fld1 and $fld2<br />";
}
But I really dislike the mysqli extension. PDO is much cooler ... ;-)
$db = new PDO('...');
$stmt = $db->prepare("SELECT fld1, fld2 FROM phptech_contact");
$stmt->execute();
while ($obj = $stmt->fetchObject()) {
// ...
}
or
$objs = stmt->fetchAll(PDO::FETCH_OBJ);
if you're trying to get the rows from the database, the function you need is mysqli_stmt::fetch(), not mysqli_stmt::fetch_metadata()
You're also missing a few steps. When using prepared statements, you must specify the fields you would like to return instead of using the star wildcard, and then use mysqli_stmt::bind_result() to specify which variables the database fields should be placed in.
If you're more familiar with the original MySQL extension, prepared statements have a different process to use. If your select statement has a parameter (eg., "WHERE value=?") prepared statements are definitely recommended, but for your simple query, mysqli:query() would be sufficient, and not very different from the process of mysql_query()
I believe the problem is that mysqli_stmt::result_metadata() returns a mysqli_result object without any of the actual results — it only holds metadata.
So what you want to do is use $result = $statement->bind_result(...) and then call $result->fetch() repeatedly to get the results.
One of the comments under the bind-result() article shows how to do this for a query like yours, where you don't necessarily know all of the columns being returned.

Categories