Selecting non-group field from query with aggregate function - mysql

I have a table tbl with three columns:
id | fk | dateof
1 | 1 | 2012-01-01
2 | 1 | 2012-01-02
3 | 2 | 2012-02-01
4 | 2 | 2012-03-01
5 | 3 | 2012-04-01
id is the ID of the row, fk is a foreign key to another table and dateof is a date column.
What I want is to get the id where the dateof is the latest grouped by fk. What I've tried:
SELECT id, MAX(dateof) FROM tbl GROUP BY fk
But I get results like this:
1 | 2012-01-02
3 | 2012-03-01
5 | 2012-04-01
When I want:
2 | 2012-01-02
4 | 2012-03-01
5 | 2012-04-01
How can I query and get the results I'm looking for? MySQL server if it matters. Thanks.

Personally I would do
SELECT id, tbl.dateof dateof
FROM tbl
INNER JOIN
(SELECT fk, MAX(dateof) dateof
FROM tbl
GROUP BY fk) temp
ON tbl.fk = temp.fk AND tbl.dateof = temp.dateof
Gordon's answer is correct and less code, but I prefer creating a temp table. It's more clear to other developers what I'm doing.

To get what you want:
select t.*
from (select tbl.*,
row_number() over (partition by fk order by dateof desc) as seqnum
from tbl
) t
where seqnum = 1
This assumes that you are using a reasonable database that has window functions. You don't specify the database in your question.

Related

How can I identify if a certain row is the last by using an ID in MySQL

