RAND returns suplicates and sometimes no values - php

I'm trying to make a quiz using mysql and php
I want to fetch questions randomly from the database
I have a truble with rand function
sometimes it seturns no value and returns also duplications
I tried to find a solution on the net but couldn't make it
this is the part of code that generate the problem
$link=mysqli_connect("localhost","root","","database");
$req="SELECT DISTINCT *
FROM `qst_s`
WHERE `id_qst` = ROUND( RAND()*49 ) + 1 AND `level` = '1' LIMIT 40";
$result=mysqli_query($link,$req);
$question=$result->fetch_assoc();
I have 50 questions in my database level 1 by the way

The simplest way of selecting random rows from the MySQL database is to use "ORDER BY RAND()" clause in the query.
SELECT * FROM `table` ORDER BY RAND() LIMIT 0,1;
The problem with this method is that it is very slow. The reason for it being so slow is that MySQL creates a temporary table with all the result rows and assigns each one of them a random sorting index. The results are then sorted and returned.
Your query would look like this:
$req="SELECT DISTINCT *
FROM `qst_s`
WHERE `level` = '1'
ORDER BY RAND()
LIMIT 40";

Related

MySQL out of sort memory

I have data in MYSQL table where i have to paginate through 4 rows at a time.
To do this I use the command below where i increase X from 0 by 4 until i reach the end of the data.
The command works for X=0 and X=4, when i reach X=8 i get the error #1038 - out of sort memory, i tried increasing it to 256K but same result.
Anybody know how to solve? Im using PHP
SELECT DISTINCT * FROM ((SELECT DISTINCT * FROM table WHERE (scope=0) AND (id='6')) UNION (SELECT DISTINCT * FROM table WHERE (scope=0) AND (id<=1000))) as total ORDER BY timestamp DESC LIMIT X,4
Not an answer, but your query seems to be functionally identical to this:
SELECT columns
, you
, actually
, want
FROM table
WHERE scope = 0
AND id<=1000
ORDER
BY timestamp DESC
LIMIT X,4

IDs not sorting

