Can I use MySQL functions in the LIMIT offset - mysql

Can I use MySQL functions in the LIMIT offset?
Like:
SELECT * FROM sites WHERE ... LIMIT FLOOR(1 + RAND() * (SELECT COUNT(*) FROM sites)) , 1

No, you can't do that directly. LIMIT and OFFSET values must be constants.
Citation from the MySQL docs:
The LIMIT clause can be used to constrain the number of rows returned by the SELECT statement. LIMIT takes one or two numeric arguments, which must both be nonnegative integer constants (except when using prepared statements).
You can use prepared statements and variables, though:
SELECT #offset:=FLOOR(1 + RAND() * COUNT(*)) FROM sites;
PREPARE STMT FROM 'SELECT * FROM sites WHERE ... LIMIT ?, 1';
EXECUTE STMT USING #offset;

Related

Using arithmetic operators and/or brackets in a LIMIT clause

I have the following small subquery in query in stored procedure.
(select f_cnt from results limit (i-1)*10,i*10)
But there is the syntax error:
"(" is not valid at this position, expecting an identifier
So, the question is: do I have a possibility to use brackets and/or arithmetic operators in LIMIT clause?
Documentation says I can use local variables in LIMIT clause within stored procedure. Do I really need to declare and set different variables for this case?
Just in case, link for the code of stored procedure.
You can't do arithmetic at that point
So do instead
SET #sql := CONCAT("SELECT * FROM TEsttable WHERE id In(select f_cnt from results limit ",(i-1) * 10,",",i*10,")");
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Which will produce
SELECT * FROM TEsttable WHERE id In(select f_cnt from results limit 90,100)

Limit by RAND without random order

I am trying to select records in order but limited randomly.
SELECT * FROM tm_winners WHERE paid_out=0 ORDER BY DESC LIMIT RAND(4,8)
However it seems I cannot have a random limit of rows. I am bassiclty trying to grab between 4 and 8 rows but NOT randomly, just limited randomly.
You'll have to use a prepared statement to use a variable amount in the LIMIT clause.
SET #lrand = FLOOR(4 + RAND() * (8 - 4));
PREPARE STMT FROM 'SELECT * tm_winners WHERE paid_out=0 ORDER BY DESC LIMIT ?';
EXECUTE STMT USING #lrand;
Personally, even if its not the cleanest way, Id set a var previous to the select statement and Limit by the var.
$randomVar = rand(4, 8);
$sql = 'SELECT * FROM tm_winners WHERE paid_out=0 ORDER BY DESC LIMIT 0,'.$randomVar;
Clearly if you wanted randomness within the actual results, you could clearly reintroduce the rand() within the statement
Hope it helps
Will

Issue with using MAX()

I was answering a question on SO that encountered this issue.
Why I can't use MAX() within a LIMIT?
SELECT *
FROM table
ORDER BY id DESC
LIMIT 0, MAX(id)
Or
SELECT *, MAX(id) AS m
FROM table
ORDER BY id DESC
LIMIT 0, m
Both give a similar syntax error:
1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'MAX(id)' at line 4
Wanted query:
SELECT *
FROM table
ORDER BY id DESC
LIMIT 0, MAX(id)-5
MAX() is an aggregate function over the result rows, but LIMIT is a clause that constrains the number of result rows. In short, you cannot use a function that depends on the result rows while you are still determining which rows will be in the result -- that simply doesn't make any sense.
According to the documentation, LIMIT arguments must either be integer constants or parameters of a prepared query (emphasis mine):
LIMIT takes one or two numeric arguments, which must both be nonnegative integer constants (except when using prepared statements).
The syntax specification simply does not allow a column or function to be used. You would have to pre-compute the value and then use it in a prepared query (or by string substitution, though I would avoid that).
Based on the query you gave in your question:
SELECT *
FROM table
ORDER BY id DESC
LIMIT 0, MAX(id)-5
I suspect that this is the query you actually want:
SELECT *
FROM table
WHERE id <= (SELECT MAX(id) FROM table) - 5
ORDER BY id DESC
Even if it were valid, the first query you gave will not do what you expect if there are gaps in the sequence of the id column (for example, if a row was deleted).
An alternative if you want to get your desired result is to create a dynamic sql.
SET #maxID = (SELECT MAX(ID) FROM tableName);
SET #sql = CONCAT('SELECT *
FROM tableName
ORDER BY ID DESC
LIMIT 0,', #maxID);
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
The function MAX cannot be used on the limit.
Here is the documentation:
http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_max
The count argument in the limit should be a value so you cannot execute that in one step. Here is the select documentation:
http://dev.mysql.com/doc/refman/5.0/en/select.html
You can do is:
SET #a=(SELECT MAX(ID) FROM table);
PREPARE STMT FROM 'SELECT * FROM table ORDER BY id DESC LIMIT 0, ?';
EXECUTE STMT USING #a;
So it is a tree step here. Get max in a variable. Prepare a statement and than execute it applying the variable to the statement.
You can use the HAVING clause with MAX(id) to obtain your desired result
SELECT *
FROM table1
HAVING id<((SELECT Max(id) FROM table1)-5)
ORDER BY id DESC
Fiddle http://sqlfiddle.com/#!2/16e50/3
ISSUE IS WIth your query::
Select
*
from
myTable,
(SELECT MAX(id) as n
FROM table) temp
ORDER BY id DESC
LIMIT 0, temp.n

