SQL - group by column 1, order by column 2 - mysql

Here's my situation, I have two tables named people and contacts respectively
id name
1 dev one
2 dev two
3 dev three
4 dev five
5 dev four
id person_id code_name updated_at
1 1 base1 2019-12-18 00:00:01
2 3 base2 2019-12-18 00:00:02
3 2 home 2019-12-18 00:00:03
4 2 home2 2019-12-18 00:00:04
5 3 work 2019-12-18 00:00:05
6 4 work 2019-12-18 00:00:06
7 5 base 2019-12-18 00:00:07
8 4 base2 2019-12-18 00:00:08
9 2 base 2019-12-18 00:00:09
10 5 work 2019-12-18 00:00:10
And I'm trying to get a result from contacts where its ordered by most recent updated_at and grouped(note: not exactly the sql "group by") by person_id, that looks similar to the following result.
id person_id code_name updated_at
10 5 work 2019-12-18 00:00:10
7 5 base 2019-12-18 00:00:07
9 2 base 2019-12-18 00:00:09
4 2 home2 2019-12-18 00:00:04
3 2 home 2019-12-18 00:00:03
8 4 base2 2019-12-18 00:00:08
6 4 work 2019-12-18 00:00:06
5 3 work 2019-12-18 00:00:05
2 3 base2 2019-12-18 00:00:02
1 1 base1 2019-12-18 00:00:01
Currently I'm ordering the contacts table by person_id desc and updated_at desc and results to a bit close of what I expected but not exactly correct.
See results when doing ORDER BY person_id DESC, updated_at DESC https://monosnap.com/file/xN0cuZAu2x2df4Q5qNDksKq5P3sEjU contact with id => 1 should be at the top of the result set since it's the most recent updated of them all.
Note: PostgreSQL is my first use case on this case but it's nice to know also for MySQL if there is any difference.

I have tried the following in the PostgreSQL 9.3.
Data Sample:
create table contact
(
id int,
person_id int,
code_name varchar(20),
updated_at timestamp
);
INSERT INTO contact VALUES
(1,1,'base1','2019-12-18 00:00:01'),
(2,3,'base2','2019-12-18 00:00:02'),
(3,2,'home','2019-12-18 00:00:03'),
(4,2,'home2','2019-12-18 00:00:04'),
(5,3,'work','2019-12-18 00:00:05'),
(6,4,'work','2019-12-18 00:00:06'),
(7,5,'base','2019-12-18 00:00:07'),
(8,4,'base2','2019-12-18 00:00:08'),
(9,2,'base','2019-12-18 00:00:09'),
(10,5,'work','2019-12-18 00:00:10');
Query:
DROP TABLE IF EXISTS TEMP_Stage_Table;
SELECT string_agg(id::text,',' order by updated_at desc) id,
person_id,
string_agg(code_name,',' order by updated_at desc) code_name,
string_agg(updated_at::text,',' order by updated_at desc) updated_at INTO TEMP_Stage_Table
FROM contact
GROUP BY person_id
ORDER BY MAX(updated_at) DESC;
SELECT regexp_split_to_table(t.id, E',') AS id,
t.person_id,
regexp_split_to_table(t.code_name, E',') AS code_name,
regexp_split_to_table(t.updated_at, E',') AS updated_at
FROM TEMP_Stage_Table t;
Output:

(MySQL/MariaDB syntax)
This will find the "ordering" for each "group of rows" for a person, correct?
SELECT MAX(updated_at), person_id
FROM tbl GROUP BY person_id ;
So, let's make use of that thus:
SELECT y.*
FROM (SELECT MAX(updated_at) AS latest, person_id
FROM tbl GROUP BY person_id ) AS x
JOIN tbl AS y USING(person_id)
ORDER BY x.latest DESC, y.updated_at DESC;

Related

Retrieve last row with specific condition

