Selecting the latest row for each customer that matches these params - mysql

I have an SQL table that stores reports. Each row has a customer_id and a building_id and when I have the customer_id, I need to select the latest row (most recent create_date) for each building with that customer_id.
report_id customer_id building_id create_date
1 1 4 1553561789
2 2 5 1553561958
3 1 4 1553561999
4 2 5 1553562108
5 3 7 1553562755
6 3 8 1553570000
I would expect to get report id's 3, 4, 5 and 6 back.
How do I query this? I have tried a few sub-selects and group by and not gotten it to work.

If you are using MySQL 8+, then ROW_NUMBER is a good approach here:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY customer_id, building_id
ORDER BY create_date DESC) rn
FROM yourTable
)
SELECT
report_id,
customer_id,
building_id,
create_date
FROM cte
WHERE rn = 1;
If there could be more than one customer/building pair tied for the latest creation date, and you want to capture all ties, then replace ROW_NUMBER with RANK, and use the same query.

Another variation:
SELECT a.*
FROM myTable a
WHERE a.create_date = (SELECT MAX(create_date)
FROM myTable b
WHERE b.customer_id = a.customer_id
AND b.building_id = a.building_id)
Can try doing a search for "effective dated records" to see various approaches.

Related

Simple SQL select sum one time and values of same column

I have create this query:
SELECT table1.id, b.sum
FROM table1
CROSS JOIN (SELECT SUM(id) sum
FROM table1) b
ORDER BY id DESC;
But this produces results like this:
id
sum
3
6
2
6
1
6
Sum value print only one time. Can you help me.
But I want this result :
id
sum
3
6
2
1
This should do it:
select
id,
CASE WHEN id=(max(id) over())
THEN sum(id) over (order by id) END as 'sum'
from cte1
order by id desc;
more info see: Window Function Concepts and Syntax

Select the first row after order by using distinct value

I have a table named emails which looks like this:
customer_id
email_template_id
send_time
order_type
1
1
2021-01-10
1
2
1
2021-01-10
1
1
2
2021-02-10
2
3
1
2021-03-10
1
2
2
2021-03-10
2
1
3
2021-04-10
1
I want to order the data by email_template_id which will be given on the web page (can be 1,2,3...). Whenever a person clicks on sort by (template_id = 2), I want to retrieve the data order by template_id then by date, but still get the unique customer_id of course. The result should look something like this:
customer_id
email_template_id
send_time
order_type
1
2
2021-02-10
2
2
2
2021-03-10
2
3
1
2021-03-10
1
I have tried this but I am getting duplicate rows.
select distinct customer_id as cust,order_type,email_template_id,send_time
from email_order
ORDER by FIELD(email_template_id,2,1,3,4,5,6),send_time desc
I tried using subquery to select distinct customer_id
select distinct(t1.customer_id) from
(select distinct customer_id as cust,email_template_id
from email_order
ORDER by FIELD(email_template_id,2,1,3),send_time desc) as t1
It works when I only select (t1.customer_id), when ever I select order_type or send_time from t1, it no longer shows unique customer_id, but shows the data according to send_time desc etc
Your help and time will be highly appreciated!
If your have an older version you can use the query below:
select customer_id, email_template_id, send_time, order_type
from ( select customer_id, email_template_id, send_time,order_type
from emails order by case when email_template_id=2 then -1 else email_template_id end ) as e
group by customer_id;
SQLfiddle working on MySQL 5.6 : http://sqlfiddle.com/#!9/d55f4d/1
But if the version is MariaDB, i think not in your case you should apply limit inside the subquery.
The images below are example in 10.4.17-MariaDB version
ROW_NUMBER should work well here:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY customer_id
ORDER BY FIELD(email_template_id, 2, 1, 3, 4, 5, 6)) rn
FROM email_order
)
SELECT customer_id, email_template_id, send_time, order_type
FROM cte
WHERE rn = 1;

MySQL - Group and return single row for each group based on most recent

So I'm having an issue with what I expect is a very simple problem, but for the life of me I can't figure it out!
I have a table like this:
id name status date
1 bob good 01/01/2020
2 john good 01/01/2020
3 bob bad 02/01/2020
4 john good 02/01/2020
5 ben good 02/01/2020
I want to retrieve the latest record for each name.
I have tried the following:
SELECT name
,STATUS
,MAX(DATE)
FROM TABLE
GROUP BY name
ORDER BY MAX(DATE)
I thought this worked, however it is returning a record for bob, john and ben, but it is showing bobs date as 02/01/2020 but his status as "good" from the other record!
At a loss as to how to do this in the simplest way possible, all help is much appreciated!
Don't think of this as aggregation. Think of this as filtering!
Select t.name, t.status, t.date
from table t
where t.date = (select max(t2.date)
from table t2
where t2.name = t.name
);
You are not aggregating anything. Your result set just wants columns from one row, the row with the maximum date for each name. That is more like filtering than grouping.
With not exists:
select t.* from tablename t
where not exists (
select 1 from tablename
where name = t.name and date > t.date
)
The result is:
every row of the table for which there is not another row with the same name and later date.
For MySql 8.0+ you can use ROW_NUMBER() window function:
select t.id, t.name, t.status, t.date
from (
select *, row_number() over (partition by name order by date desc) rn
from tablename
) t
where t.rn = 1
Maria DB 10.2 apparently. – Ed Jones
SELECT DISTINCT name,
FIRST_VALUE(status) OVER (PARTITION BY name
ORDER BY date DESC) status,
MAX(date) OVER (PARTITION BY name) date
FROM table;
The index by (name, data) will increase the performance.

