EDIT: The goal is to order the output so that the first result is the row with store_id = 5 and the maximum rank of all items with store_id = 5. Then the rest of the deals are ranked according to their rank in descending order, regardless of their store_id. For the actual query, a union would be too expensive.
An example of data would be:
+----+----------+------+
| id | store_id | rank |
+----+----------+------+
| 1 | 1 | 10 |
+----+----------+------+
| 2 | 5 | 9 |
+----+----------+------+
| 3 | 4 | 8 |
+----+----------+------+
| 4 | 3 | 7 |
+----+----------+------+
| 5 | 3 | 6 |
+----+----------+------+
| 6 | 1 | 5 |
+----+----------+------+
The final query being run would be:
SELECT id,store_ID,IF(#id=id,rank=rank*9999999,rank) AS rank
FROM (
SELECT * FROM (
SELECT id,rank,store_id
FROM items
) b
JOIN (#id:=(SELECT id FROM b WHERE store_id = 5 ORDER BY rank DESC LIMIT 1)) AS s
)
ORDER BY rank DESC
And the desired outcome would be:
+----+----------+------+
| id | store_id | rank |
+----+----------+------+
| 2 | 5 | 9 |
+----+----------+------+
| 1 | 1 | 10 |
+----+----------+------+
| 3 | 4 | 8 |
+----+----------+------+
| 4 | 3 | 7 |
+----+----------+------+
| 5 | 3 | 6 |
+----+----------+------+
| 6 | 1 | 5 |
+----+----------+------+
Here (#id:=(SELECT id FROM b WHERE store_id = 5 ORDER BY rank DESC LIMIT 1)) is being used to select the top scoring item with store_id = 5. This item is meant to appear first in the list, hence why it is reranked by multiplying it by a very large number. All other ranks remain untouched and are ranked according to their score in descending order.
The problem is that the select statement in the join is trying to join a selection from a subquery called b. However, table b does not actually exist in the database, so MySQL is saying that table b does not exist. How can I reference the subquery? The actual subquery is incredibly complex, so I haven't included it here, but if interested you can see a version of it here: MySQL - How To Optimize thie Query. The query in this question is just to keep the problem simple.
You can adjust your order by clause to put a specific row first. Let's say you wanted store_id 5 to be first, you can do this:
ORDER BY store_id = 5 DESC, rank DESC;
Which will put the row with store_id 5 on top. In this example, you want the row that has the highest rank for store_id 5, so I wrote the following subquery:
SELECT id
FROM myTable
WHERE store_id = 5
ORDER BY rank DESC
LIMIT 1;
And I put that in the ORDER BY clause:
SELECT *
FROM myTable
ORDER BY id =
(SELECT id FROM myTable WHERE store_id = 5 ORDER BY rank DESC LIMIT 1) DESC,
rank DESC;
Here is an SQL Fiddle.
EDIT
If you put a subquery in your FROM clause, you won't be able to reference its alias in the subquery of the ORDER BY clause. However, in a very unreadable fashion, you can put the same subquery there to accomplish it, like this:
SELECT *
FROM (SELECT * FROM myTable) b
ORDER BY id =
(SELECT id FROM (SELECT * FROM myTable) b WHERE store_id = 5 ORDER BY rank DESC LIMIT 1) DESC,
rank DESC;
Another Fiddle.
Related
My database is like
id|service_id
1 | 8
2 | 3
3 | 4
4 | 1
5 | 3
6 | 2
7 | 1
i want to sort it like rows with same service_id came together but randomly. like
id|service_id
1 | 8
4 | 1
7 | 1
2 | 3
5 | 3
6 | 2
3 | 4
means first all rows sort via service_id then again sort via random service_id. I have tried SELECT * from sample_table order by service_id DESC and tried ASC too but it only do sort via DESC or ASC. I have tried order by rand(service_id) too but it is also showing some fixed sorting.
select t.*
from your_table t
join
(
select service_id, rand() as r
from your_table
group by service_id
) tmp on t.service_id = tmp.service_id
order by tmp.r;
SQLFiddle demo
Basically this makes a random substitute for the service_id and orders by that
+-----+-------+-----+
| id | Name |Votes|
+-----+-------+-----+
| 1 | Joe | 36 |
| 2 | John | 34 |
| 3 | Mark | 42 |
| 4 | Ryan | 29 |
| 5 | Jay | 36 |
| 6 | Shawn | 39 |
+-----+-------+-----+
For this example, what I want is to retrieve the rows with the first 3 highest votes. However, if you'll notice, there are two rows with the same vote count. So this should be the result:
+-----+-------+-----+
| id | Name |Votes|
+-----+-------+-----+
| 3 | Mark | 42 |
| 6 | Shawn | 39 |
| 1 | Joe | 36 |
| 5 | Jay | 36 |
+-----+-------+-----+
How to achieve this?
You will have to perform an INNER JOIN, using the table back on itself. First, you want to select the top 3 unique/distinct scores, and this can be done by using:
SELECT DISTINCT Votes FROM mytable ORDER BY Votes DESC LIMIT 3
Now that you have obtained the top 3 scores, you want to join it back to the original table:
SELECT t1.* FROM mytable AS t1
INNER JOIN
(SELECT DISTINCT Votes FROM mytable ORDER BY Votes DESC LIMIT 3) AS topvotes
ON
topvotes.Votes = t1.Votes
ORDER BY t1.Votes DESC
Refer to a simple diagram for the strategy:
For this query to be efficient, you will want to index the Votes column so that the subquery can fish out distinct votes quickly ;)
Here is a proof-of-concept SQLfiddle: http://sqlfiddle.com/#!9/c78f0/10
Probably not the most efficient, but I think this should work:
SELECT * FROM scores WHERE score IN(SELECT score FROM scores ORDER BY score DESC LIMIT 3)
Although this can yield an error about limit not being supported in subqueries.
A workaround;
SELECT * FROM scores WHERE score IN(SELECT * FROM (SELECT score FROM scores ORDER BY score DESC LIMIT 3) AS t)
I got following tables
1. user
+----+-------------------+
| id | email |
+----+-------------------+
| 2 | user1#example.com |
| 3 | user2#example.com |
| 1 | user3#example.com |
+----+-------------------+
2. answer
+----+---------+-------------+-----------+---------------------+
| id | user_id | question_id | option_id | created |
+----+---------+-------------+-----------+---------------------+
| 1 | 2 | 1 | 5 | 2015-12-19 15:15:07 |
| 2 | 2 | 1 | 5 | 2015-12-19 15:16:05 |
| 3 | 2 | 2 | 3 | 2015-12-19 15:16:06 |
| 4 | 2 | 3 | 3 | 2015-12-19 15:16:08 |
| 5 | 2 | 1 | 1 | 2015-12-19 15:32:46 |
| 6 | 2 | 1 | 4 | 2015-12-19 15:39:22 |
| 7 | 2 | 1 | 2 | 2015-12-19 15:39:23 |
| 8 | 2 | 1 | 2 | 2015-12-19 15:40:38 |
| 9 | 2 | 1 | 1 | 2015-12-19 15:41:25 |
+----+---------+-------------+-----------+---------------------+
I want to fetch option_id with most occurrences grouped by user with the following condition
If there are two or more maximum occurrences of option_id, get last record.
With reference to above answer table, as you see there are four maximum occurrences for option_id, in this case i want last in the list to be returned which is option_id 1
Here is the query i used to achieve what i want
SELECT
option_id,
COUNT(option_id) as occurence
FROM
answer
GROUP BY
option_id
ORDER BY
occurence DESC LIMIT 1;
This works, however when i add WHERE condition, it gives me option_id 5, whereas i expect option_id 1
SELECT
option_id,
COUNT(option_id) as occurence
FROM
answer
WHERE
user_id = 2
GROUP BY
option_id
ORDER BY
occurence DESC LIMIT 1;
What am i missing here?
Note : This is a follow-up question from this link, the reason i am re-posting here is to post simplified version of the same question.
I did something similar on my tables, and that's my solution:
SELECT option_id
FROM answer
WHERE id = (SELECT id
FROM answer
WHERE user_id = 2
GROUP BY option_id
ORDER BY COUNT(option_id) DESC, id DESC
LIMIT 1)
Or see what happens if you do ORDER BY occurence DESC, id DESC in your query.
If it's really only the order_id you are interested in, you can simply add MAX(created) DESC to your ORDER BY:
SELECT
option_id,
COUNT(option_id) as occurence
FROM
answer
WHERE
user_id = 2
GROUP BY
option_id
ORDER BY
occurence DESC, MAX(created) DESC LIMIT 1;
Step by step:
Count option_id occurrences (group by option_id and count)
Get maximum count from above figures (with max or with order by and limit 1)
3, Get all option_id with this count.
Get all records with one of those option_ids.
Keep only the last record of the this found records (order by limit 1).
Query:
select *
from answer
where option_id in
(
select option_id
from answer
group by option_id
having count(*) =
(
select count(*) as cnt
from answer
group by option_id
order by count(*) desc limit 1
)
)
order by created desc limit 1;
Say if I have a table similar to this but including more columns and more rows (These are the only relevant ones):
+-------+----+
| name | id |
+-------+----+
| james | 1 |
| james | 2 |
| james | 3 |
| adam | 4 |
| max | 5 |
| adam | 6 |
| max | 7 |
| adam | 8 |
+-------+----+
How could I get it so that it would only show the max(id) from each name like:
+-------+----+
| name | id |
+-------+----+
| adam | 8 |
| max | 7 |
| james | 3 |
+-------+----+
I currently just have this
"select * from table order by id desc"
but this just shows the latest ids. I only want to be able to see one of each name.
So basically show only the highest id of each name
You would use aggregation and max():
select name, max(id)
from table t
group by name
order by max(id) desc
limit 40;
EDIT:
If you need select * with the highest id, then use the not exists approach:
select *
from table t
where not exists (select 1 from table t2 where t2.name = t.name and t2.id > t.id)
order by id desc
limit 40;
The "not exists" essentially says: "Get me all rows in the table where there is no other row with the same name and a higher id". That is a round-about way of getting the maximum row.
One way to achieve this is to leverage a non-standard GROUP BY extension in MySQL
SELECT *
FROM
(
SELECT *
FROM table1
ORDER BY id DESC
) q
GROUP BY name
-- LIMIT 40
or another way is to grab a max id per name first and then join back to your table to fetch all other columns
SELECT t.*
FROM
(
SELECT MAX(id) id
FROM table1
GROUP BY name
-- LIMIT 40
) q JOIN table1 t
ON q.id = t.id
ORDER BY name;
Output:
| NAME | ID |
|-------|----|
| adam | 8 |
| james | 3 |
| max | 7 |
Here is SQLFiddle demo
$construct = "SELECT * FROM mytable GROUP BY nid HAVING nid>1";
mytable:
+----+----------+
| id | nid |
+----+----------+
| 1 | null |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 2 |
| 6 | 2 |
| 7 | 3 |
| 8 | 3 |
| 9 | 4 |
| 10 | 4 |
-----------------
How do i GROUP BY nid except nid=1? This is a brief example but with my code i am not getting the desired results. Is the query correct for what i am trying to accomplish?
How about this:
SELECT * FROM mytable WHERE nid != 1 ORDER BY nid
GROUP BY causes an aggregate query which you can only sensibly use with an aggregation function. For example, SELECT COUNT(*), nid GROUP BY nid would give you the counts of rows with a given nid.
Update: Not sure I'm understanding you, but how about this then:
(SELECT * FROM mytable WHERE nid = 1 UNION SELECT * FROM mytable WHERE nid != 1 GROUP BY nid) ORDER BY nid
I'm not sure it makes sense to mix aggregate and non-aggregate queries, though -- on the aggregate side you'll just end up with an indeterminate representative row of that group.
SELECT count(*), nid FROM mytable where nid <> 1 GROUP BY nid;
or
SELECT count(*), nid FROM mytable where nid != 1 GROUP BY nid;
Not sure if you are using Oracle or MySQL.
…
GROUP BY CASE nid WHEN 1 THEN -id ELSE nid END
…