get a random query result and then sort it - php

I need to run an ORDER BY RAND query and then sort the resulting data set numerically. In other words I want a random set of data (in this case 7 numbers), but then I need to sort those 7 results numerically.
After this code runs:
if ($today == "Oct 31") {
$dayList = "halloween";
$stmt = $pdo->query("SELECT `rand` FROM `jukebox2014`
WHERE `class` = '$dayList' ORDER BY RAND() LIMIT 7");
}
I need to sort the 7 results.
Any ideas?
Thanks.

As simple as:
SELECT `rand`
FROM (
SELECT ... ORDER BY RAND() LIMIT 7
)
ORDER BY `rand`
Not that ORDER BY RAND() LIMIT 7 is a rather inefficient method to select random data; more efficient methods will depend on your exact data. Search Stackoverflow for many questions regarding this topic.

I think this is what you mean.
$new = array();
foreach ($results as $result) {
$new[] $result['rand'];
}
$sorted = ksort($new);

Related

Issue sorting inside while loop

I have a strange issue happening. I've got a table where I want to order the number of times a product was sold. I'll post the query, but the issue is inside the while loop.
Query:
$cat = $db->query("SELECT *, COUNT(id_produto) as quantos FROM produtos p JOIN pedidos m ON p.id_prod = m.id_produto GROUP BY id_produto ORDER BY quantos +0 DESC");
$cat->execute();
Now the loop:
while($res_cat = $cat->fetch(PDO::FETCH_ASSOC)){
$quantidade_ok = $res_cat['quantos'] * $res_cat['qtde'];
$quanti = array($quantidade_ok);
rsort($quanti);
foreach($quanti as $ord){
echo $ord."<br>";
}
The output is:
40
50
4
1
3
2
10
But I want it to be:
50
40
10
4
3
2
1
I'll be happy for any help.
You're sorting within a loop (so your sorting already happened), and you're sorting an array with just 1 value $quanti, so your sort does nothing.
You have 2 ways to approach this properly: edit your query to actually sort how you wish, or sort the PHP array before looping it.
Option 1: Edit your query
Based on your code it seems clear that you wish to sort by the product of quantos times qtde. So you can simply edit your query as follows:
SELECT *, COUNT(id_produto) as quantos
FROM produtos p JOIN pedidos m ON p.id_prod = m.id_produto
GROUP BY id_produto
ORDER BY (quantos*qtde) DESC
Option 2: Sort via PHP
If you prefer to sort via PHP as you don't want to change your query, you can simply populate a temporary array, the product of quantos times qtde as key and then use krsort to sort the array.
In code:
$array = [];
while ($res_cat = $cat->fetch(PDO::FETCH_ASSOC)) {
$key = $res_cat['quantos'] * $res_cat['qtde'];
$array[$key] = $res_cat;
}
krsort($array);
foreach ($array as $ord => $res_cat) {
echo $ord."<br>";
}
You overwrite $quanti each time so it always has a single value.
Try this:
$quanti = [];
while($res_cat = $cat->fetch(PDO::FETCH_ASSOC)) {
$quantidade_ok = $res_cat['quantos'] * $res_cat['qtde'];
$quanti[] = $quantidade_ok;
}
rsort($quanti);
foreach($quanti as $ord){
echo $ord."<br>";
}

In what way can I order data by the calculated value in my mysql statement in this situation?

I want the mysql select statement order by $combine_count, but the $combine_count is calculate from this statement. Do you have any way to achieve my goal? Appreicate.
$stm =$db->prepare("SELECT id , COUNT(user_id) as 'count'FROM sign WHERE term IN (:term_0,:term_10)");
$term_0="$term[0]";
$term_1="$term[1]";
$stm->bindParam(":term_0", $term_0);
$stm->bindParam(":term_1", $term_1);
$stm->execute();
$rows = $stm->fetchALL(PDO::FETCH_ASSOC);
foreach ($rows as $rows) {
$count=$rows['count'];
$count_percentage=round(($count/$count_user_diff)*100);
$count_key_match=round(($count/$term_match_number)*100);
$combine_count=round(($count_percentage+$count_key_match)/2);
echo $combine_count ;
}//foreach
Your code doesn't show where are $term_match_number and $count_user_diff coming from, but not from SQL for sure. So no, you cannot do this in this specific situation.
The best approach is not to echo directly, but sort array on the PHP side.
$array = array();
foreach ($rows as $rows) {
$count=$rows['count'];
$count_percentage=round(($count/$count_user_diff)*100);
$count_key_match=round(($count/$term_match_number)*100);
$combine_count=round(($count_percentage+$count_key_match)/2);
$array[] = $combine_count ;
}
sort($array); // sorts ascending (low to high)
rsort($array); // sorts descending (high to low)
I'm not sure if MySQL will handle it but it's worth a try:
SELECT s.id, s.count
FROM (SELECT id, COUNT(user_id) as 'count' FROM sign WHERE term IN (:term_0,:term_10) GROUP BY id) AS s
ORDER BY s.count

PHP How do you create an increasing number array?

as the title says i am trying to create an increasing numbers array for example:
I have a number 30 and i want to create an array out of it like
$numbers = array(6,12,18,24,30);
// then extract data from mysql
foreach( $numbers as $LIMITNUMBER ){
$query = "SELECT * FROM table WHERE id=id ORDER BY ASC LIMIT 0,".$LIMITNUMBER;
}
The 30 number above could be any number 100 or 200 but it always divides by 6 so the array first value has to be 6 and then +6 addition to the previous value.
The easiest way to do this would be to use a function implementing PHP 'range'.
Here's an example based on your question:
function incrementToMax($max) {
foreach (range(6, $max, 6) as $currentMax) {
$query = "SELECT * FROM table WHERE id=id ORDER BY ASC LIMIT 0,".$currentMax;
}
}
Example usage:
incrementToMax(60);

Php MySQL Nonrepeating Random Data Display

Let's say I've got a mySQL database: two columns: ID and names, with 100 rows.
What I'd like to do is pick out say 10 names - with no repeats - and display them in random order.
Using this code, I can pick out just one name...
<?php
include('connection.php');
$rand = rand(1, 100);
$sql = "SELECT * FROM names WHERE ID=$rand";
$result = mysql_query($sql, $sandbox);
while ($row = mysql_fetch_array ($result))
{
$name1 = $row['Name'];
echo $name1 . "<br>";
}
?>
... what would be the best code to get my 10 random nonrepeating names?
Depending on the number of rows in the table, you probably don't want to do ORDER BY RAND() as suggested by 4 other people for performance reasons:
The ORDER BY RAND() operation actually re-queries each row of your table, assigns a random number ID and then delivers the results. This takes a large amount of processing time for table of more than 500 rows.
The recommended approach extends your original idea to get 10 IDs and do a simple select query for those.
If they're sequential, you can just generate a random in that range, or retrieve all the IDs, shuffle them and request the first 10 values.
There are some other suggestions in response to this question.
The easiest way (and fastest for the database) would be to generate ten unique numbers using range and shuffle functions, and then create a query with the IN clause:
$numbers = range(1, 100);
shuffle($numbers);
$numbers = implode( ', ', array_slice($numbers, -10) );
$sql = "SELECT * FROM names WHERE ID IN ($numbers)";
// example query:
// SELECT * FROM names WHERE ID IN (63, 76, 69, 59, 9, 84, 60, 18, 23, 62)
$sql = "SELECT DISTINCT id, name FROM names ORDER BY RAND() LIMIT 10";
You will want to use ORDER BY RAND()
$sql = "SELECT * FROM names ORDER BY RAND() LIMIT 10";
$result = mysql_query($sql, $sandbox);
while ($row = mysql_fetch_array ($result)){
try this :
SELECT DISTINCT * FROM names ORDER BY RAND() LIMIT 10

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