I have a table that list system licences, multiple licences for each system (the expired ones and existing ones). I've only posted two columns in this question as they're the only important ones.
| id | systemid |
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | 2 |
| 5 | 3 |
| 6 | 3 |
I need to get the rows with the id of 2, 4 and 6.
I need to collect 1 record for each systemid and it has to be the earliest (youngest) record, so in this case, the record with the highest id. I've been exploring GROUP BY, ORDER BY and LIMIT but I'm not producing the result I'm after. How do you collect one record for each individual value in one column and make sure it's the record with the highest id?
I KNOW this is wrong, but it's what I'm currently starring at:
SELECT * FROM licences GROUP BY systemid ORDER BY id DESC LIMIT 1
SELECT max(id), systemid FROM table GROUP BY systemid
Note that with a GROUP BY, all columns you select must either be in the GROUP BY clause or wrapped in an aggregating function, like max, min, sum, or average.
This will grab the highest id per systemid.
SELECT MAX(id), systemid
FROM ...
GROUP BY systemid
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
Trying to retrieve records by first summing their time_spent, then using max to retrieve the largest record by time. which seems to be working.
I need to now check if on the chance that the sum of time_spent are the same value for users (a tie, like in the example below, both users have time_spent as 10 so it should then select the user that has the latest post), if they are then I need to only get the user_id that was posted last (newer) using the created_at column. I just don't know what to use to do that check, is it a CASE, or IF Function? and if so where would it go in my query?
Here is a sql fiddle link: http://sqlfiddle.com/#!9/f24985/2
Table1 layout
+----+---------+-----------+---------+------------+------------+
| id | user_id | member_id | item_id | time_spent | created_at |
+----+---------+-----------+---------+------------+------------+
| 1 | 1 | 1 | 1 | 5 | 2019-06-01 |
| 2 | 2 | 1 | 1 | 1 | 2019-06-07 |
| 3 | 2 | 1 | 1 | 5 | 2019-06-08 |
| 4 | 2 | 1 | 2 | 4 | 2019-06-01 |
| 5 | 1 | 1 | 2 | 5 | 2019-06-07 |
+----+---------+-----------+---------+------------+------------+
Current SQL:
SELECT
MAX(attribute_time.sum_time), attribute_time.user_id
FROM (
SELECT
SUM(time_spent) AS sum_time, user_id
FROM
table1
WHERE
member_id = 1
AND item_id IN (1, 2)
AND (created_at BETWEEN '2019-06-1' AND '2019-06-30')
GROUP BY
user_id
ORDER BY
sum_time desc
) AS attribute_time;
In this example, both users have a total of 10 for time, currently returns the first record of the 2 and not based on the created_at date, which in this case, should be user 2.
Expected
+---------+
| user_id |
+---------+
| 2 |
+---------+
This is what you are looking for. http://sqlfiddle.com/#!9/a5306c/4/0
The MAX clause is problematic for sub-queries involving quantities unless you use some repetitive and verbose queries (DRY!), as seen in answer here: MySQL: Select MAX() from sub-query with COUNT() - it seems to decouple the rows, so you get the highest (max) sum_time with the wrong id (I thought I was seeing things, seemed so simple)
I used LIMIT to get around it. Sorting descending (the highest on top), and then LIMITing the result to 1 achieves the same thing as "Max".
Also - Im not sure if in event of a tie in max time you wanted to pick the earliest or latest record, but this picks the latest. I use MAX to pick the last day/time for each user, and orderby sum_of_time, then by date. If you want the opposite, sub MIN for MAX and/or DESC for ASC in the order-by. Regards! Thx for the exercise.
SELECT
SUM(time_spent) AS sum_time, user_id, MAX(created_at)
FROM
Table1
WHERE
member_id = 1
AND item_id IN (1, 2)
AND (created_at BETWEEN '2019-06-1' AND '2019-06-30')
GROUP BY
user_id
ORDER BY
sum_time DESC, created_at DESC
LIMIT 1
Try to use this it will give you user id 2
SELECT
MAX(attribute_time.sum_time), attribute_time.user_id
FROM (
SELECT
SUM(time_spent) AS sum_time, user_id
FROM
table1
WHERE
member_id = 1
AND item_id IN (1, 2)
AND (created_at BETWEEN '2019-06-1' AND '2019-06-30')
GROUP BY
user_id
ORDER BY
sum_time,user_id desc
) AS attribute_time;
I want to show first two top voted Posts then others sorted by id
This is table
+----+-------+--------------+--------+
| Id | Name | Post | Votes |
+====+=======+==============+========+
| 1 | John | John's msg | -6 |
| 2 |Joseph |Joseph's msg | 8 |
| 3 | Ivan | Ivan's msg | 3 |
| 4 |Natalie|Natalie's msg | 10 |
+----+-------+--------------+--------+
After query result should be:
+----+-------+--------------+--------+
| Id | Name | Post | Votes |
+====+=======+==============+========+
| 4 |Natalie|Natalie's msg | 10 |
| 2 |Joseph |Joseph's msg | 8 |
-----------------------------------------------
| 1 | John | John's msg | -6 |
| 3 | Ivan | Ivan's msg | 3 |
+----+-------+--------------+--------+
I have 1 solution but i feel like there is better and faster way to do it.
I run 2 queries, one to get top 2, then second to get others:
SELECT * FROM table order by Votes desc LIMIT 2
SELECT * FROM table order by Id desc
And then in PHP i make sure that i show 1st query as it is, and on displaying 2nd query i remove entry's that are in 1st query so they don't double.
Can this be done in single query to select first two top voted, then others?
You would have to use subqueries or union - meaning you have a single outer query, which contains multiple queries inside. I would simply retrieve the IDs from the first query and add a id not in (...) criterion to the where clause of the 2nd query - thus filtering out the posts retrieved in the first query:
SELECT * FROM table WHERE Id NOT IN (...) ORDER BY Id DESC
With union the query would look like as follows:
(SELECT table.*, 1 as o FROM table order by Votes desc LIMIT 2)
UNION
(SELECT table.*, 0 FROM table
WHERE Id NOT IN (SELECT Id FROM table order by Votes desc LIMIT 2))
ORDER BY o DESC, if(o=1,Votes,Id) DESC
As you can see, it wraps 3 queries into one and has a more complicated ordering as well because in union the order of the records retrieved is not guaranteed.
Two simple queries seem to be a lot more efficient to me in this particular case.
There could be different ways to write a query that returns the rows in the order you want. My solution is this:
select
table.*
from
table left join (select id from table order by votes desc limit 2) l
on table.id = l.id
order by
case when l.id is not null then votes end desc,
tp.id
the subquery will return the first two id ordered by votes desc, the join will succeed whenever the row is one of the first two otherwise l.id will be null instead.
The order by will order by number of votes desc whenever the row is the first or the second (=l.id is not null), when l.id is null it will put the rows at the bottom and order by id instead.
Suppose we have a table like the one below.
Id | Name | Group
-----------------
1 | John | 1
2 | Zayn | 2
3 | Four | 2
4 | Ben_ | 3
5 | Joe_ | 2
6 | Anna | 1
The query below will select all of them.
SELECT `Name` FROM `Table` WHERE 1;
How would I select only one person from each group? Who it is doesn't really matter, as long as there's only one name from group 1 and one name from group 2 etc.
The GROUP BY clause isn't fit for this (according to my error console) because I am selecting non aggregated values, which makes sense.
The DISTINCT clause isn't great here either, since I don't want to select the "Group" and definitely not group by their names.
If is not important the resulting name You can anawy leverage some group functions eg with max or min..
leverage the group functions
select max(name) from your_table
group by Group;
otherwise you can use subquery
select name from your_table
where Id in (select min(Id) from your_table group by Group);
I have this table:
What's the correct query to get this result:
ID | Type | Total
1 | A | 300
1 | B | 100
2 | A | 30
2 | B | 40
Which means sum by type first and then group by user id?
Your aggregate functions, such as SUM, are performed on your groups; so there is no "SUM by type first then group by user_id", you are wanting to group by user_id and type.
Like so: GROUP BY user_id, type
If you want to guarantee that ordering in the future also have an ORDER BY user_id, type clause as well. Currently, GROUP BY also orders, but I believe that feature has been marked as deprecated recently.
I am looking for an SQL query to give me a list of duplicate entries in a table. However, there are 3 different columns to take into account. First is an ID, Second is a Name, and third is a Date. The situation is that there are multiple Names that are assigned with the same ID, and there are multiple records of those in a day, which makes THOUSANDS of different records per day.
I already filtered it so that only results for the past 7 days will show, but the amount of records is still too much for me to extract. I just want to decrease the number of rows in the output order to properly extract the results.
Sample
|--id-|--name--|-------date------|
| 1 | a |5-9-2015, 10:00am|
| 1 | a |5-8-2015, 10:02am|
| 1 | a |5-8-2015, 11:00am|
| 1 | b |5-8-2015, 10:00am|
| 1 | b |5-8-2015, 10:02am|
| 1 | c |5-8-2015, 10:00am|
| 2 | d |5-8-2015, 10:00am|
expected output
|--id-|--name--|
| 1 | a |
| 1 | b |
| 1 | c |
| 2 | d |
Inclusion of entries without any duplicates are fine. The important thing is to only return a single record of a unique id-name combination for a day.
Thanks in advance for any help that you can give.
You can get the combinations as:
select distinct id, name
from sample;
If you want duplicates, using group by and having:
select id, name
from sample
group by id, name
having count(*) > 1;
EDIT:
If you want this by date, then add date(date) to the group by and perhaps select clauses.
To return single id-name data per day you can use this:
select id, name
from tab
group by id, name, date(date)
The DATE() function extracts the date part of a date or date/time expression.
select id,name
from sample
group by id,name,DATE(date)
having count(*)>1;