How do I count unique rows in php pdo? - php

Here's my usual way of counting rows...
$query = "SELECT * FROM users";
$stmt = $db->prepare($query);
$stmt->execute();
$count = $stmt->rowCount();
This will count all rows, even if I use a WHERE clause, it'll still count every row that meets that condition. However, let's say I have a table, we'll call it tokensEarned (that's my actual table name). I have the following data...
user_id = 1,2,4,5,8,8,2,4,3,7,6,2 (those are actual rows in my table - clearly, user 1 has 1 entry, 2 has three entries, etc.) In all, I have 12 entries. But I don't want my query to count 12. I want my query to count each user_id one time. In this example, my count should display 8.
Any help on this? I can further explain if you have any specific questions or clarification you need. I would appreciate it. Thank You.

The following query will yield the distinct user count:
$query = "SELECT COUNT(DISTINCT user_id) AS cnt FROM users";
$stmt = $db->prepare($query);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
echo "distinct user count: " . $row['cnt'];
It isn't possible to get all records and the distinct count in a single query.
Whether you use the query above or you return all the actual distinct rows really depends on whether you need the full records. If all you need are the counts, then it is wasteful to return the data in the records, and what I gave above is probably the best option. If you do need the data, then selecting all distinct rows might make more sense.

You can use distinct in mysql to select only unique fields in your table.
$query = "SELECT distinct user_id FROM users";
$stmt = $db->prepare($query);
$stmt->execute();
$count = $stmt->rowCount();

Change your query to the following, this way you only shows the unique user_id:
$query = "SELECT DISTINCT user_id FROM users";

Related

Select random row from table with deleted records

If I have a table with 3 rows with IDs 1,3,5 because rows with ID 2 and 4 were deleted, how do I make sure I select a row that exists?
$stmt = $db->prepare("SELECT COUNT(*) FROM table");
$stmt->execute();
$stmt->bind_result($numRows);
$stmt->fetch();
$stmt->close();
$random = mt_rand(1,$numRows);
$stmt = $db->prepare("SELECT link FROM table WHERE id=$random");
This won't ever select row with id 5, and also will select one that doesn't exist (2).
If the number of rows are small (and you are sure that it will stay that way), you can use ORDER BY RAND()
(Please note that this will create performance problems with big tables).
Other way is first counting how many rows are there
SELECT COUNT(*) AS total FROM table;
then pick a random number
$rand = rand(1, $total);
and select that row with limit
SELECT * FROM table LIMIT $rand, 1;
U can use a SQLstatement with EXISTS
SELECT link
FROM table
WHERE EXISTS (SELECT link
FROM table
WHERE id = $random);
If you just want a random row and don't care about the id, then you could use:
SELECT link FROM table
ORDER BY RAND()
LIMIT 1
For large numbers of rows (10000+), then you may need to implement another solution, as this query can be slow. This site has a good explanation and alternative solutions
If you want to follow your approach then you have to do some changes in your query.
1.) Query one : select id from table. // It will give you array of existing id.
2.) You have to use array_rand(). and use your second query.
Example :
$stmt = $db->prepare("SELECT ID FROM table");
$result = $stmt->fetchAll();
$random = array_rand(array_flip($result), 1);
$stmt = $db->prepare("SELECT link FROM table WHERE id=$random");
You could select one, randomly order, like this:
SELECT link FROM table ORDER BY RAND() LIMIT 1
UPDATE:
You should benchmark the different solutions you have, but I'm thinking this one could be nice with large amount of rows:
$stmt = $db->prepare("SELECT COUNT(*) FROM table");
$stmt->execute();
$stmt->bind_result($numRows);
$stmt->fetch();
$stmt->close();
$random = mt_rand(1,$numRows);
$stmt = $db->prepare("SELECT link FROM table WHERE id>=$random LIMIT 1");
$stmt->execute();
$stmt->bind_result($link);
if(!$link){
$stmt = $db->prepare("SELECT link FROM table WHERE id<$random LIMIT 1");
$stmt->execute();
$stmt->bind_result($link);
}

MySQL PDO, selecting a single row from a result-set

If I run a query that returns multiple rows, is there a way I can select just one row out of that result?
So if I do something like
SELECT * FROM table WHERE number = 10
and it returns 33 results, is there a way I can go through those one at a time instead of returning the whole result set at once, or just return, for example, row 5 of the result set?
I have read about scrollable cursors but it seems they don't work on MySQL, although that seems to be what I am looking for....
I am using PDO with MySQL and PHP. I hope this makes sense, if not I will try and explain better.
Edit: This worked for what I wanted. Thanks.
$stmt = $dbh->prepare("SELECT * FROM $table WHERE user_points = '$target' ORDER BY tdate DESC LIMIT $count,1");
is there a way I can select just one row out of that result?
Yes there is, you can use LIMIT:
SELECT * FROM table WHERE number = 10 LIMIT 1;
$sql= "SELECT * FROM $table WHERE user_points = '$target' ORDER BY tdate";
$stmt= $pdo -> prepare($sql);
$stmt->execute();
$data = $stmt ->fetchAll();
//You asked about getting a specific row 5
//rows begin with 0. Now $data2 contains row 5
$data2 = $data[4];
echo $data2['A_column_in_your_table'];//row 5 data

Count rows, or keep int field for counting?

