Select random row from table with deleted records - php

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);
}

Related

How do I count unique rows in php pdo?

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";

Select random row from table making sure that row has not been rated

I'm selecting a random row from a table with sequential records:
$stmt = $db->prepare("SELECT COUNT(*) FROM $table");
$stmt->execute();
$stmt->bind_result($total);
$stmt->fetch();
$stmt->close();
$rand = mt_rand(1,$total);
$try = 0;
do {
$stmt = $db->prepare("SELECT id,link,(select count(*) from $table2 where img_id=? AND ip=?) FROM $table WHERE id = ?");
$stmt->bind_param("isi",$rand,$ip,$rand);
$stmt->execute();
$stmt->bind_result($id,$link,$count);
$stmt->fetch();
$stmt->close();
$try++;
} while ($try < 10 && $count > 0);
I'm checking if a user has rated a particular thing before based on their IP.
I get a random number between the first record and the last one with mt_rand(1,$total), then I select that row.
However, the user may have already rated that particular item before, then I need to 're-select' or try for another random row, and here I try 10 times before giving up in the program.
Any way in SQL to do this better or make sure I select one they have never rated before without the whole inefficient try do while thingy? I can't use ORDER BY RAND() because it's too slow.

Ordering by a value that is not in the database where selecting from

how would i go about ordering by a value that is not in the table where i am selecting from, in this instance the value $count1 is not in the table search.
count has the same identifying id as that of the thing it is being reffered to in the other table, this is where count1 is grabbed
$q = $db->prepare("SELECT COUNT(rating) FROM ratings WHERE id='$id' AND rating = 'd'");
$q->execute();
$count1 = $q->fetchColumn();
$query = "SELECT * FROM search WHERE title LIKE '$each' ORDER BY '$count1'"
$query = $db->prepare($query);
$query->execute();
that is from ratings, how would i go about ordering the entries like that, so that they are based off the number of count1 and are decided, i might have to implement something like
$query = "SELECT * FROM search WHERE title LIKE '$each' AND id = '$id' ORDER BY '$count1'"
$query = $db->prepare($query);
$query->execute();
Possible Duplicate: Mysql order by specific ID values
Same thing here, you'll just output your $count1as a comma separated string and add it in the SQL query as ORDER BY FIELD(COUNT,___comma_sep_string___)
ratings is a table, not a database. You can join tables or use subqueries to get the desired result, without having to make multiple queries.
You haven't described how the FOREIGN_KEY is set up in the ratings table, but assuming you have something ratings.search_id, this should work:
SELECT search.*, (SELECT COUNT(rating)
FROM ratings
WHERE ratings.search_id = search.id
AND rating = 'd'
) AS rating_count
FROM search
WHERE title LIKE '$each'
ORDER BY rating_count

PDO get last selected row

Is there a method to get last select ID in a similar way to lastInsertId?
For example:
<?php
$stmt = $db->prepare('SELECT * FROM users WHERE user_id = :user_id');
$stmt->bindValue(':user_id', $_GET['id'], PDO::PARAM_INT);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_OBJ);
$user_id = $db->lastSelectId('user_id'); // what can I do here?
?>
Obviously in the above example I could simply get the last selected row ID with $user->user_id but that's not the question. Any ideas?
If you want to select the last inserted row from your database table, there is no point selecting all rows and then looking for the last in a loop. Besides, user_id should be primary key, in which case you query should only return one row.
If user_id is an auto-incremented field, your query should go like SELECT * FROM users ORDER BY user_id DESC LIMIT 1, this will return the user with the largest user_id.
I will also suggest the you save the timestamp of when users are inserted and then you can do ORDER BY date_added DESC LIMIT 1 this will work irrespective of the ORDER of the user_ids.
No, there is no other way than reading $user->user_id. No magic functions to get the last id of a select.
And it's probably because there is no need for it, since the select returns that value itself. You've shown in your question how easy it is to read the id.
Try With following -
$stmt->insert_id;
Refer the below link -
Using PHP, MySQLi and Prepared Statement, how I return the id of the inserted row?
Make sure you use LIMIT 1 if you're searching for one specific user.
$stmt = $db->prepare('SELECT * FROM users WHERE user_id = :user_id LIMIT 1');
$stmt->bindValue(':user_id', $_GET['id'], PDO::PARAM_INT);
$stmt->execute();
$user = $stmt->fetch(PDO::FETCH_OBJ);
echo $user->user_id;
Here are my solutions :
Put the desired ID of the last SELECT in the $_SESSION['name'].
Put it in the html file as a Hidden Input.
Go fetch it at the beginning of you controller file so it's always set and ready to use.
Use lastInsertId();

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