Need assistance with simplifying this SQL query to possibly a single SELECT:
(SELECT * FROM `deals`
WHERE category_id = 1
ORDER BY id desc
LIMIT 10)
UNION
(SELECT * FROM `deals`
WHERE category_id = 2
ORDER BY id desc
LIMIT 10)
UNION
(SELECT * FROM `deals`
WHERE category_id = 4
ORDER BY id desc
LIMIT 10)
UNION
(SELECT * FROM `deals`
WHERE category_id = 5
ORDER BY id desc
LIMIT 10)
UNION
(SELECT * FROM `deals`
WHERE category_id = 6
ORDER BY id desc
LIMIT 10)
UNION
(SELECT * FROM `deals`
WHERE category_id = 8
ORDER BY id desc
LIMIT 10)
UNION
(SELECT * FROM `deals`
WHERE category_id = 9
ORDER BY id desc
LIMIT 10)
UNION
(SELECT * FROM `deals`
WHERE category_id = 10
ORDER BY id desc
LIMIT 10)
UNION
(SELECT * FROM `deals`
WHERE category_id = 17
ORDER BY id desc
LIMIT 10)
I've been told to try using GROUP BY and HAVING. However, any query I tried didn't work in the slightest...
Any help will be greatly appreciated!
EDIT - apologies, forgot to mention database engine is MySQL
You can condense this down with a window function to limit each group bucket to 10.
SELECT
*
FROM
(
SELECT
*,
ROW_NUMBER() OVER PARTITION BY(category_id ORDER BY id DESC) AS GroupOrder
FROM `deals`
WHERE category_id BETWEEN 1 AND 10
)AS X
WHERE
GroupOrder<=10
I'm not sure, I need to know if you need that limit of 10, is this like take top 10 of all those things?
if not then
SELECT * FROM `deals`
WHERE category_id between 0 and 10 or category_id=17
ORDER BY category_id asc, id desc
For older versions of MySQL without the windowing functions, here is the code.
SELECT T1.ID, T1.Category_ID, T1.Name
FROM (
SELECT #row_num := IF(#prev_value=concat_ws('',t.Category_ID),#row_num+1,1) AS RowNumber
,t.*
,#prev_value := concat_ws('',t.Category_ID)
FROM data t,
(SELECT #row_num := 1) x,
(SELECT #prev_value := '') y
ORDER BY t.Category_ID
) T1
WHERE T1.RowNumber < 10
AND T1.Category_ID IN (1,2,3,4,5,6,7,8,9,10)
You will need to add the necessary field names to the other select.
This uses the technique described here
Related
Can I somehow combine these two queries into one in MySQL 5.7 without global variables and stored procedures?
define p1_id, p2_id int;
...
insert into Cards_in_game_decks select * from Cards_in_decks where Cards_in_decks.player_id=p1_id order by rand() limit 10;
insert into Cards_in_game_decks select * from Cards_in_decks where Cards_in_decks.player_id=p2_id order by rand() limit 10;
You just need to do a union:
insert into Cards_in_game_decks select * from (
(select * from Cards_in_decks where Cards_in_decks.player_id=p1_id order by rand() limit 10)
union all
(select * from Cards_in_decks where Cards_in_decks.player_id=p2_id order by rand() limit 10)
) random_cards
fiddle
To get up to 10 cards per player for up to 6 players listed in a Players table, you just have to get repetitive:
insert into Cards_in_game_decks select * from (
(select * from Cards_in_decks where Cards_in_decks.player_id=(select id from Players where game_id=1 order by id limit 0,1) order by rand() limit 10)
union all
(select * from Cards_in_decks where Cards_in_decks.player_id=(select id from Players where game_id=1 order by id limit 1,1) order by rand() limit 10)
union all
(select * from Cards_in_decks where Cards_in_decks.player_id=(select id from Players where game_id=1 order by id limit 2,1) order by rand() limit 10)
union all
(select * from Cards_in_decks where Cards_in_decks.player_id=(select id from Players where game_id=1 order by id limit 3,1) order by rand() limit 10)
union all
(select * from Cards_in_decks where Cards_in_decks.player_id=(select id from Players where game_id=1 order by id limit 4,1) order by rand() limit 10)
union all
(select * from Cards_in_decks where Cards_in_decks.player_id=(select id from Players where game_id=1 order by id limit 5,1) order by rand() limit 10)
) random_cards
fiddle
There are likely much better ways to do this in mysql 8.
First of all, good news: I have only 1 table.
Now, I have 3 similar queries:
SELECT id FROM (
SELECT * FROM my_table
ORDER BY columnA
DESC LIMIT 600
) AS aliasA
WHERE (revenue > 10000);
SELECT id FROM (
SELECT * FROM my_table
ORDER BY columnB
DESC LIMIT 400, 999999999999
) AS aliasB
WHERE (revenue > 10000);
SELECT id FROM (
SELECT * FROM my_table
ORDER BY columnC
DESC LIMIT 800;
) AS aliasC
WHERE (revenue > 10000);
Notice the WHERE clause are the same.
Is there a way to combine these 3 queries so that I can search from the intersection of the 3 sub-queries (find rows that match all 3 sub-queries, and also my WHERE clause)?
By the way, if my single queries (before combining) can be simplified, please let me know.
Thanks!
SELECT ID FROM
(SELECT id FROM (
SELECT *
FROM my_table
WHERE (revenue > 10000)
ORDER BY columnA DESC
LIMIT 600
) AS aliasA) AS a
INNER JOIN (
SELECT id FROM (
SELECT *
FROM my_table
WHERE (revenue > 10000)
ORDER BY columnB DESC
LIMIT 400, 999999999999
) AS aliasB
)
AS b ON a.id = b.id
INNER JOIN (
SELECT id FROM (
SELECT *
FROM my_table
WHERE (revenue > 10000)
ORDER BY columnC DESC
LIMIT 800
) AS aliasC
)
AS c ON a.id = c.id AND b.id = c.id
then you can search this query
In your case, the best solution will be JOIN USING (id):
SELECT id
FROM (SELECT id FROM my_table ORDER BY columnA DESC LIMIT 600)
aliasA
JOIN (SELECT id FROM my_table ORDER BY columnB DESC LIMIT 400, 999999999999)
aliasB USING (id)
JOIN (SELECT id FROM my_table ORDER BY columnC DESC LIMIT 800)
aliasC USING (id)
JOIN my_table USING (id)
WHERE (revenue > 10000);
If you didn't have LIMIT in your subquery, you could have used id IN:
SELECT id FROM my_table
WHERE (
id IN (SELECT id FROM my_table ORDER BY columnA DESC LIMIT 600)
AND
id IN (SELECT id FROM my_table ORDER BY columnB DESC LIMIT 400, 999999999999)
AND
id IN (SELECT id FROM my_table ORDER BY columnC DESC LIMIT 800)
AND
revenue > 10000
)
However LIMIT is not supported in subquery, so you can't do it.
If you were using other SQLs, you could have used INTERSECT:
(SELECT id FROM my_table ORDER BY columnA DESC LIMIT 600)
INTERSECT
(SELECT id FROM my_table ORDER BY columnB DESC LIMIT 400, 999999999999)
INTERSECT
(SELECT id FROM my_table ORDER BY columnC DESC LIMIT 800)
INTERSECT
(SELECT id FROM my_table WHERE revenue > 10000)
However MySql doesn't support INTERSECT.
You can use INNER JOIN on key: id then put where clause at the end. All 3 queries is making use of revenue > 10000 and it can be combined in onenwhere clause since all revenue columnn comes from the same table.
select t1.id from (
SELECT id, revenue FROM (
SELECT * FROM my_table
ORDER BY columnA
DESC LIMIT 600
) AS aliasA) as t1
INNER JOIN (
SELECT id FROM (
SELECT * FROM my_table
ORDER BY columnB
DESC LIMIT 400, 999999999999
) AS aliasB) as t2 on t1.id= t2.id
INNER JOIN (
SELECT id, revenue FROM (
SELECT * FROM my_table
ORDER BY columnC
DESC LIMIT 800;
) AS aliasC) as t3 on t1.id =t3.id
WHERE (t1.revenue > 10000);
I'm attempting a mysql union but nothing is returned? Below is an example. In total there is 16 select queries
$url_array = array(
"euro-gbp","euro-aud","euro-usd",
"euro-jpy","gbp-jpy","euro-cad",
"usd-cad","usd-jpy","cad-jpy",
"gbp-usd","aud-usd","gbp-cad",
"aud-cad","aud-jpy","aud-nzd",
"euro-nzd","gbp-aud","gbp-nzd",
"nzd-usd","nzd-cad","nzd-jpy");
foreach($url_array as $urls) {
$sql[]= "SELECT *
FROM `data_analysis_child`
WHERE type='".$urls."'
ORDER BY id DESC
LIMIT 2";
}
$sql = implode(" UNION ",$sql);
The sql result
SELECT * FROM `data_analysis_child` WHERE type='euro-gbp' ORDER BY id DESC LIMIT 2 UNION SELECT * FROM `data_analysis_child` WHERE type='euro-aud' ORDER BY id DESC LIMIT 2 UNION SELECT * FROM `data_analysis_child` WHERE type='euro-usd' ORDER BY id DESC LIMIT 2 UNION SELECT * FROM `data_analysis_child` WHERE type='euro-jpy' ORDER BY id DESC LIMIT 2 UNION SELECT * FROM `data_analysis_child` WHERE type='gbp-jpy' ORDER BY id DESC LIMIT 2 UNION SELECT * FROM `data_analysis_child` WHERE type='euro-cad' ORDER BY id DESC LIMIT 2 UNION SELECT * FROM `data_analysis_child` WHERE type='usd-cad' ORDER BY id DESC LIMIT 2 UNION SELECT * FROM `data_analysis_child` WHERE type='usd-jpy' ORDER BY id DESC LIMIT 2 UNION SELECT * FROM `data_analysis_child` WHERE type='cad-jpy' ORDER BY id DESC LIMIT 2 UNION SELECT * FROM `data_analysis_child` WHERE type='gbp-usd' ORDER BY id DESC LIMIT 2 UNION SELECT * FROM `data_analysis_child` WHERE type='aud-usd' ORDER BY id DESC LIMIT 2 UNION SELECT * FROM `data_analysis_child` WHERE type='gbp-cad' ORDER BY id DESC LIMIT 2 UNION SELECT * FROM `data_analysis_child` WHERE type='aud-cad' ORDER BY id DESC LIMIT 2 UNION SELECT * FROM `data_analysis_child` WHERE type='aud-jpy' ORDER BY id DESC LIMIT 2 UNION SELECT * FROM `data_analysis_child` WHERE type='aud-nzd' ORDER BY id DESC LIMIT 2 UNION SELECT * FROM `data_analysis_child` WHERE type='euro-nzd' ORDER BY id DESC LIMIT 2 UNION SELECT * FROM `data_analysis_child` WHERE type='gbp-aud' ORDER BY id DESC LIMIT 2 UNION SELECT * FROM `data_analysis_child` WHERE type='gbp-nzd' ORDER BY id DESC LIMIT 2 UNION SELECT * FROM `data_analysis_child` WHERE type='nzd-usd' ORDER BY id DESC LIMIT 2 UNION SELECT * FROM `data_analysis_child` WHERE type='nzd-cad' ORDER BY id DESC LIMIT 2 UNION SELECT * FROM `data_analysis_child` WHERE type='nzd-jpy' ORDER BY id DESC LIMIT 2
How do i solve? Is there a better way to restructure this query?
if you need order by and limit for each select you should use () around each select
foreach($url_array as $urls) {
$sql[]= " ( SELECT * FROM `data_analysis_child` WHERE type='".$urls."' ORDER BY id DESC LIMIT 2 )";
break;
}
Just a little addition to do that. If you are getting ZERO results then use UNION ALL instead of only UNION. Actually duplicate records are avoided in UNION but with UNION ALL you will get everything that a query is returning. give it a try !!!
The documentation of UNION clearly explains:
To apply ORDER BY or LIMIT to an individual SELECT, place the clause inside the parentheses that enclose the SELECT:
(SELECT a FROM t1 WHERE a=10 AND B=1 ORDER BY a LIMIT 10)
UNION
(SELECT a FROM t2 WHERE a=11 AND B=2 ORDER BY a LIMIT 10);
It also says:
Note
Previous versions of MySQL may permit such statements without parentheses. In MySQL 5.7, the requirement for parentheses is enforced.
If you are using MySQL 5.7 or newer, the code doesn't return anything because the query doesn't run. You should check the type of the returned value (the query functions return FALSE when the query is invalid) and the error message.
You could do this:
SELECT *
FROM (
SELECT #rank := if (#last_type=type, #rank+1, 1) as type_rank, *
FROM data_analysis_child
WHERE type IN ('euro-gbp', 'euro-aud', 'euro-usd'....)
ORDER BY id DESC
)
WHERE type_rank<=2;
But the performance will suck.
Depending on how the indexes are configured and assuming that your id column is unique this might be better:
SELECT 'first', dac1.*
FROM data_analysis_child dac1
WHERE dac1.id IN (
SELECT MAX(dac2.id)
FROM data_analysis_child dac2
WHERE dac2.type IN ('euro-gbp', 'euro-aud', 'euro-usd'....)
GROUP BY dac2.type
)
UNION
SELECT 'second', dac3.*
FROM data_analysis_child dac3
WHERE dac3.id IN (
SELECT MAX(dac4.id)
FROM data_analysis_child dac4
WHERE dac4.type IN ('euro-gbp', 'euro-aud', 'euro-usd'....)
AND dac4.id NOT IN (
SELECT MAX(dac5.id)
FROM data_analysis_child dac5
WHERE dac5.type IN ('euro-gbp', 'euro-aud', 'euro-usd'....)
GROUP BY dac5.type
)
GROUP BY dac4.type
)
But it does not scale well to getting the top N values for each type.
A better solution would be to to flag the state of the first or second rank as an attribute on the record and filter directly.
I have table called "users" and need to select 2 rows before and after specific row, sorted by users.score ASC
users table (structure):
id name score
1 John 2
2 Sara 1
3 san 3
4 test 2
5 jery 5
6 simon 6
7 bob2 7
8 jack 4
9 man 2
for example: need to select 2 rows before and after users.id = 5 order by users.score
result should be like:
id name score
3 san 3
8 jack 4
5 jery 5
6 simon 6
7 bob2 7
thanks,
Using union all and subqueries to limit the records should do it:
select * from users where id = 5
union all (
select * from users
where score < (select score from users where id = 5)
order by score desc limit 2
)
union all (
select * from users
where score > (select score from users where id = 5)
order by score asc limit 2
)
order by score
Sample SQL Fiddle
Edit: I think a better method is to number the rows according to score and then select the rows with number -2 and +2 from the rows of id 5:
select id, name, score
from (select
t.*, #rownum1 := #rownum1 + 1 as rank
from users t, (select #rownum1 := 0) r
order by score
) a,
(select rank from (
select t.*,
#rownum := #rownum + 1 as rank
from users t, (select #rownum := 0) r
order by score
) t
where id = 5
) b
where b.rank between a.rank -2 and a.rank+2
order by score;
Sample SQL Fiddle
Perhaps using union all
(
select * from users where id < 5 order by score limit 2
)
union all
(
select * from users where id > 5 order by score limit 2
)
(SELECT x.* FROM users x JOIN users y ON y.score <= x. score WHERE y.id = 5 ORDER BY score LIMIT 3)
UNION
(SELECT x.* FROM users x JOIN users y ON y.score >= x. score WHERE y.id = 5 ORDER BY score DESc LIMIT 3)
[ORDER BY score] ;
http://www.sqlfiddle.com/#!9/45c22/42
I just write the query, based on "jpw" solution (many thanks to him)
select * from users where id = 5
union all (
select * from users
where id in (select id from users where score < (select score from users u where u.id = 5) order by score ASC)
order by score desc limit 2
)
union all (
select * from users
where id in (select id from users where score > (select score from users u where u.id = 5) order by score ASC)
order by score ASC limit 2
)
order by score
Selecting arbitrarily ordered rows before and after a specific id
SET #j = 0;
SET #i = 0;
SELECT *
FROM (
SELECT id, col1, col2, ..., #j:=#j+1 AS pos
FROM `table`
WHERE col1=... ORDER BY col1 DESC, col2 ASC
) AS zz
WHERE (
SELECT position
FROM (
SELECT id AS id2, #i:=#i+1 AS position
FROM `table`
WHERE col1=... ORDER BY col1 DESC, col2 ASC
) AS zz
WHERE id2=$currId
)
IN (pos-5,pos-4,pos-3,pos-2,pos-1,pos,pos+1,pos+2,pos+3,pos+4,pos+5)
I have issue in one mysql query
Note I have checked all answer plz don't mark this question as repeat
i have one table with name of "questions" which contain que_id,cat_id, etc fields
I need to fetch 50 records from table which should be random 30 from cat_id=1 and random 20 from cat_id=2
I guess this should do it:
(SELECT * FROM questions
WHERE cat_id = 1
ORDER BY RAND()
LIMIT 30)
UNION ALL
(SELECT * FROM questions
WHERE cat_id = 2
ORDER BY RAND()
LIMIT 20)
(SELECT * FROM `questions` WHERE cat_id=2 and que_id >=
(SELECT FLOOR( MAX(que_id) * RAND()) FROM `questions` ) ORDER BY que_id LIMIT 20) UNION ALL
(SELECT * FROM `questions` WHERE cat_id=1 and que_id >=
(SELECT FLOOR( MAX(que_id) * RAND()) FROM `questions` ) ORDER BY que_id LIMIT 30)