SQL / Limit (another query)

I have 2 tables. One of them holds the limit numbers.
And i am trying to run an sql like
SELECT * FROM table WHERE x='1' LIMIT (another query)
The Select TOP clause limit the number of rows. You can put an expression in it
http://msdn.microsoft.com/en-us/library/ms189463.aspx
In MySQL you can't do that using regular querys.
LIMIT clause does not allow variables. LIMIT clause only accepts constant numbers.
You have to use Stored Procedures instead regular querys.
Try use something like this inside an SP code:
DECLARE offset bigint
SELECT your_field INTO offset FROM your_table where your_conditions
SELECT * FROM other_table LIMIT offset;
Use a prepared statement:
PREPARE stmt FROM "SELECT * FROM table WHERE x='1' LIMIT ?";
SET #limit = (another query);
EXECUTE stmt USING #limit;
\You do what I have suggested to several people is generate SQL from a select statement and then execute that. Here is my solution:
SELECT "select * from other_table LIMIT ", your_field
FROM your_table where your_conditions
Then execute this new SQL. Of course this works best in UNIX/Linux and with a scripting language.

Fetching RAND() rows without ORDER BY RAND() in just one query

Using RAND() in MySQL to get a single random row out of a huge table is very slow:
SELECT quote FROM quotes ORDER BY RAND() LIMIT 1
Here is an article about this issue and why this is the case.
Their solution is to use two queries:
SELECT COUNT(*) AS cnt FROM quotes
- Use result to generate a number between 0 and COUNT(*)
SELECT quote FROM quotes LIMIT $generated_number, 1
I was wondering, whether this would be possible in just one query.
So my approach was:
SELECT * FROM quotes
LIMIT (
ROUND(
(SELECT COUNT(*) FROM quotes) * RAND()
)
), 1
But it seams MySQL does not allow any logic within Limit.
Though I can not find any information about this topic, whether this is true.
So my Questions:
How can I use RAND() within LIMIT?
Do you know of any other way to
solve this with just one query?
Is there a reason why a stored procedure cannot be used to create a prepared statement?
DELIMITER //
DROP PROCEDURE IF EXISTS rand_quote//
CREATE PROCEDURE rand_quote()
BEGIN
SET #rand := ROUND((SELECT COUNT(*) FROM quotes) * RAND());
SET #sql := CONCAT('SELECT * FROM quotes LIMIT ', #rand, ', 1');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END;
//
DELIMITER ;
I just figgured this one, that seams like a solution:
SELECT * FROM quotes
WHERE quotes_id = ROUND(
(SELECT COUNT(*) FROM quotes) * RAND()
)
LIMIT 1
But it will work only if quotes_id has no gaps.
I solved the problem by checking max id. Then i made php loop of rand(0, max_id) which checks if object exists. Done.
Much faster then previous ordering by rand.