Suppose we have a table like:
ID
Account
Amount
Date
1
4455
52
01-01-2022
2
4455
32
02-01-2022
3
4455
23
03-01-2022
4
4455
23
04-01-2022
5
6565
236
01-01-2022
6
6565
623
02-01-2022
7
6565
132
03-01-2022
8
2656
564
01-01-2022
9
2656
132
02-01-2022
We need to retrieve every last row of given account_no. We need output like:
ID
Account
Amount
Date
4
4455
23
04-01-2022
7
6565
132
03-01-2022
10
2656
13
03-01-2022
Kindly suggest me a query to retrieve data like this in table of 2000 records.
You want the last row of certain query. So you must be having an order by clause. Just reverse the ordering and use a limit clause with limit set to one row.
SELECT column_name(s)
FROM table_name
WHERE condition
order by your_reversed_orderby_clause
LIMIT 1;
If you are using MySQL 8, then you can use ROW_NUMBER() function for this:
WITH CTE AS
(
SELECT ID,Account,Amount,Date
,ROW_NUMBER() OVER(PARTITION BY Account ORDER BY ID DESC) AS RN
FROM Table1
)
SELECT * FROM CTE
WHERE RN=1
ORDER BY ID;
ID
Account
Amount
Date
RN
4
4455
23
2022-04-01 00:00:00
1
7
6565
132
2022-03-01 00:00:00
1
9
2656
132
2022-02-01 00:00:00
1
See this db<>fiddle
SELECT * FROM table_name
WHERE ID IN (
SELECT max(ID) FROM table_name
GROUP BY Acount
ORDER BY Account
)

Is there a way to include double condition in HAVING?

I'm currently working on a query that looks like this. There are two tables - members and member_gathering.
SELECT id, city_id, name FROM members
WHERE "id" = "member_id" IN
(
SELECT "member_id" from member_gathering
GROUP BY "member_id"
HAVING COUNT(DATEDIFF("visited","joined">=365))>=5
ORDER BY "member_id"
)
ORDER BY id*1;
The goal is to have an output of all IDs satisfying the condition of being in more than 5 groups, in which a member is active for more than a year. Being active means having a difference between "visited" and "joined" columns (both are TIMESTAMP) for more than a year (I set that as 365 days).
However, after running, this code shows all the rows in a members table (though manual check of both tables shows that some rows do not satisfy both conditions at the same time).
Any ideas on how to improve the code above? I'm not sure if I can use 'nested' condition inside COUNT(), but all other variants used before show either NULL values or returned all rows in the table, which is obviously not right. Also, I was thinking that problem might be with DATEDIFF function.
All suggestions are welcome: I'm a newbie to MySQL, so I'm not that familiar with it.
UPD: data sample:
1) members
id city_id name
2 980 Joey
5 980 Carl
10 1009 Louis
130 1092 Andrea
2) member_gathering
member_id gathering_id joined visited
2 1 2010-01-01 00:00:00 2010-02-01 00:00:00
2 2 2010-01-01 00:00:00 2010-02-01 00:00:00
5 2 2010-01-01 00:00:00 2010-02-01 00:00:00
10 3 2010-01-01 00:00:00 2010-02-01 00:00:00
130 1 2010-02-01 00:00:00 2013-02-01 00:00:00
130 2 2010-02-01 00:00:00 2013-02-01 00:00:00
130 3 2010-02-01 00:00:00 2014-02-01 00:00:00
130 4 2010-02-01 00:00:00 2018-02-01 00:00:00
130 5 2010-02-01 00:00:00 2015-02-01 00:00:00
Expected result would be only ID 130, thus: 130, 1092, Andreana.
I believe you first need to find all records where datediff is 365 days or more. Then find members who have 5 or more such instances. This needs both WHERE and HAVING clause:
SELECT id, city_id, name
FROM members
WHERE id IN (
SELECT member_id
FROM member_gathering
WHERE DATEDIFF(visited, joined) >= 365
GROUP BY member_id
HAVING COUNT(*) >= 5
)
You could use this way
SELECT id, city_id, name FROM members
WHERE member_id IN
(
SELECT member_id from member_gathering
GROUP BY member_id
HAVING SUM(DATEDIFF(visited, joined) >= 365)>=5
ORDER BY member_id
)
You should use separated expression for count differente category of datediff and remmeber that count work for not null values so if you want obtain the totale for true values you should sue SUM