First 5 entries for a single user

I am using PostgreSQL. I need to get the dates for the first 5 transactions of every user on my DB.
Transaction - trans.id, trans.date, trans.cust_id, trans.value
Customer - cust.id, cust.created_at
I need to get the date of the first 5 transactions for all the customers.
Try this query:
SELECT cust_id, date
FROM (
SELECT cust_id,
date,
row_number() OVER (partition by cust_id
ORDER BY date, id ) rn
FROM Transaction
) as alias
WHERE rn <= 5
ORDER BY 1,2
demo: http://sqlfiddle.com/#!15/cfd2e/4

mysql query sorting and ordering

Say i have a table called "users" and it has 40 rows of records, each row has fields:
id, firstname, group_id, login_count, stay_on_page_count
groups has
administrator (1), manager (2), employee (3)
is it possible to create a query that will sort and order the rows this way
group _id stay_on_page_count login_count
========= ================== ===========
1 100mins 100
1 90mins 90
2 100mins 100
3 100mins 100
1 80mins 80
1 70mins 70
2 90mins 90
3 90mins 90
1 60mins 60
1 50mins 50
2 80mins 80
3 80mins 80
1 40mins 40
1 30mins 30
2 70mins 70
3 70mins 70
Basically I would like to create a 4x4 grid view using the query result. the pseudo code is probably
SELECT all FROM user table and to group the result in to cluster of 4,
while each 4 should have ORDER BY group_id ASC as first priority (1,1,2,3)
AND stay_on_page_count ORDER BY DESC as second priority,
AND login_count ORDER BY DESC as last or third priority
i don't know if the pseudo code explains enough, but that's the only thing i can came up with :)
And if its possible, then will it sacrifice performance?
Is there any better approach to accomplish this?
I am using Mysql and PHP (CakePHP 2.x)
Thanks
One approach (using summarised as a table holding the summarised values listed in the question):
select * from
(select s1.*,
#rank1:=#rank1+1 as rankcalc,
floor(#rank1/2) rankgroup,
#rank1%2 rankingroup
from (select #rank1:=1) r1
cross join
(select * from summarised where group_id=1 order by stay_on_page_count desc) s1
union all
select s2.*,
#rank2:=#rank2+1 as rankcalc,
#rank2 rankgroup,
2 rankingroup
from (select #rank2:=0) r2
cross join
(select * from summarised where group_id=2 order by stay_on_page_count desc) s2
union all
select s3.*,
#rank3:=#rank3+1 as rankcalc,
#rank3 rankgroup,
3 rankingroup
from (select #rank3:=0) r3
cross join
(select * from summarised where group_id=3 order by stay_on_page_count desc) s3
) sq order by rankgroup, rankingroup
SQLFiddle here.
Note that this solution is dependent on the ordering being evaluated in the sequence specified in the sub-queries, and not overridden by the optimizer - this should work in current versions of MySQL, but may not work in MariaDB (the open source fork of MySQL) or future versions of MySQL.
While this will generate the order you want it's very much a forced and hard coded effort. It makes several assumptions like login_count is always in the ranges you listed.
SELECT group_ID, Stay_on_Page_count, Login_Count, myset
FROM (
SELECT group_ID, Stay_on_Page_Count, Login_Count, 1 as mySet
FROM USERS
WHERE (GROUP_ID=1 and Login_count >= 90) OR (group_ID in (2,3) and Login_Count=100)
UNION
SELECT group_ID, Stay_on_Page_Count, Login_Count, 2 as mySet
FROM USERS
WHERE (GROUP_ID=1 and Login_count Between 70 and 80)
OR (group_ID in (2,3) and Login_Count=90)
UNION
SELECT group_ID, Stay_on_Page_Count, Login_Count, 3 as mySet
FROM USERS
WHERE (GROUP_ID=1 and Login_count Between 50 and 60)
OR (group_ID in (2,3) and Login_Count=80)
UNION
SELECT group_ID, Stay_on_Page_Count, Login_Count, 4 as mySet
FROM USERS
WHERE (GROUP_ID=1 and Login_count Between 30 and 40)
OR (group_ID in (2,3) and Login_Count=70))
ORDER BY myset, group_ID, Login_count Desc