When I want to find out how many shoes Alfred has, I always count the rows in the table "usershoes" where the userid matches Alfred's
But since I switched to PDO, and select row count is not simple or bulletproof/consistent, I'm reconsidering my methods
Maybe I should instead keep an int field "shoes" directly in table "users", keep number of shoes there, and then increase/decrease that number for that user along the way? Feels not right..
If anyone has a solid method for simple row counting on an existing select query, without extra query, let me know
Try something like this
SELECT COUNT(*) FROM usershoes
WHERE userid="theIdOfTheUser";
I could not get count(fetchColumn()) or fetchColumn() to work correctly (outputted 1 when 0 was the real number)
So now I'm using this, and it works:
$sql = 'SELECT COUNT(*) as numrows, shoecolor FROM usershoes WHERE userid = ?'
$STH = $conn->prepare($sql);
$STH->execute(array($someuseridvar));
And then:
$row = $STH->fetch();
if ($row['numrows'] > 0) {
// at least one row was found, do something
}
With MySQL, you can use FOUND_ROWS():
$db = new PDO(DSN...);
$db->setAttribute(array(PDO::MYSQL_USE_BUFFERED_QUERY=>TRUE));
$rs = $db->query('SELECT SQL_CALC_FOUND_ROWS * FROM table LIMIT 5,15');
$rs1 = $db->query('SELECT FOUND_ROWS()');
$rowCount = (int) $rs1->fetchColumn();
$rowCount will contain the total number of rows, not 15.
Taken from:
http://php.net/manual/en/pdostatement.rowcount.php#83586

issue with SELECT COUNT(id)

I've been using this command to retrieve the number of the fields which have same email address:
$query = $db->query("SELECT COUNT(`user_id`) FROM `users` WHERE `email`='$email'") or die($db-error);
There are 3 records in users table with the same email address. The problem is when I put * instead of COUNT(user_id) it returns correctly: $query->num_rows gives 3 but when I use COUNT(user_id) then $query->num_rows returns 1 all the time. how can I correct this or where is my problem?
When you use $query->num_rows with that query it will return 1 row only, because there is only one count to return.
The actual number of rows will be contained in that query. If you want the result as an object, or associative array give the count a name:
$query = $db->query("SELECT COUNT(`user_id`) AS total FROM `users` WHERE `email`='$email'") or die($db-error);
And in the returned query total should be 3, while $query->num_rows will still be 1. If you just want the value a quick way would be using $total = $query->fetchColumn();.
As others have said though, be careful with NULL user ids, because COUNT() will ignore them.
Emails have to be uinque in users table. Thus, you need no count at all.
You ought to use prepared statements.
You shouldn't post a code that will never run.
Here goes the only correct way to run such a query:
$sql = "SELECT * FROM `users` WHERE `email`=?";
$stm = $db->prepare($sql);
$stm->execute([$email]);
$user = $stm-fetch();
(the code was written due to erroneous tagging. For mysqli you will need another code, but guidelines remains the same.)
Something like this
$sql = "SELECT * FROM `users` WHERE `email`=?";
$stm = $db->prepare($sql);
$stm->bind_param('s',$email);
$stm->execute();
$res = $stm->get_result()
$user = $res->fetch_assoc();
in $user variable you will have either userdata you will need in the following code or false which means no user found. Thus $user can be used in if() statement all right without the need of any counts.
In case when you really need to count the rows, then you use this count() approach you tried. You can use a function from this answer for this:
$count = getVar("SELECT COUNT(1) FROM users WHERE salary > ?", $salary);
That's the correct behaviour: If you use the COUNT function, the result of your select query will be just one row with one column containing the number of data sets.
So, you can retrieve the number of users with the given E-mail address like this:
$query = $db->query("SELECT COUNT(`user_id`) FROM `users` WHERE `email`='$email'") or die($db-error);
$row = $query->fetch_row();
$count = $row[0];
Note that this is faster than querying all data using SELECT * and checking $query->num_rows because it does not need to actually fetch the data.

What columns do I select if I only want a count

I want to count the results in my table... but I am usually confronted with a decision, what column do I select? Should I select the primary key? Wild Card? What has the most performance? Does it matter? Below is an example of how I call it
// Wild Card, I feel like this is the worst one for performance?
$query = "SELECT * FROM table WHERE status = ?";
// Only selecting one column? Is there a better way
$query = "SELECT id FROM table WHERE status = ?";
$stmt = $mysqli->prepare($query);
$stmt->bind_param('i',$status);
$stmt->execute();
$stmt->store_result();
$returned_amount = $stmt->num_rows;
$stmt->free_result();
$stmt->close();
Well, if you want MySQL to handle the count, you can just do the following
$query = "SELECT COUNT(*) as count FROM `table` WHERE `field` = ?";
The as count part means that you can access the count as if it were a column.
You should SELECT COUNT(`id`) FROM `table` WHERE `status`=?, much more efficient ;)
If you are willing to count all the results of your query (ie: for pagination), you can use FOUND_ROWS() option:
In your main query you need to add SQL_CALC_FOUND_ROWS option just after SELECT and in second query you need to use FOUND_ROWS() function to get total number of rows.
SELECT SQL_CALC_FOUND_ROWS id FROM table WHERE status = 'something' LIMIT 10;
SELECT FOUND_ROWS();
More info in dev.mysql.com

Categories