PHP select random id from database - php

I want to select a random userid from this query where level = 1 (Normal user) and alive = Yes (User is alive and can play)
$getaid = $sql->query("SELECT userid FROM `users` WHERE `level`='1' AND `alive`='yes'");
I know I can use
$totalusers = mysql_num_rows($getaid);
$randomuserid = rand(1,$totalusers);
to select a random userid but in that code there's a chance that it select a user that is dead (alive = No) or a staff member (level >= 1). is there a better way I can select a random userid without a chance to grab staff members/dead members?

You can do
"SELECT userid FROM `users` WHERE `level`='1' AND `alive`='yes' ORDER BY RAND() LIMIT 1"

Instead of selecting all the records and trying to choose a random one in PHP's side, let the database do the heavy lifting for you:
SELECT userid
FROM `users`
WHERE `level` = '1' AND `alive` = 'yes'
ORDER BY RAND()
LIMIT 1

Your method is about the worst of all possible worlds -- you are bringing all the data back from the database to an array in PHP and then choosing a random value there. But, it should work, because you are only bringing back the appropriate users.
One way of doing this in the database that is simple enough is:
SELECT userid
FROM `users`
WHERE `level`='1' AND `alive` = 'yes'
ORDER BY rand()
LIMIT 1;
However, if you have more than a few hundred rows (or a few thousand), this starts to get too expensive. There are definitely other methods, but bringing all the data back is not a good idea.
One simple fix is to get approximately 100 rows and then randomize them:
SELECT userid
FROM `users` CROSS JOIN
(SELECT COUNT(*) as cnt FROM users `level` = '1' AND `alive` = 'yes') x
WHERE `level` = '1' AND `alive` = 'yes' AND
rand() < 100 * 1 / x
ORDER BY rand()
LIMIT 1;
For performance, you want an index on `users(level, alive).

You can use ORDER BY RAND() LIMIT 1:
$getaid = $sql->query("SELECT userid FROM `users` WHERE `level`='1' AND `alive`='yes' ORDER BY RAND() LIMIT 1");

Related

SQL Order By id and Count star not working

I would like to get number of all records and get last record :
$sql_count_sms = "SELECT count(*) as total,content,id FROM android_users_sms WHERE user_id=$id ORDER BY id DESC";
$result_count_sms = mysql_query($sql_count_sms);
$row_num_sms = mysql_fetch_assoc($result_count_sms);
$num_sms = $row_num_sms['total'];
$last_my_sms = $row_num_sms['content'];
I can get number of records but I can't get last content record .
It returns first record !
Where is my wrong ?
Below codes works fine, but I think count(*) is faster than mysql_num_rows .
$sql_count_sms = "SELECT content,id FROM android_users_sms WHERE user_id=$id ORDER BY id DESC";
$result_count_sms = mysql_query($sql_count_sms);
$row_num_sms = mysql_fetch_assoc($result_count_sms);
$num_sms = mysql_num_rows($result_count_sms);
$last_my_sms = $row_num_sms['content'];
Any solution?
The grain of the two results you want is not the same. Without using a sub-query you can't combine an aggregate and a single row into the same result.
Think of the grain as the base unit of the result. The use of GROUP BY and aggregate functions can influence that "grain"... one result row per row on table, or is it grouped by user_id etc... Think of an aggregate function as a form of grouping.
You could break it out into two separate statements:
SELECT count(*) as total FROM android_users_sms WHERE user_id = :id;
SELECT * FROM android_users_sms WHERE user_id = :id ORDER BY id DESC LIMIT 1;
Also, specific to your question, you probably want a LIMIT 1 in combination with the ORDER BY to get just the last row.
Now, counter intuitively perhaps, this should also work:
SELECT count(*), content, id
FROM android_users_sms
WHERE user_id = :id
GROUP BY id, content
ORDER BY id
LIMIT 1;`
This is because we've changed the "grain" with the GROUP BY. This is the real nuance and I feel like this could probably be explained better than I am doing now.
You could also do this with a sub query like so:
SELECT aus.*,
(SELECT count(*) as total FROM android_users_sms WHERE user_id = :id) AS s1
FROM android_users_sms AS aus
WHERE user_id = :id ORDER BY id DESC LIMIT 1;

