mysql + php: Selecting multiple random results - php

I've been looking for this for a while but with no success.
I am trying to implement a recomendation bar, for example like in youtube, when you are seeing a video it shows the list or recommended videos on the right.
At this moment I am using this method:
$offset_result = mysql_query( " SELECT FLOOR(RAND() * COUNT(*)) AS `offset` FROM `$tablename` ");
$offset_row = mysql_fetch_object($offset_result );
$offset = $offset_row->offset;
$result_rand = mysql_query( " SELECT * FROM `$tablename` LIMIT $offset, 9 " );
This works fine, but sometimes doesn't show any result, and the problem is also that its not completely random, because it shows for example the first ID as 200, so the next result will be id 201 and then 202 and so.
I would like to know if there is a way to show this 9 randon results, for example 1º result id 500, 2º result id 10, 3º result id 788, etc etc?
Thank you

Not entirely sure this answers what you are looking for, but try:
$result_rand = mysql_query("SELECT * FROM " . $tablename . " ORDER BY RAND() LIMIT 9");

You can use php rand() function to create 5 numbers and save them in an array:
http://php.net/manual/en/function.rand.php
<?php
$rand_array = array();
for($i=1;$i<5;$i++) {
$rand_array[$i] = rand(0,500);
}
?>
and after that create a query with every int with a foreach loop and work with your data.
<?php
foreach ($rand_array as $integer) {
$q = "SELECT * from $tablename WHERE id='$integer';";
}
?>
Does this helps?

First you should use mysqli_ functions instead of mysql_ because the latter is deprecated. Second use order by rand() to get random rows:
$rand_result = mysqli_query( "SELECT * FROM $tablename ORDER BY RAND() LIMIT 9;" );