I got an odd problem. Whenever I add a new entry to the database it gives it a higher id number, but somehow sees it as a lower number and puts it below the older ones.
And when sorting DESC in PHP I also get this order, does anybody know whats going wrong here?
I use $res = mysql_query("SELECT * FROM data ORDER BY 'id' DESC");to sort them, and it gives the same order as in the pic. (Not sure why i'm being downvoted here but ok..)
Pic:
Your query is:
SELECT * FROM data ORDER BY 'id' DESC
You are sorting by the string 'id', note the syntax highlighting when not within quotes. This means you get them in a random order. Remove the '. If you meant to escape the column id you should use back-ticks:
SELECT * FROM data ORDER BY `id` DESC
When you insert data on the tables, it does not mean that the new number (or the highest) always on the lowest row. It randomly inserts the record. The only way you can sort it when you retrieve the rows in by using ORDER BY clause, example
SELECT *
FROM tableName
ORDER BY ID DESC
So assume that ID is numeric. If you're ID is stored as string then you should convert it to numeric,
SELECT *
FROM tableName
ORDER BY CAST(ID AS SIGNED) DESC
UPDATE 1
It should be
$res = mysql_query("SELECT * FROM data ORDER BY `id` DESC");
not
$res = mysql_query("SELECT * FROM data ORDER BY 'id' DESC");
what you have done was you have surrounded the ID with single quote forcing the server to read it as String and not Numeric
When you are not using any ORDER BY clause, the order in PHPMyAdmin is kind of random. Indeed, new rows could take place of old ones.
I am curious how you sort DESC in PHP though.
Don't absolutely trust in GUI web interface because sometimes (not always) it bugs with session management, however you can run SQL Queries using it.
1- To run a SQL query, click on "SQL" on the navigation bar.
2- Enter your SQL in the box provided.
SELECT *
FROM tableName
ORDER BY ID DESC
3- Click "Go".

mysql heavy RAND query alternatives

I have query that gets 10 random posts , and as you know this is very slow and heavy query, is there any alternatives to submit this query without any slow appearance?
my current rand query :
SELECT * FROM posts ORDER BY RAND() LIMIT 10
From MySQL document:
SELECT * FROM tablename ORDER BY RAND() LIMIT 1
works for small tables, but once the tables grow larger than 300,000 records or so this will be very slow because MySQL will have to process ALL the entries from the table, order them randomly and then return the first row of the ordered result, and this sorting takes long time. Instead you can do it like this (atleast if you have an auto_increment PK):
SELECT MIN(id), MAX(id) FROM tablename;
Fetch the result into $a
//php code
$id=rand($a[0],$a[1]);
SELECT * FROM tablename WHERE id>='$id' LIMIT 1

Returning random rows from mysql database without using rand()

I would like to be able to pull back 15 or so records from a database. I've seen that using WHERE id = rand() can cause performance issues as my database gets larger. All solutions I've seen are geared towards selecting a single random record. I would like to get multiples.
Does anyone know of an efficient way to do this for large databases?
edit:
Further Edit and Testing:
I made a fairly simple table, on a new database using MyISAM. I gave this 3 fields: autokey (unsigned auto number key) bigdata (a large blob) and somemore (a medium int). I then applied random data to the table and ran a series of queries using Navicat. Here are the results:
Query 1: select * from test order by rand() limit 15
Query 2: select *
from
test
join
(select round(rand()*(select max(autokey) from test)) as val from test limit 15) as rnd
on
rnd.val=test.autokey;`
(I tried both select and select distinct and it made no discernible difference)
and:
Query 3 (I only ran this on the second test):
SELECT *
FROM (
SELECT #cnt := COUNT(*) + 1,
#lim := 10
FROM test
) vars
STRAIGHT_JOIN
(
SELECT r.*,
#lim := #lim - 1
FROM test r
WHERE (#cnt := #cnt - 1)
AND RAND(20090301) < #lim / #cnt
) i
ROWS: QUERY 1: QUERY 2: QUERY 3:
2,060,922 2.977s 0.002s N/A
3,043,406 5.334s 0.001s 1.260
I would like to do more rows so I can see how query 3 scales, but at the moment, it seems as though the clear winner is query 2.
Before I wrap up this testing and declare an answer, and while I have all this data and the test environment set up, can anyone recommend any further testing?
Try:
select * from table order by rand() limit 15
Another (and possibly more efficient way) would be to join against a set of random values. This should work, if there's some contiguous integer key in the table. Here is how I would do it in postgres (My MySQL is a bit rusty)
select * from table join
(select (random()*maxid)::integer as val from generate_series(1,15)) as rnd
on rand.val=table.id;
where maxid is the highest id in table. If id has an index, then this would mean only 15 index lookup, so its very fast.
UPDATE:
Looks like there no such thing as generate_series in MySQL. My fault. We don't need it actually:
select *
from
table
join
-- this just returns 15 random numbers.
-- I need `table` here only to produce rows for rand()
(select round(rand()*(select max(id) from table)) as val from table limit 15) as rnd
on
rnd.val=table.id;
P.S. If I don't want duplicates returned, I can use (select distinct [...]) in the random generator expression.
Update: Check out the accepted answer in this question. It's pure mySQL and even deals with even distribution.
The problem with id = rand() or anything comparable in PHP is that you can't be sure whether that particular ID still exists. Therefore, you need to work with LIMIT, and that can become slow for large amounts of data.
As an alternative to that, you could try using a loop in PHP.
What the loop does is
Create a random integer number using rand(), with a scope between 0 and the number of records in the database
Query the database whether a record with that ID exists
If it exists, add the number to an array
If it doesn't, go back to step 1
End the loop when the array of random numbers contains the desired number of elements
this method could cause a lot of queries in a fragmented table, but they should be pretty fast to execute. It may be faster than LIMIT rand() in certain situations.
The LIMIT method, as outlined by #Luther, is certainly the simplest code-wise.
You could do a query with all the results or however many limited, then use mysqli_fetch_all followed by:
shuffle($a);
$a = array_slice($a, 0, 15);
For a large dataset doing
select * from table order by rand() limit 15
can be quite time and memory consuming.
If your data records happen to be numbered you can put and index on the numbering colum and do a
select * from table where no >= rand() limit 15
Or even better do the random number generation in your application and do
select * from table where no >= $rand and no <= $rand+15
If your data doesn't change too often, it might be worth to add such a numbering a column to make the selection efficient.
Assuming MySQL supports nested queries and that operations on the primary key are fast, I'd try something like
select * from table where id in (select id from table order by rand() limit 15)

displaying random row from last 100 entries?

I'm currently displaying a random row from all the entries and that works fine.
SELECT * FROM $db_table where live = 1 order by rand() limit 1
now, i'd like to limit it to the last 100 entries in the db.
every row in the db has an ID and a timestamp.
it's a small database, so overhead-minimization is not a priority.
thanks!
EDIT:
Still can't get it running.. I get a mysql_fetch_array error:
"Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource
Here's all of my code:
<?php $sql = "SELECT * FROM
(SELECT * FROM $db_table ORDER BY $datetime DESC LIMIT 100)
ORDER BY rand() LIMIT 1";
$query = mysql_query($sql);
while($row = mysql_fetch_array($query)) {
echo "".$row['familyname']."";
} ?>
Thanks again!
This is what I came up with off the top of my head. I've tested it and it works in SQLite, so you shouldn't have much trouble with MySQL. The only change was that SQLite's random function is random() not rand():
SELECT * FROM
(SELECT * FROM $db_table ORDER BY $timestamp DESC LIMIT 100)
ORDER BY rand() LIMIT 1
This page has a pretty detailed writeup on how to optimize an ORDER BY RAND()-type query. It's actually too involved for me to explain adequately on SO (also, I don't fully understand some of the SQL commands used, though the general concept makes sense), but the final optimized query makes use of several optimizations:
First, ORDER BY RAND(), which uses a filesort algorithm on the entire table, is dropped. Instead, a query is constructed to simply generate a single random id.
At this stage, an index scan is being used, which is even less efficient than a filesort in many cases, so this is optimized away with a subquery.
The WHERE clause is replaced with a JOIN to reduce the number of rows fetched by the outer SELECT, and the number of times the subquery is executed, to just 1.
In order to account for holes in the ids (from deletions) and to ensure an equal distribution, a mapping table is created to map row numbers to ids.
Triggers are used to automatically update & maintain the mapping table.
Lastly, stored procedures are created to allow multiple rows to be selected at once. (Here, ORDER BY is reintroduced, but used only on the result rows.)
Here are the performance figures:
Q1. ORDER BY RAND()
Q2. RAND() * MAX(ID)
Q3. RAND() * MAX(ID) + ORDER BY ID
100 1.000 10.000 100.000 1.000.000
Q1 0:00.718s 0:02.092s 0:18.684s 2:59.081s 58:20.000s
Q2 0:00.519s 0:00.607s 0:00.614s 0:00.628s 0:00.637s
Q3 0:00.570s 0:00.607s 0:00.614s 0:00.628s 0:00.637s

Categories