mysql select 2 table based on date and combine each other

i Need some help.. i have some case like this below
i have 2 table.. call ("in_table" and "out_table")
data "in_table" look like
stock_id item_id date qty_in
-----------------------------------------
1 11 2017-07-11 12
2 11 2017-07-11 10
3 12 2017-07-11 10
4 12 2017-07-19 10
And i have "out_table" is like
id_tr item_id date qty_out
-------------------------------------
1 11 2017-07-19 2
1 12 2017-07-19 1
2 11 2017-07-19 2
2 12 2017-07-19 1
And i want to combine the date and display all the data like this,
Update: the join is by item_id but i want to select by date
date item_id qty_in qty_out
---------------------------------------
2013-07-11 11 22 0
2013-07-11 12 10 0
2013-07-19 11 0 4
2013-07-19 12 10 2
Thank you for your help.
It looks like you need kind of a full outer join of two aggregate subqueries. But in your case I would get item_id and date in a union subquery (derived table) and the sums in correlated subqueries (subselect).
select item_id, date,
(select sum(qty_in) from in_table i where i.item_id = sub.item_id and i.date = sub.date) as qty_in,
(select sum(qty_out) from out_table o where o.item_id = sub.item_id and o.date = sub.date) as qty_out
from (
select item_id, date from in_table
union
select item_id, date from out_table
) sub
order by date, item_id

MySQL sum column and display last row in column

I have a table of users,subscription packages and various user subscriptions.
I need to fetch a sum of all subscription cost and display the latest/last subscription. The latest subscription is the subscription
with the highest subscription_id. How can I write my query? My tables are listed as below.
Users table
user_id name
1 John
2 Jane
3 Matthew
Subscription Packages table
package_id package_name
1 Basic
2 Advanced
3 Premium
User Subscriptions
subscription_id user_id package_id subscription_cost date
1 1 1 2 2014-04-01
2 2 1 2 2014-04-01
3 3 1 2 2014-04-01
4 1 1 2 2014-05-01
5 1 2 3.5 2014-06-01
6 2 2 3.5 2014-06-01
7 2 2 3.5 2014-07-01
8 1 3 5 2014-07-01
9 3 2 5 2014-07-01
10 2 2 3.5 2014-08-01
11 1 1 2 2014-08-01
My results should be like so
name total_costs latest_package
John 14.5 Basic
Jane 12.5 Advanced
Matthew 7 Premium
Because you need to do an aggregation anyway, I would go for the group_concat()/substring_index() trick:
select u.user_id, u.name, sum(subscription_cost) as total_costs,
substring_index(group_concat(p.package_name order by us.date desc), ',', 1) as latest_package
from usersubscriptions us join
users u
on us.user_id = u.user_id join
packages p
on us.package_id = p.package_id
group by u.user_id;
This assumes that no package names have commas. It is also subject to default limits on the length of the result of group_concat() -- but this method often works in practice.

Select the 2 latest records from table

I have data like in this mysql table:
id customer_id date price
1 A 2014-01-01 4
2 A 2014-02-01 3
3 B 2014-03-01 2.5
4 B 2014-04-01 1
5 B 2014-05-01 5
6 C 2014-06-01 2
7 D 2014-07-01 2
8 D 2014-08-01 2.5
9 D 2014-09-01 1
I want to get the latest two dates for customer_id A, B and D. My result should be like this:
id customer_id date price
1 A 2014-01-01 4
2 A 2014-02-01 3
4 B 2014-04-01 1
5 B 2014-05-01 5
8 D 2014-08-01 2.5
9 D 2014-09-01 1
Any help is greatly appreciated.
One possible way :
SELECT *
FROM test s
WHERE (
SELECT COUNT(*)
FROM test f
WHERE f.customer_id = s.customer_id AND
f.`date` >= s.`date`
) <= 2
AND customer_id in('A','B','D');
[SQL Fiddle demo]
Try like this
select * from table where customer_id in('A','B','D') order by date desc limit 2