UNTESTED:
SELECT id, #rownum:=#rownum+1 AS rownum, name
FROM users u,
(SELECT #rownum:=0) r
THis will give a unique number to each row in sequence. Now if you create a temp table with 9 random numbers between 1 and count(*) of your table and JOIN those two together...
Not sure about performance but seems like it might be faster than Rand and order by since I only need 9 random numbers

Related

Select a fixed number of records from a particular user in a sql result

I have 2 tables - users and articles.
users:
user_id (int)
name (varchar)
articles:
article_id (int)
user_id (int)
title (varchar)
description (text)
In my application I need to display 20 RANDOM articles on a page.
My query is like this:
SELECT a.title
, a.description
, u.name
FROM articles a
JOIN users u
USING (user_id)
ORDER
BY RAND()
LIMIT 20
A user can have any number of articles in the database.
Now the problem is sometimes out of 20 results, there are like 9-10 articles from one single user.
I want those 20 records on the page to not contain more than 3 (or say 4) articles from a particular user.
Can I achieve this through SQL query. I am using PHP and MySQL.
Thanks for your help.
You could try this?
SELECT * FROM
(
SELECT B.* FROM
(
SELECT A.*, ROW_NUMBER() OVER (PARTITION BY A.USER_ID ORDER BY A.R) USER_ROW_NUMBER
FROM
(
SELECT a.title, a.description, u.name, RND() r FROM articles a
INNER JOIN users u USING (user_id)
) A
) B
WHERE B.USER_ROW_NUMBER<=4
) C
ORDER BY RAND() LIMIT 20
Mmm, intresting I don't think this is possible through a pure sql query.
My best idea would be to have an array of the articles that you'll eventually display query the database and use the standard SELECT * FROM Articles ORDER BY RAND() LIMIT 20
The go through them, making sure that you have indeed got 20 articles and no one has breached the rules of 3/4 per user.
Have another array of users to exclude, perhaps using their user id as an index and value of a count.
As you go through add them to your final array, if you find any user that hits you rule add them to the array.
Keep running the random query, excluding users and articles until you hit your desired amount.
Let me try some code (it's been a while since I did php)
$finalArray = [];
$userArray = [];
while(count($finalArray) < 20) {
$query = "SELECT * FROM Articles ";
if(count($finalArray) > 0) {
$query = $query . " WHERE articleID NOT IN(".$finalArray.")";
$query = $query . " AND userID NOT IN (".$userArray.filter(>4).")";
}
$query = $query . " ORDER BY Rand()";
$result = mysql_query($query);
foreach($row = mysql_fetch_array($result)) {
if(in_array($finalArray,$row) == false) {
$finalArray[] = $row;
}
if(in_array($userArray,$row[userId]) == false) {
$userArray[$row[userId]] = 1;
}
else {
$userArray[$row[userId]] = $userArray[$row[userId]] + 1;
}
}

How to get the minimum id from the table in MySql

I have written a MySql query to get the columns related with minimum id . Looks something like this
SELECT min(id) as ID,feed , idpropiedad FROM `registrofeed` WHERE feed=21
The table has 4 rows looks like this
So according to the function that I have written
function setLC()
{
$sql = "
SELECT min(id) as ID
, feed
, idpropiedad
FROM `registrofeed`
WHERE feed=21
";
$result = $this->localDb->execute($sql);
$row=mysql_fetch_array($result);
echo $sql;
echo $row['idpropiedad'];
$this->lastCode = $row['idpropiedad'];
}
It returns empty string for idpropiedad
Can any one help me out where I am going wrong
Thanks in advance
I'd think the query you're actually looking for is this:
SELECT id, feed, idpropiedad
FROM registrofeed
WHERE feed = 21
ORDER BY id ASC
LIMIT 1
MIN() is giving you the generally lowest value in the column, it does not affect the rest of the columns. If you want the whole row with the lowest id it doesn't help.
To illustrate, if you really wanted to use MIN here, you'd have to do:
SELECT id, feed, idpropiedad
FROM registrofeed
WHERE id = (SELECT MIN(id) FROM registrofeed WHERE feed = 21)
You can do a better query like this:
$sql = "
SELECT id as ID
, feed
, idpropiedad
FROM `registrofeed`
WHERE feed=21
HAVING MIN(id)
";
This will return only one row with the minimum id number. It's more readable than using ORDERING AND LIMIT 1.
try your select query as
SELECT * FROM registrofeed WHERE feed='21' ORDER BY id ASC LIMIT 1
this fetches the row having minimum id.
Hope it helps
Try this
$sql = "SELECT min(id) as ID,feed , idpropiedad FROM `registrofeed` WHERE feed='21' order by id asc";

PHP Calculate rank from database

I got a little problem, I've got a database, in that database are different names, id, and coins. I want to show people their rank, so your rank has to be 1 if you have the most coins, and 78172 as example when your number 78172 with coins.
I know I can do something like this:
SELECT `naam` , `coins`
FROM `gebruikers`
ORDER BY `coins` DESC
But how can I get the rank you are, in PHP :S ?
You can use a loop and a counter. The first row from MySql is going the first rank,I.e first in the list.
I presume you want something like:
1st - John Doe
2nd - Jane Doe
..
..
right?
See: http://www.if-not-true-then-false.com/2010/php-1st-2nd-3rd-4th-5th-6th-php-add-ordinal-number-suffix
Helped me a while ago.
You could use a new varariable
$i = "1";
pe care o poti folosi in structura ta foreach,while,for,repeat si o incrementezi mereu.
and you use it in structures like foreach,while,for,repeat and increment it
$i++;
this is the simplest way
No code samples above... so here it is in PHP
// Your SQL query above, with limits, in this case it starts from the 11th ranking (0 is the starting index) up to the 20th
$start = 10; // 0-based index
$page_size = 10;
$stmt = $pdo->query("SELECT `naam` , `coins` FROM `gebruikers` ORDER BY `coins` DESC LIMIT {$start}, {$page_size}");
$data = $stmt->fetchAll();
// In your template or whatever you use to output
foreach ($data as $rank => $row) {
// array index is 0-based, so add 1 and where you wanted to started to get rank
echo ($rank + 1 + $start) . ": {$row['naam']}<br />";
}
Note: I'm too lazy to put in a prepared statement, but please look it up and use prepared statements.
If you have a session table, you would pull the records from that, then use those values to get the coin values, and sort descending.
If we assume your Session table is sessions(session_id int not null auto_increment, user_id int not null, session_time,...) and we assume that only users who are logged in would have a session value, then your SQL would look something like this: (Note:I am assuming that you also have a user_id column on your gebruikers table)
SELECT g.*
FROM gebruikers as g, sessions as s WHERE s.user_id = g.user_id
ORDER BY g.coins DESC
You would then use a row iterator to loop through the results and display "1", "2", "3", etc. The short version of which would look like
//Connect to database using whatever method you like, I will assume mysql_connect()
$sql = "SELECT g.* FROM gebruikers as g, sessions as s WHERE s.user_id = g.user_id ORDER BY g.coins DESC";
$result = mysql_query($sql,$con); //Where $con is your mysql_connect() variable;
$i = 0;
while($row = mysql_fetch_assoc($result,$con)){
$row['rank'] = $i;
$i++;
//Whatever else you need to do;
}
EDIT
In messing around with a SQLFiddle found at http://sqlfiddle.com/#!2/8faa9/6
I came accross something that works there; I don't know if it will work when given in php, but I figured I would show it to you either way
SET #rank = 0; SELECT *,(#rank := #rank+1) as rank FROM something order by coins DESC
EDIT 2
This works in a php query from a file.
SELECT #rank:=#rank as rank,
g.*
FROM
(SELECT #rank:=0) as z,
gebruikers as g
ORDER BY coins DESC
If you want to get the rank of one specific user, you can do that in mysql directly by counting the number of users that have more coins that the user you want to rank:
SELECT COUNT(*)
FROM `gebruikers`
WHERE `coins` > (SELECT `coins` FROM `gebruikers` WHERE `naam` = :some_name)
(assuming a search by name)
Now the rank will be the count returned + 1.
Or you do SELECT COUNT(*) + 1 in mysql...

PHP add many rows to database

I have problem with database. I want to add about 10 new rows after pageload. It should check if there is an article with id (that is actually loading). If its not, add 10 of that id with different tag id.
$sprawdz = "SELECT id_artykulow FROM tag_art WHERE id_artykulow='".$_GET['id']."' " ;
$miasto = mysql_query($sprawdz);
$a = mysql_num_rows($miasto);
while ($a<=10){
$zm = "SELECT id FROM tag_content ORDER BY RAND() LIMIT 1";
$sw = mysql_query($zm);
while($row3=mysql_fetch_array($sw)){
$zmienna = "INSERT INTO tag_art(id_artykulow, id_tagow) VALUES ('".$_GET['id']."', '".$row3['id']."' ) ";
$cokolwiek = mysql_query($zmienna);
}
$a++;
}
There is two tables. One tag_content with id (of the tags), and another tag_art with id_artykulow (= id of the article) and id_tagow (id of the tag taken from tag_content)
Don't know why, but it doesn't add 10 rows (it should be for example ten id_artykulow = 10, with different id_tagow). How to fix it?
Thx for help and let me know if u need more informations (like more code etc.)
why do you have 2 while loops?
cant you just replace
while ($a<=10){
$zm = "SELECT id FROM tag_content ORDER BY RAND() LIMIT 1";
with
if ($a<=10){
$zm = "SELECT id FROM tag_content ORDER BY RAND() LIMIT " . (10-$a);
or just let the databse do all the work by:
$query = "INSERT INTO tag_art(id_artykulow, id_tagow) SELECT '".$_GET['id']."', id FROM tag_content ORDER BY RAND() LIMIT " . (10-$a);
(and i would also recomendate protection agains sql-injections, mysql_real_escape_string())
Or let the database do all the work:
// store id as mysql-variable, but let php force it to an integer first
mysql_query("SET #id = " . (int) $_GET['id']);
// calculate how many new art we need, to get 10, using greatest to avoid negative numbers
mysql_query("SELECT #art_needed := GREATEST(0, 10 - COUNT(*)) FROM tag_art WHERE id_artykulow = #id");
// Need to use prepere to be able to have an mysql-variable as limit
mysql_query("PREPARE art_stmt FROM 'INSERT INTO tag_art(id_artykulow, id_tagow) SELECT ?, id FROM tag_content ORDER BY RAND() LIMIT ?'");
// execute the query using the mysql-variables
mysql_query("EXECUTE art_stmt USING #id, #art_needed");
just reread my old answer, and no longer agrees with "or just let the databse do all the work by" for that answer, the database could do alot more.

Select variable number of random records from MySQL

I want to show a random record from the database. I would like to be able to show X number of random records if I choose. Therefore I need to select the top X records from a randomly selected list of IDs
(There will never be more than 500 records involved to choose from, unless the earth dramatically increases in size. Currently there are 66 possibles.)
This function works, but how can I make it better?
/***************************************************/
/* RandomSite */
//****************/
// Returns an array of random site IDs or NULL
/***************************************************/
function RandomSite($intNumberofSites = 1) {
$arrOutput = NULL;
//open the database
GetDatabaseConnection('dev');
//inefficient
//$strSQL = "SELECT id FROM site_info WHERE major <> 0 ORDER BY RAND() LIMIT ".$intNumberofSites.";";
//Not wonderfully random
//$strSQL = "SELECT id FROM site_info WHERE major <> 0 AND id >= (SELECT FLOOR( COUNT(*) * RAND()) FROM site_info ) ORDER BY id LIMIT ".$intNumberofSites.";";
//Manual selection from available pool of candidates ?? Can I do this better ??
$strSQL = "SELECT id FROM site_info WHERE major <> 0;";
if (is_numeric($intNumberofSites))
{
//excute my query
$result = #mysql_query($strSQL);
$i=-1;
//create an array I can work with ?? Can I do this better ??
while ($row = mysql_fetch_array($result, MYSQL_NUM))
{
$arrResult[$i++] = $row[0];
}
//mix them up
shuffle($arrResult);
//take the first X number of results ?? Can I do this better ??
for ($i=0;$i<$intNumberofSites;$i++)
{
$arrOutput[$i] = $arrResult[$i];
}
}
return $arrOutput;
}
UPDATE QUESTION:
I know about the ORDER BY RAND(), I just don't want to use it because there are rumors it isn't the best at scaling and performance. I am being overly critical of my code. What I have works, ORDER BY RAND() works, but can I make it better?
MORE UPDATE
There are holes in the IDs. There is not a ton of churn, but any churn that happens needs to be approved by our team, and therefore could handled to dump any caching.
Thanks for the replies!
Why not use the Rand Function in an orderby in your database query? Then you don't have to get into randomizing etc in code...
Something like (I don't know if this is legal)
Select *
from site_info
Order by Rand()
LIMIT N
where N is the number of records you want...
EDIT
Have you profiled your code vs. the query solution? I think you're just pre-optimizing here.
If you dont want to select with order by rand().
Instead of shuffeling, use array_rand on the result:
$randKeys = array_rand($arrResult, $intNumberofSites);
$arrOutput = array_intersect_key(array_flip($randKeys), $arrResult);
edit: return array of keys not new array with key => value
Well, I don't think that ORDER BY RAND() would be that slow in a table with only 66 rows, but you can look into a few different solutions anyway.
Is the data really sparse and/or updated often (so there are big gaps in the ids)?
Assuming it's not very sparse, you could select the max id from the table, use PHP's built-in random function to pick N distinct numbers between 1 and the max id, and then attempt to fetch the rows with those ids from the table. If you get back less rows than you picked numbers, get more random numbers and try again, until you have the number of rows needed. This may not be particularly fast either.
If the data is sparse, I would set up a secondary "id-type" column that you make sure is sequential. So if there are 66 rows in the table, ensure that the new column contains the values 1-66. Whenever rows are added to or removed from the table, you will have to do some work to adjust the values in this column. Then use the same technique as above, picking random IDs in PHP, but you don't have to worry about the "missing ID? retry" case.
Here are the three functions I wrote and tested
My answer
/***************************************************/
/* RandomSite1 */
//****************/
// Returns an array of random rec site IDs or NULL
/***************************************************/
function RandomSite1($intNumberofSites = 1) {
$arrOutput = NULL;
GetDatabaseConnection('dev');
$strSQL = "SELECT id FROM site_info WHERE major <> 0;";
if (is_numeric($intNumberofSites))
{
$result = #mysql_query($strSQL);
$i=-1;
while ($row = mysql_fetch_array($result, MYSQL_NUM)) {
$arrResult[$i++] = $row[0]; }
//mix them up
shuffle($arrResult);
for ($i=0;$i<$intNumberofSites;$i++) {
$arrOutput[$i] = $arrResult[$i]; }
}
return $arrOutput;
}
JPunyon and many others
/***************************************************/
/* RandomSite2 */
//****************/
// Returns an array of random rec site IDs or NULL
/***************************************************/
function RandomSite2($intNumberofSites = 1) {
$arrOutput = NULL;
GetDatabaseConnection('dev');
$strSQL = "SELECT id FROM site_info WHERE major<>0 ORDER BY RAND() LIMIT ".$intNumberofSites.";";
if (is_numeric($intNumberofSites))
{
$result = #mysql_query($strSQL);
$i=0;
while ($row = mysql_fetch_array($result, MYSQL_NUM)) {
$arrOutput[$i++] = $row[0]; }
}
return $arrOutput;
}
OIS with a creative solution meeting the intend of my question.
/***************************************************/
/* RandomSite3 */
//****************/
// Returns an array of random rec site IDs or NULL
/***************************************************/
function RandomSite3($intNumberofSites = 1) {
$arrOutput = NULL;
GetDatabaseConnection('dev');
$strSQL = "SELECT id FROM site_info WHERE major<>0;";
if (is_numeric($intNumberofSites))
{
$result = #mysql_query($strSQL);
$i=-1;
while ($row = mysql_fetch_array($result, MYSQL_NUM)) {
$arrResult[$i++] = $row[0]; }
$randKeys = array_rand($arrResult, $intNumberofSites);
$arrOutput = array_intersect_key($randKeys, $arrResult);
}
return $arrOutput;
}
I did a simple loop of 10,000 iterations where I pulled 2 random sites. I closed and opened a new browser for each function, and cleared the cached between run. I ran the test 3 times to get a simple average.
NOTE - The third solution failed at pulling less than 2 sites as the array_rand function has different output if it returns a set or single result. I got lazy and didn't fully implement the conditional to handle that case.
1 averaged: 12.38003755 seconds
2 averaged: 12.47702177 seconds
3 averaged: 12.7124153 seconds
mysql_query("SELECT id FROM site_info WHERE major <> 0 ORDER BY RAND() LIMIT $intNumberofSites")
EDIT
Damn, JPunyon was a bit quicker :)
Try this:
SELECT
#nv := #min + (RAND() * (#max - #min)) / #lc,
(
SELECT
id
FROM site_info
FORCE INDEX (primary)
WHERE id > #nv
ORDER BY
id
LIMIT 1
),
#max,
#min := #nv,
#lc := #lc - 1
FROM
(
SELECT #min := MIN(id)
FROM site_info
) rmin,
(
SELECT #max := MAX(id)
FROM site_info
) rmax,
(
SELECT #lc := 5
) l,
site_info
LIMIT 5
This will select a random ID on each iteration using index, in descending order.
There is slight chance, though, that you get less results that you wanted, as it gives no second chance to the missed id's.
The more percent of rows you select, the bigger is the chance.
I would simply use the rand() function (I assume you are using MySQL)...
SELECT id, rand() as rand_idx FROM site_info WHERE major <> 0 ORDER BY rand_idx LIMIT x;
I'm with JPunyon. Use ORDER BY RAND() LIMIT $N. I think you'll get a bigger performance hit from $arrResult having and shuffling so many (unused) entries than from using the MySQL RAND() function.
function getSites ( $numSites = 5 ) {
// Sanitize $numSites if necessary
$result = mysql_query("SELECT id FROM site_info WHERE major <> 0 "
."ORDER BY RAND() LIMIT $numSites");
$arrResult = array();
while ( $row = mysql_fetch_array($result,MYSQL_NUM) ) {
$arrResult[] = $row;
}
return $arrResult;
}

Categories