Different ORDER BY for each SELECT in a UNION with MySQL - php

Using PHP and MySQL, is there a way to use a different ORDER BY for each of the SELECT statements in a UNION?
SELECT * FROM the_table WHERE color = 'blue' ORDER BY price ASC LIMIT 5
UNION ALL
SELECT * FROM the_table WHERE color = 'red' ORDER BY RAND() LIMIT 10
The above statement does not work. It seems you can only do an ORDER BY on the final result set. Is there a way to do an ORDER BY on the first SELECT then a different ORDER BY on the second SELECT using UNION?

(SELECT * FROM the_table WHERE color = 'blue' ORDER BY price ASC LIMIT 5)
UNION ALL
(SELECT * FROM the_table WHERE color = 'red' ORDER BY RAND() LIMIT 10)

Please note that this does not work if you don't specify a LIMIT (though you can specify a very large dummy limit). See mysql documentation (13.2.7.3. UNION Syntax):
"Use of ORDER BY for individual SELECT statements implies nothing about the order in which the rows appear in the final result because UNION by default produces an unordered set of rows...
"To cause rows in a UNION result to consist of the sets of rows retrieved by each SELECT one after the other, select an additional column in each SELECT to use as a sort column and add an ORDER BY following the last SELECT:
"(SELECT 1 AS sort_col, col1a, col1b, ... FROM t1)
UNION
(SELECT 2, col2a, col2b, ... FROM t2) ORDER BY sort_col;
To additionally maintain sort order within individual SELECT results, add a secondary column to the ORDER BY clause:
"(SELECT 1 AS sort_col, col1a, col1b, ... FROM t1)
UNION
(SELECT 2, col2a, col2b, ... FROM t2) ORDER BY sort_col, col1a;"

Related

MySQL Rand() not in subquery?

I have a set of tracks that need to be played,
There are something like 70 tracks in the database, and my script need to generate a new ID to play in order to start the next track.
Current query: ($row['v_artist'] is the current artist playing)
SELECT *
FROM t_tracks
WHERE v_artist NOT LIKE '%".$row['v_artist']."%'
ORDER BY RAND()
LIMIT 1;
Now I wish to add a subquery to rand() so that it picks a random id, but not from the first 50 (NOT IN?)
Subquery:
SELECT *
FROM `t_playlist`
ORDER BY pl_last_played DESC
LIMIT 50, 1
How can I get a random ID from t_tracks that does not exist in the query for t_playlist?
Conceptually, I think you want this:
SELECT *
FROM t_tracks
WHERE v_artist NOT LIKE '%".$row['v_artist']."%' AND
track_id NOT IN (SELECT track_id FROM t_playlist ORDER BY pl_last_played DESC LIMIT 50)
ORDER BY RAND()
LIMIT 1;
However, MySQL doesn't permit LIMIT in some subqueries, so use LEFT JOIN instead:
SELECT t.*
FROM t_tracks t LEFT JOIN
(SELECT track_id
FROM t_playlist
ORDER BY pl_last_played DESC
LIMIT 50
) p
ON t.track_id = p.track_id
WHERE t.v_artist NOT LIKE '%".$row['v_artist']."%' AND
p.track_id IS NULL
ORDER BY RAND()
LIMIT 1;

How do I select random from a table 25 questions from each categories,I have 4 categories?

I have a table with questions, i want to select random from each category 25 questions.
The table "questions" looks like that:
The categories are: C PC P PP
id question category answer1 answer2 answer3
The easiest way is to use union all with a subquery for each category:
(select * from questions where category = 'C' order by rand() limit 25)
union all
(select * from questions where category = 'PC' order by rand() limit 25)
union all
(select * from questions where category = 'P' order by rand() limit 25)
union all
(select * from questions where category = 'PP' order by rand() limit 25)
If you have a lot of categories or a whole lot of questions (hundreds of thousands or more), then you might want a query that performs better. But for lesser amounts of data, this is probably fine.
I want to emphasize that union all is better for such a query than union. union removes duplicates adding addition processing that should not be needed in this case.
Not sure what you mean by i want to select random from each category but you can just fetch 25 question from each category and union them like
select question from sometable where category = 'C' limit 25
union
select question from sometable where category = 'PC' limit 25
union
select question from sometable where category = 'P' limit 25
union
select question from sometable where category = 'PP' limit 25
Used an outer query with ORDER BY RAND() to select records which are selected from the inner query in a random fashion.
SELECT * FROM
(
(SELECT * FROM questions WHERE category='C' ORDER BY RAND() LIMIT 25) T1
UNION
(SELECT * FROM questions WHERE category='PC' ORDER BY RAND() LIMIT 25) T2
UNION
(SELECT * FROM questions WHERE category='P' ORDER BY RAND() LIMIT 25) T3
UNION
(SELECT * FROM questions WHERE category='PP' ORDER BY RAND() LIMIT 25) T4
) TT
ORDER BY RAND()
This is a HINT you can work upon.
SYNTAX:
Use this query :
SELECT * FROM `table` WHERE id >= (SELECT FLOOR( MAX(id) * RAND()) FROM `table` )
In place of 'table' put 'questions'.(This is because you have a table name 'questions').
Hope this will help.

