Select the first row after order by using distinct value - mysql

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;

Related

MySQL Query to get each sales per month

I have 2 tables in Mysql. I want to regroup and count the Number of Orderid per month for each customer. If there is no order, I would like to add 0.
Customer Table
CustomerID
1
2
3
Order Table
OrderId CustomerID Date
1 1 2022-01-02
2 1 2022-01-04
3 2 2022-02-03
4 2 2022-03-03
Expect results
CustomerID Date CountOrderID
1 2022-01 2
2 2022-01 1
3 2022-01 0
1 2022-02 0
2 2022-02 1
3 2022-02 0
1 2022-03 0
2 2022-03 1
3 2022-03 0
How I can do this in Mysql?
SELECT customer.CustomerID,
year_month.y_m AS `Date`,
COUNT(order.OrderId) AS CountOrderID
FROM customer
CROSS JOIN (
SELECT DISTINCT DATE_FORMAT(`date`, '%Y-%m') AS y_m
FROM order
) AS year_month
LEFT JOIN order ON order.CustomerID = customer.CustomerID
AND DATE_FORMAT(order.`date`, '%Y-%m') = year_month.y_m
GROUP BY 1, 2;
If order table does not contains for some year and month then according row won't present in the output. If you need in it then you'd generate calendar table instead of year_month subquery.
you can reduce the number of cte's I added more here to explain the steps:
first you need the format year and month, for that I used DATE_FORMAT() function
since you need to have all the combination of dates and the year month you need a cross join. This will produce all the distinct dates with all the distinct customer id's. In other words all the pairs between dates and customer id
once you have a table with all the combinations you need to pass the actual data with the left join this will produce null where you actually don't have rows and hence will produce 0 when the count is performed
the last step is simply count function
with main as (
select distinct DATE_FORMAT(date,'%Y-%m') as year_month from order
),
calendar as (
select * from customer
cross join main
),
joining_all as (
select
calendar.*,
order. OrderId
left join order
on calendar.CustomerID = order.CustomerID
and calendar.year_month = DATE_FORMAT(order.date,'%Y-%m')
)
select
CustomerID,
year_month as Date,
count(OrderId) as CountOrderID
from joining_all
group by 1,2
maybe the shorter version can work with the code below. if runs into syntax you can use the one above
with main as (
select distinct DATE_FORMAT(date,'%Y-%m') as year_month from order
cross join customer
)
select
main.CustomerID,
main.year_month as Date,
count(order.OrderId) as CountOrderID
from main
left join order
on main.CustomerID = order.CustomerID
and main.year_month = DATE_FORMAT(order.date,'%Y-%m')
group by 1,2

Finding rows with latest date by id mysql

I have these 2 tables I am trying to write a query that will help me select all rows that gives this result
users
id
name
1
test
2
test2
logs
id
userId
message
date
1
1
this is a test
2020-10-07 12:57:14
2
1
another reason
2020-10-07 13:57:14
3
1
another reason 2
2020-10-07 14:57:14
4
2
another reason 3
2020-10-04 12:57:14
5
2
another reason 4
2020-10-05 12:57:14
6
2
another reason 4
2020-10-06 12:57:14
Output Table
I need to pass many user Ids like in this case (1,2) and get below table only return MAX (date) per row per userId
id
userId
message
date
3
1
another reason 2
2020-10-07 14:57:14
6
2
another reason 4
2020-10-06 12:57:14
Is this possible with one Query? This what I have but not working
SELECT
id ,
userId ,
message,
date
FROM logs
WHERE userId IN (1,2)
ORDER BY date DESC;
You may use the results of a window function ROW_NUMBER to retrieve these results.
SELECT
id,userId,message,date
FROM (
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY userId
ORDER BY date DESC
) as rn
FROM
logs
) t
WHERE rn=1 AND
userId IN (1,2)
ORDER BY date DESC
and for older mysql versions
SELECT
id,userId,message,date
FROM (
SELECT
l.*,
#row_num:=IF(userId=#prev_user_id,#row_num+1,1) as rn,
#prev_user_id:=userId
FROM
logs l
CROSS JOIN (
SELECT #row_num:=0, #prev_user_id:=NULL
) as vars
ORDER BY userId, date DESC
) t
WHERE rn=1 AND
userId IN (1,2)
ORDER BY date DESC

Selecting the latest row for each customer that matches these params

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.

MySQL query that selects all users that have more than one entry per day

I want to get all the USER_ID for users who have posted more than one thing per day,
I tried originally tried this
SELECT USER_ID, count(DISTINCT cast(POSTING_DATE as DATE))
AS NUM_DAYS_OF_DUPLICATES FROM POSTING_TABLE
WHERE USER_ID IN
(SELECT USER_ID FROM POSTING_TABLE
GROUP BY CAST(POSTING_DATE AS DATE) HAVING count(*) >= 2)
GROUP BY USER_ID ORDER BY NUM_DAYS_OF_DUPLICATES DESC;
Then this works for a specific USER_ID
SELECT USER_ID FROM POSTING_TABLE WHERE USER_ID = 30
GROUP BY cast(POSTING_DATE AS DATE)
HAVING count(cast(POSTING_DATE AS DATE)) > 1
The above gives me the correct result, however when I run the query on the entire table without specifying a USER_ID it does not.
eg.,
table structure USER_ID, POSTING_DATE ...
USER_ID POSTING_DATE
1 10-10-13
1 10-10-13
1 10-12-13
1 10-12-13
2 10-10-13
2 10-10-13
3 10-10-13
4 10-12-13
Where the result would give me
USER_ID NUM_DAYS_WITH_MORE_THAN_ONE_POSTING
1 2
2 1
3 0
4 0
Also if we can omit the 0's
This is the solution
select x.user_id, count(x.num_days)
from
(
select USER_ID, COUNT(USER_ID) AS NUM_DAYS
from data1
group by user_id, posting_date
having count(user_id) > 1
) x
group by 1
Working SQL Fiddle
(I used a varchar for date for simplicity but it should work fine with date too. You can check with your own database)

count no of instance of tuple with same value in some attribute

i have following database
=================================
**id(PK) orderid email_content**
----------------------------------------------
1 6544 complain regarding service
2 6544 request for replacement
3 6544 complain for late delievery
4 9822 thanking note
5 5762 faulty product
6 5762 complain for
what i need is no of time interaaction per order
that is for each orderid , how many time email is present
for example for 6544 order4 id we have 3 emails..and like wise
orderid no_of_iteraction
-------------------------------
6544 3
9822 1
5762 2
please suggest appropriate mysql query
SELECT count(id) as no_of_iterations, orderid from mytable group by orderid
Try this:
SELECT orderid, COUNT(orderid) no_of_iteraction
FROM tblTemp
GROUP BY orderid
OR
As per your request using SUM function
SELECT orderid, SUM(1) no_of_iteraction
FROM tblTemp
GROUP BY orderid
OR
SELECT orderid, SUM(cnt)
FROM (SELECT orderid, 1 cnt FROM tblTemp ORDER BY orderid) AS A
GROUP BY orderid