Sorry to confuse you about my title. I am building an auction system and I am having a difficulty in getting the user's winning item.
Example I have a table like this:
the columns are:
id, product_id, user_id, status, is_winner, info, bidding_price, bidding_date
here's my sql fiddle:
http://sqlfiddle.com/#!9/7097d/1
I want to get every user's item that they already win. So I need to identify if they are the last who bid in that item.
I need to filter it using a user_id.
If I do a query like this:
SELECT MAX(product_id) AS product_id FROM auction_product_bidding
WHERE user_id = 3;
it will get only the product_id that is 12 and the product_id of 9 did not get. Product ID 9 is also that last bid of the user_id 3.
Can you help me? I hope you got my point. Thanks. Sorry if my question a little bit confusing.
According to your question, seems 11 is also what you want, try this query:
SELECT apd.product_id
FROM auction_product_bidding apd
JOIN (
SELECT MAX(bidding_date) AS bidding_date, product_id
FROM auction_product_bidding
GROUP BY product_id
) t
ON apd.product_id = t.product_id
AND apd.bidding_date = t.bidding_date
WHERE apd.user_id = 3;
Check Demo Here
select id,product_id,user_id,status,is_winner,info,bidding_price,bidding_date,rank
from
( SELECT apb.*,
greatest(#rank:=if(product_id=#prodGrp,#rank+1,1),-1) as rank,
#prodGrp:=product_id as dummy
FROM auction_product_bidding apb
cross join (select #prodGrp:=-1,#rank:=0) xParams
order by product_id,bidding_date DESC
) xDerived
where user_id=3 and rank=1;
That user won 9,11,12
+----+------------+---------+--------+-----------+------+---------------+---------------------+------+
| id | product_id | user_id | status | is_winner | info | bidding_price | bidding_date | rank |
+----+------------+---------+--------+-----------+------+---------------+---------------------+------+
| 60 | 9 | 3 | | 0 | | 75000.00 | 2016-08-02 16:31:23 | 1 |
| 59 | 11 | 3 | | 0 | | 15000.00 | 2016-08-02 12:04:16 | 1 |
| 68 | 12 | 3 | | 0 | | 18000.00 | 2016-08-10 09:20:01 | 1 |
+----+------------+---------+--------+-----------+------+---------------+---------------------+------+
SELECT product_id FROM auction_product_bidding where bidding_price= any
(select max(bidding_price) from auction_product_bidding group by product_id)
and user_id='3';
select * from
(select product_id,user_id,max(bidding_price) from
(select * from auction_product_bidding order by bidding_price desc) a
group by product_id) b
where user_id=3;
Answer:
product_id user_id max(bidding_price)
9 3 75000
11 3 15000
12 3 18000
An idea could be to sort the table desc by date and select every distinct row by product_id and customer_id. Something like
SELECT DISTINCT prod_id, user_id FROM (
SELECT * FROM auction_product_bidding ORDER BY date DESC
)
You want everything that bids last in 3, is it right ?

select non group by columns with count in Mysql

I have a table tbl with three columns:
id | fk | dateof
1 | 1 | 2016-01-01
2 | 1 | 2016-01-02
3 | 2 | 2016-02-01
4 | 2 | 2016-03-01
5 | 3 | 2016-04-01
I want to get the results like this
Id count of Id max(dateof)
2 | 2 | 2016-01-02
4 | 2 | 2016-03-01
5 | 1 | 2016-04-01
My try
SELECT id,tbl.dateof dateof
FROM tbl
INNER JOIN
(SELECT fk, MAX(dateof) dateof ,
count(id) cnt_of_id -- How to get this count value in the result
FROM tbl
GROUP BY fk) temp
ON tbl.fk = temp.fk AND tbl.dateof = temp.dateof
This is an aggregation query, but you don't seem to want the column being aggregated. That is ok (although you cannot distinguish the rk that defines each row):
select count(*) as CountOfId, max(dateof) as maxdateof
from t
group by fk;
In other words, your subquery is pretty much all you need.
If you have a reasonable amount of data, you can use a MySQL trick:
select substring_index(group_concat(id order by dateof desc), ',', 1) as id
count(*) as CountOfId, max(dateof) as maxdateof
from t
group by fk;
Note: this is limited by the maximum intermediate size for group_concat(). This parameter can be changed and it is typically large enough for this type of query on a moderately sized table.
You obviously want one result row per fk, so group by it. Then you want the max ID, the row count and the max date for each fk:
select
max(id) as max_id,
count(*) as cnt,
max(date_of) as max_date_of
from tbl
group by fk;

Sql Query to retain max 2 duplicate records

I have the following data:
Name ID Date
Dave | 123 | 1-2-2011
Jim | 123 | 1-3-2011
Mike | 123 | 1-10-2011
Bill | 111 | 1-2-2011
Henry | 222 | 1-3-2011
Larry | 222 | 1-4-2011
I need a delete query to reduce this to:
Jim | 123 | 1-3-2011
Mike | 123 | 1-10-2011
Bill | 111 | 1-2-2011
Larry | 222 | 1-4-2011
i.e. I want to keep the two latest records for each ID.
I tried this:
Delete FROM UserTable a
WHERE
Date <> (SELECT MAX(Date) FROM UserTable b WHERE a.ID = b.ID)
AND ID IN (SELECT ID FROM UserTable GROUP BY ID HAVING COUNT(*) > 1)
but this retains only the latest 1 unique records.
Try something of the sort:
SELECT col1, col2, col3, COUNT(*)
FROM (SELECT DISTINCT * FROM your_TableName) AS T1
GROUP BY col2, col3
try using this code below depending on what you want to request try using ASC or DESC
SELECT column_name,column_name
FROM table_name
ORDER BY column_name,column_name ASC|DESC;
Try also reading up on your sql http://www.w3schools.com/sql/sql_orderby.asp
Partition over can be used to retain more than one duplicate.
delete from UserTable
where (id, date) in (
select id,date from (
select id,date,
row_number() over (partition by id order by date desc) as temp
from UserTable)
where temp > 2
);
This query adds another column with row number partitioned by the id, ordered by date and then deletes those records which have row number greater than 2.

Sort data before using GROUP BY?

I have read that grouping happens before ordering, is there any way that I can order first before grouping without having to wrap my whole query around another query just to do this?
Let's say I have this data:
id | user_id | date_recorded
1 | 1 | 2011-11-07
2 | 1 | 2011-11-05
3 | 1 | 2011-11-06
4 | 2 | 2011-11-03
5 | 2 | 2011-11-06
Normally, I'd have to do this query in order to get what I want:
SELECT
*
FROM (
SELECT * FROM table ORDER BY date_recorded DESC
) t1
GROUP BY t1.user_id
But I'm wondering if there's a better solution.
Your question is somewhat unclear but I have a suspicion what you really want is not any GROUP aggregates at all, but rather ordering by date first, then user ID:
SELECT
id,
user_id,
date_recorded
FROM tbl
ORDER BY date_recorded DESC, user_id ASC
Here would be the result. Note reordering by date_recorded from your original example
id | user_id | date_recorded
1 | 1 | 2011-11-07
3 | 1 | 2011-11-06
2 | 1 | 2011-11-05
5 | 2 | 2011-11-06
4 | 2 | 2011-11-03
Update
To retrieve the full latest record per user_id, a JOIN is needed. The subquery (mx) locates the latest date_recorded per user_id, and that result is joined to the full table to retrieve the remaining columns.
SELECT
mx.user_id,
mx.maxdate,
t.id
FROM (
SELECT
user_id,
MAX(date_recorded) AS maxdate
FROM tbl
GROUP BY user_id
) mx JOIN tbl t ON mx.user_id = t.user_id AND mx.date_recorded = t.date_recorded
Iam just using the technique
"Using order clause before group by inserting it in group_concat clause"
SELECT SUBSTRING_INDEX(group_concat(cast(id as char)
ORDER BY date_recorded desc),',',1),
user_id,
SUBSTRING_INDEX(group_concat(cast(`date_recorded` as char)
ORDER BY `date_recorded` desc),',',1)
FROM data
GROUP BY user_id

MySQL: How to GROUP BY a field to retrieve the rows with ORDER BY another field?

assume following data:
Data:
id | date | name | grade
--------+---------------+-----------+---------------
1 | 2010/12/03 | Mike | 12
2 | 2010/12/04 | Jenny | 12
3 | 2010/12/04 | Ronald | 15
4 | 2010/12/03 | Yeni | 11
i want to know who has the best grade in each day, something like this:
Desired Result:
id | date | name | grade
--------+---------------+-----------+---------------
1 | 2010/12/03 | Mike | 12
3 | 2010/12/04 | Ronald | 15
i thought query should look like this:
SELECT name FROM mytable
GROUP BY date
ORDER BY grade DESC
but it returns something like this:
Current Unwanted Result:
id | date | name | grade
--------+---------------+-----------+---------------
1 | 2010/12/03 | Mike | 12
2 | 2010/12/04 | Jenny | 12
i searched and i found the reason:
GROUP BY happens before ORDER BY so it does not see and can't apply ORDER.
so how can i apply ORDER on GROUP BY?
Note: please keep in mind that i need the most simple query, because my query is actually very complex, i know i can achieve this result by some subquery or JOINing, but i want to know how to apply ORDER to GROUP BY. thanks
I used Oracle for this example, but the SQL should work in mysql (you may need to tweak the to_date stuff to work with mysql). You really need a subquery here to do what you are asking.
CREATE TABLE mytable (ID NUMBER, dt DATE, NAME VARCHAR2(25), grade NUMBER);
INSERT INTO mytable VALUES(1,to_date('2010-12-03','YYYY-MM-DD'),'Mike',12);
INSERT INTO mytable VALUES(1,to_date('2010-12-04','YYYY-MM-DD'),'Jenny',12);
INSERT INTO mytable VALUES(1,to_date('2010-12-04','YYYY-MM-DD'),'Ronald',15);
INSERT INTO mytable VALUES(1,to_date('2010-12-03','YYYY-MM-DD'),'Yeni',11);
SELECT id
, dt
, name
, grade
FROM mytable t1
WHERE grade = (SELECT max(grade)
FROM mytable t2
WHERE t1.dt = t2.dt)
ORDER BY dt
Results:
ID DT NAME GRADE
1 12/3/2010 Mike 12
2 12/4/2010 Ronald 15
I know you said you wanted a GROUP / ORDER only solution but you will need to use a subquery in this instance. The simplest way would be something like this:
SELECT id, date, name, grade
FROM mytable t1
WHERE grade =
(SELECT MAX(t2.grade) FROM mytable t2 WHERE t1.id = t2.id)
This would show multiple students if they shared the highest grade for the day.