I want to have a union of two request BUT the order by rand() of the second one doesn't work..
(select * from survey
where is_priority = 1)
union all (
select * from (
select * from survey
order by rand()
) as t );
The result look like this :
I speculate that you want is_priority = 1 first followed by the rest in random order.
If so, you should not do this with union all. Just add the right keys to the order by:
select s.*
from survey s
order by (s.is_priority = 1) desc, -- put priority 1 first in the result set
rand();
Admittedly, this puts the top priority rows in random order (amongst them). But you haven't specified an order for them (and this is a non-issue if there is only one row where the priority condition is true).
Related
I've a set of tables and I want to select a random row from within a random one of the tables.
If there's 5 tables
Table1
Table2
Table3
Table4
Table5
Each has the same data format.
I've tried the below, the first part in selecting a random table works but grabbing the info from the table is returning 0 rows.
SELECT * FROM (SELECT `cat_table_name` from `category-defines` GROUP BY `cat_table_name` LIMIT 1) AS x ORDER BY RAND() LIMIT 3
Not every task can be done in a single SQL query.
In SQL, all table names (really, all identifiers) must be fixed at the time the query is parsed. You can't write an SQL query that makes choices of tables (or columns, etc.) based on expressions evaluated during the query.
By analogy: it would be like in any other code, trying to call a function whose name is based on the return value of the function you want to call.
So you can't do what you want in one query.
You could pick the random table name in one query, then use that result as you form the next query.
SELECT `cat_table_name` from `category-defines` GROUP BY `cat_table_name` LIMIT 1
SELECT * FROM `$result_from_previous_query` ORDER BY RAND() LIMIT 3
That's the simplest solution.
Be sure to delimit your table name in back-ticks, just in case one of the table names is FROM or some other reserved keyword.
(Note: the first query above doesn't pick a random table name, it always picks the first table).
A comment above suggests a UNION of all the tables. This is what that would look like:
SELECT *
FROM
(SELECT FLOOR(RAND()*5)+1 AS table_num) AS r
JOIN (
(SELECT 1 AS table_num, * FROM my_table1 ORDER BY RAND() LIMIT 3)
UNION
(SELECT 2 AS table_num, * FROM my_table2 ORDER BY RAND() LIMIT 3)
UNION
(SELECT 3 AS table_num, * FROM my_table3 ORDER BY RAND() LIMIT 3)
UNION
(SELECT 4 AS table_num, * FROM my_table4 ORDER BY RAND() LIMIT 3)
UNION
(SELECT 5 AS table_num, * FROM my_table5 ORDER BY RAND() LIMIT 3)
) AS x USING (table_num)
But this has at least two problems:
It bears the performance cost of picking random rows from every table, just to throw away most of them. Wasteful.
You still have to know the table names in advance, so if they aren't fixed, you end up running another query before this one to get the list of table names.
That is because the main query will be executed on the result of the derived table (in this case the table name), not the actual table name.
As far as my MySQL knowledge stretches you have to split this into two queries, where the first query retrieves the table name and the second query uses the result from the first query. In PHP it would look something like this
//get a random table name
$stmt = $this->pdo->prepare('SELECT `cat_table_name` as table from `category-defines` GROUP BY `cat_table_name` LIMIT 1');
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
//select 3 random rows with the table name
$stmt = $this->pdo->prepare('SELECT * FROM '.$row['table'].' ORDER BY RAND() LIMIT 3');
$stmt->execute();
$randomRows = $stmt->fetchAll(PDO::FETCH_ASSOC); //the three random rows
SELECT *
FROM fixed_questions
WHERE examid = 15
ORDER BY RAND() LIMIT 1
UNION ALL
SELECT *
FROM questions
WHERE examid = 15
ORDER BY RAND() LIMIT 4;
This is my query and also i try this code. Is it possible to do that maybe i am not sure that union is worked or not for mysql.Mainly problem is random value one table get 1 and another table get 4 (that's my logic). How i do that
SELECT *
FROM v_fixed_questions vf
,v_questions vq ON (vf.examid = vq.examid)
I think the main problem is that to use LIMIT with a union it needs to be in actual subquery, and not at the outer level. It is possible to use LIMIT at the end of a union query, but then it would apply to the entire union result set, and not to one of the subqueries.
(
SELECT *
FROM fixed_questions
WHERE examid = 15
ORDER BY RAND()
LIMIT 1
)
UNION ALL
(
SELECT *
FROM questions
WHERE examid = 15
ORDER BY RAND()
LIMIT 4
);
This answer assumes that the fixed_questions and questions tables have the same number of columns (ideally with the same type). If not, then it might not make sense to ask for a union of the two full tables.
I have a query that selects * from my database ordering by views and limiting by 4:
SELECT * FROM articles WHERE visible = 1 ORDER BY views LIMIT 4;
But in the same query I want to find all other rows ordering by column updated_at.
I haved tryied things like this, but doesn't works:
(SELECT * FROM articles ORDER BY views DESC LIMIT 4)
UNION
(SELECT * FROM articles ORDER BY updated_at DESC);
The propose this are "pinning" the 4 hotest articles on home page and then ordering by time was updated.
Have any way to ORDER BY multiple ways in the same query without repeat the rows?
How can I do this?
Continuing with your current thinking, we can take a union of two subqueries. The first subquery is what you already included in your question, and finds the 4 more frequently viewed articles. The second subquery finds everything else. The trick here is to include in each subquery a computed field which we can use to keep track of the top 4 records from everything else. Then, we order by this computed field first, followed second by the updated_at field.
(
SELECT a.*, 1 AS label
FROM articles a
WHERE visible = 1
ORDER BY views DESC
LIMIT 4
)
UNION ALL
(
SELECT a.*, 2
FROM articles a
WHERE visible = 1
ORDER BY views DESC
LIMIT 1000000 OFFSET 4 -- the limit 1000000 is arbitrary; just use a number
) -- larger than the expected size of your table
ORDER BY
label, views, updated_at
From MySQL documentation:
... The default behavior for UNION is that duplicate rows are removed from the result. ...
And
... If ORDER BY appears without LIMIT in a SELECT, it is optimized away because it will have no effect anyway. ...
So the trick here is to use limit in the second query (it is up to you to choose the limit):
(SELECT * FROM articles WHERE visible = 1 ORDER BY views DESC LIMIT 4)
UNION
(SELECT * FROM articles WHERE visible = 1 ORDER BY updated_at DESC LIMIT 100);
The query was tested in MySQL 5.6 and 5.7.
You can use a comma to separate multiple ORDERcommands.
MySQL will order from left to right.
SELECT * FROM articles WHERE visible = 1 ORDER BY views, updated_at DESC LIMIT 4;
This is a tough one for me. I have a table that holds user notifications. There is a column "VIEWED" where 0 means it has NOT been viewed, 1 means it has.
I want to generate my list of notifications, SELECT * that is unviewed (0), however in the case that I have no un viewed notification, I don't want to just display "no notififications" I want to display a few old ones.
I'm already showing my most recent unviewed first. Is there a way to do something that shows ALL unviewed, but only a few viewed? Like below?
$QUERY= "SELECT * FROM point_member_notifications WHERE account_id='$accountid' ORDER BY viewed ASC, created_date DESC (LIMIT 10 WHERE VIEWED='1') "
Maybe you want UNION ALL, to limit the 'VIEWED' old ones, just place the limit clause inside the parentheses that enclose the SELECT
(SELECT * FROM point_member_notifications WHERE account_id='$accountid' ORDER BY viewed ASC)
UNION ALL
(SELECT * FROM point_member_notifications WHERE account_id='$accountid' WHERE VIEWED='1' created_date DESC LIMIT 10);
You could use a combination of LIMIT and UNION (or UNION ALL if you don't want to remove duplicate records). That could look like:
(SELECT *
FROM point_member_notifications WHERE account_id='$accountid' AND VIEWED='1'
ORDER BY created_date DESC LIMIT 10)
UNION ALL
SELECT *
FROM point_member_notifications WHERE account_id='$accountid' AND VIEWED='0'
ORDER BY created_date DESC
LIMIT 10
If you want to have 10 records unviewed + the 10 old records you need to put the 2nd SELECT in brackets too, otherwise the LIMIT 10 at the end will limit the whole thing. An ORDER BY viewed should not be necessary since UNION already selects the unviewed first and then the others.
If someone can help me with this. I'm trying to get 4 random fields form mysql table by position, the problem is that I need to have always 4 types and they to be random here is a sql http://sqlfiddle.com/#!2/2c76f/1
so the result should be always position 1,2,3,4 but with random id, is it possible to do this with one query or I have to have 4 deferent by any type?
Thank you in advance.
Since you cannot effectively use an ORDER BY clause in the same query with a GROUP BY (meaning that you cannot order records before grouping them), you could do something like this (here is the fiddle):
SELECT * FROM (
SELECT * FROM banners ORDER BY RAND() // shuffle the records
) AS banners GROUP BY position ORDER BY position // group and order them by position
This has the advantage that you don't have to hardcode the available positions. If you decide to add another position (5, 6, etc), this will still work. Otherwise, #Ronak Shah's UNION solution is simpler and more efficient.
Try below:
select * from (SELECT * FROM (`banners`) WHERE `published` =1 ORDER BY RAND() LIMIT 1) A
Union ALL
select * from (SELECT * FROM (`banners`) WHERE `published` =2 ORDER BY RAND() LIMIT 1) B
Union ALL
select * from (SELECT * FROM (`banners`) WHERE `published` =3 ORDER BY RAND() LIMIT 1) C
Union ALL
select * from (SELECT * FROM (`banners`) WHERE `published` =4 ORDER BY RAND() LIMIT 1) D
This will do. Tested.
http://sqlfiddle.com/#!2/392d3/11
SELECT * FROM (
SELECT * FROM banners WHERE position IN(1,2,3,4) ORDER BY RAND()
) AS banners GROUP BY position ORDER BY position;
you can omit the WHERE clause to work with every position available.