MySQL and PHP performance of multiple queries

I'm asking you for some advice. I have a website where I have videos and users can give them thumbs up and thumbs down. Their saved in a single table. Currently I have three sql queries to get the count of thumbs up, the count of thumbs down and what the logged in user gave (if he did).
And now I'm thinking about making that more performance, but I don't know what is better, since I want to keep the count of queries down.
Method 1) Keep these three queries as they are:
SELECT COUNT(*) as rowcount FROM `videolikes` WHERE `vid` = 'gt6w_RZfs5yx' AND `thumb` = '1' LIMIT 1
SELECT COUNT(*) as rowcount FROM `videolikes` WHERE `vid` = 'gt6w_RZfs5yx' AND `thumb` = '0' LIMIT 1
SELECT * FROM `videolikes` WHERE `vid` = 'gt6w_RZfs5yx' AND `uid` = '1' LIMIT 1
Method 2) Make one query with sub queries (something like that (it doesn't work how it is here)):
SELECT *, (SELECT COUNT(*) as rowcount FROM `videolikes` WHERE `vid` = 'gt6w_RZfs5yx' AND `thumb` = '1' LIMIT 1) as thumbsups, (SELECT COUNT(*) as rowcount FROM `videolikes` WHERE `vid` = 'gt6w_RZfs5yx' AND `thumb` = '0' LIMIT 1) as thumbsdowns FROM `videolikes` WHERE `vid` = 'gt6w_RZfs5yx' AND (`uid` = '1' OR `uid` = NULL)
Method 3) Your own idea, maybe?
Tell me what you think and give some code (if you want).
Best Regards
Charlotte
edit: I'll add some more information:
vid is the id of the video. Everything about the likes is stored in this table, referenced to the video with the VideoID (vid). uid is the UserID who gave the like (or dislike). That means there isn't only the likes and dislikes of one video in this table. To know which like and dislike is for which video, the like will be stored with the videoid.
Or you could use a single query for the up/down votes:
SELECT SUM(thumb = 1) AS upvote, SUM(thumb = 0) AS downvote, ....
MySQL will take the boolean true/false results of those thumb = X tests, convert to integer 0 or 1, and then sum them up
You can combine the first two queries like that:
SELECT
SUM(IF(`thumb` = 1, 1, 0)) AS rowcountthumb0,
SUM(IF(`thumb` = 0, 1, 0)) AS rowcountthumb1
FROM `videolikes` WHERE `vid` = 'gt6w_RZfs5yx'
Since the last query seems to be semantically different, I would keep it separate from the one mentioned here for clarity.

mysql - find out if input exists in last 30 records

I want to know if the registering user's ip exist in the last 30 records. This will help me prevent multiple registrations. I have difficulty in my attempt to do this in mysql. This is where I'm at so far:
The old query that checks multiple ip's across all records:
$same_ip_count = mysql_num_rows(mysql_query("SELECT * FROM login_users WHERE ip='".$ip."'"));
The new code that fails:
$same_ip_count = mysql_num_rows(mysql_query(
"SELECT 'user_id'
FROM (
SELECT *
FROM login_users
ORDER BY user_id DESC
LIMIT 30
) AS t
WHERE t.ip = '".$ip."'"));
Kind of new to sql so any suggestions will be great at this point.
EDIT:
To be clear - I want to search only the last 30 records. I don't want to search all records and then limit them to 30.
First use a subquery to get the last 30 items. Then, you can select from that list of 30 where the IP matches.
Select * From
(SELECT *
FROM login_users
ORDER BY registration_date DESC
LIMIT 30) a
WHERE a.ip = '".$ip."'
Mysql doesn't know what are the 30 last records. If you use an auto-incrementing primary key then you can ORDER by that key in descendant order.
try this.
$query = mysql_query(
"SELECT 'user_id' as result from `login_users` WHERE ip = '".$ip."' ORDER BY user_id DESC LIMIT 30");
$same_ip = mysql_num_rows($query);

Return a random row without using ID, MySQL

I am looking for a better way of doing this:
SELECT * FROM $tbl_name WHERE id >= (SELECT FLOOR( MAX(id) * RAND()) FROM $tbl_name ) ORDER BY id LIMIT 1;
It is slow and isn't very random, I get the same result every 10 or so query's.
This selects a random row from the table regardless of its ID.
So far I have this:
// Get amount of rows in database
$result = mysql_query("SELECT * FROM $tbl_name");
$num_rows = mysql_num_rows($result);
// Generate random number
$random_row = rand(1, $num_rows);
But I don't know how to get a certain row.
I do not mean
SELECT * FROM $tbl_name WHERE id = $random_row
Because my database has gaps in the ID column so it would sometimes fail.
Has anyone got script that can get a random result from a MySQL database without replying on IDs and is super fast? (the database contains about 20000 rows)
SELECT * FROM $tbl_name WHERE 1 ORDER BY RAND() LIMIT 1;
20,000 rows isn't really that much, the above should be fast enough.
Juhana is right by the book: "ORDER BY RAND() LIMIT 1", and of course 20k isn1t that much.
Other option will be with subselects:
SELECT [fields]
FROM myTable,
(SELECT FLOOR(MAX(myTable.id) * RAND()) AS randId FROM myTable) AS someRandId
WHERE myTable.id = someRandId.randId
discussed here
(please avoid select * when it`s unnecessary)
After some searching though the comments on the link Adi sent, I have found a decent solution.
SELECT * FROM $tbl_name T JOIN (SELECT CEIL(MAX(ID)*RAND()) AS ID FROM $tbl_name) AS x ON T.ID >= x.ID LIMIT 1;
Source: wanderr.com/jay/order-by-slow/2008/01/30
Seems to be very fast and very random!

Calculate Rank with points

I have a database with users and points (in fact it's a percentage, but that doesn't matter). The user(s) with the highest number of points is on the first rank, the second on the second rank ...
I could get the rank of a $searchedUserID if I did somethink like this:
SELECT `user_id`, `points` FROM `usertable` ORDER BY `points` DESC
/** This function returns the rank of a user. The rank nr. 1 is the best.
* It is possible that some users share a rank.
*
* #param int $searchedUserID the ID of the user whose rank you would like to
* know
*
* #return int rank
*/
function getUserRank($searchedUserID)
{
$userArray = getAllUsersOrderedByPoints();
$rank = 0;
$lastPoints = -1; // Never happens
foreach ( $userArray as $user) {
if ($user['point'] != $lastPoints) $rank++;
if ($user['user_id'] == $searchedUserID) break;
}
return $rank;
}
Isn't there a more direct way to get this with (My)SQL?
If not: Can the PHP-part be improved?
(edit: I could store the rank calculated by PHP directly in the database ... but this would mean I had to make quite a lot of UPDATEs.)
edit2: Perhaps GROUP BY could be used? Something like:
SELECT `user_id`, `points` FROM `usertable` GROUP BY `points` ORDER BY `points` DESC
The problem with this query is the possibility, that I don't get the searched user_id. It would be necessary to send a second query:
SELECT `user_id` FROM `usertable` WHERE `points` = $pointsOfTheUser
You ask:
Isn't there a more direct way to get this with (My)SQL?
Yes. In SQL:2003, you could use the DENSE_RANK() window function. In MySQL, you can simulate this, as the (dense) rank of a record by some score is simply the count + 1 of distinct better scores:
SELECT u.user_id,
u.points,
1 + COUNT(DISTINCT others.points) AS `dense_rank`
FROM users u
LEFT JOIN users others
ON u.points < others.points -- Which other users have more points?
WHERE user_id = ?
GROUP BY 1, 2;
perhaps an inner join with group by and sort would do the trick?
SELECT * FROM
INNER JOIN
(
SELECT user_id AS uid, max(points) AS score
FROM usertable GROUP BY user_id
)
AS ds ON usertable.user_id = ds.uid AND usertable.points = ds.score
ORDER BY score DESC
Just thinking on paper (pixels) .. wouldn't that give you the list in order from highest points to lowest with a unique record per user ... or are you looking to have these sorted where you can clarify a tie as a single "place" in the rankings?

Categories