how to order resultset based on fields from two tables

I have two tables one for topic_likes & one for user_comments.I must get recent updates of like & comment from this tables.Given below is the sql :-
SELECT (required fields...)
LEFT JOIN topic_likes AS TL ON (TL.delete_status=0 AND TL.user_id!=$user_id)
LEFT JOIN user_comments AS UC ON (UC.delete_status=0 AND UC.user_id!=$user_id)
WHERE
(TL.created_date >= '$lastLogin' OR UC.created_date >= '$lastLogin'
ORDER BY UC.created_date desc,TL.created_date desc
LIMIT $limit
I have given order by two fields from two tables(UC.created_date, TL.created_date)
But it does not order the resultset based on created_date from topic_likes.It only orders the results based on user_comments table
But if I removed the limit condition it gives correct results...!!
Any suggestion appreciated
This is a strange approach you're taking. If you want to display user's likes and comments using a single query you should UNION the results. Example:
SELECT * FROM
(
SELECT id, `date`, 'like' as `type` FROM topic_likes
UNION
SELECT id, `date`, 'comment' as `type` FROM user_comments
) a order by a.date DESC limit 5;
The result should be similar to this:
But there are limitations. The number of columns from each subquery must match.

Not In mysql subquery Limit

Does anybody have any ideas how I can get around a #1235 - This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery' error?
My query is below ( I've read that I can upgrade mysql but this isn't possible):
$query = #mysql_query("SELECT * FROM posts
WHERE postid NOT IN
( SELECT postid FROM log
ORDER BY posted DESC
LIMIT 10)
ORDER BY (RAND() * Multiplier)
LIMIT 1");
According to this bug, you can use this ugly workaround:
SELECT * FROM t1 WHERE s1 NOT IN
(SELECT * FROM (SELECT s2 FROM t2 ORDER BY s1 LIMIT 1) AS alias)
You can rewrite your query using JOIN:
SELECT *
FROM posts NATURAL LEFT JOIN (
SELECT postid FROM log ORDER BY posted DESC LIMIT 10
) t
WHERE t.postid IS NULL
ORDER BY RAND()
LIMIT 1
Be aware, however, that ORDER BY RAND() is very expensive. Not only must a random value be calculated for each record, but then a sort must be performed on the results. Indexes are of no use.
You would fare better if you had a column col containing unique integers, then with an index on col you can very rapidly obtain a random record with:
SELECT *
FROM posts NATURAL LEFT JOIN (
SELECT postid FROM log ORDER BY posted DESC LIMIT 10
) t JOIN (
SELECT RAND() * MAX(col) AS rand FROM posts
) r ON posts.col >= r.rand
WHERE t.postid IS NULL
LIMIT 1
Note that the uniformity of such "randomness" will depend on the distribution of the integers within col after any other filtering has taken place.

Limit the amount of results from mySQL conditionally?

Here is my query:
SELECT * FROM Photos WHERE Event_ID IN ($eventidstring)
I know I can limit the total amount of results from this query using LIMIT 5
I need the Limit the amount of results Per value in $eventidstring.
So if $eventidstring = 23,41,23*
*And there are 10 results WHERE Event_ID = 23, I want to limit this amount to 5. The same for all the other values in $eventidstring.
You may have some joy doing something similar to Oracle's RANK by PARITION in MySQL.
Sadly this feature is not available in MySQL though you can work around it using this method
Dump that in an inline view and then select those rows with rank <= 5;
Hence:
SELECT t.* FROM (
SELECT (
CASE Event_id
WHEN #curEventId
THEN #curRow := #curRow + 1
ELSE #curRow := 1 AND #curEventId := Event_Id END
) AS rank,
p.*
FROM Photos p INNER JOIN (SELECT #curRow := 0, #curEventId := '') r
ORDER BY p.Event_Id DESC
) t
WHERE t.rank <= 5 ORDER BY t.Event_Id asc;
Consider how you are going to 'choose' the top five by Event_Id too. You can always add in more after the ORDER BY p.Event_Id DESC to decide this.
I take it you're writing that query somewhere inside your PHP, so you need to split the $eventidstring into it's component values, form a SELECT for each and UNION all after the first one.
You sould do this with a loop of some sort, and concatenate the query strings in the loop...
If I understand correctly and you want to get five of each, you can use this:
(SELECT * FROM Photos WHERE Event_ID = 23 LIMIT 5)
UNION
(SELECT * FROM Photos WHERE Event_ID = 41 LIMIT 5)
UNION
(SELECT * FROM Photos WHERE Event_ID = ... LIMIT 5)
...
Maybe with a SELECT UNION but you need a new select for each value:
SELECT * FROM Photos WHERE Event_ID = 23 LIMIT 5
UNION SELECT * FROM Photos WHERE Event_ID = 41 LIMIT 5
UNION SELECT ...

Categories