How to stop this repetition and group by date - mysql

After multiple attempts I'm unable to get my required result from this query.
SELECT
cc.date as credi_date,
cd.date as debit_date,
cd.month AS month,
ROUND(IFNULL(cc.credit_amount,0),2) AS credit,
ROUND(IFNULL(cd.debit_amount,0),2) AS debit
FROM
(SELECT
DATE(cc.credit_date) as date,
MONTHNAME(cc.credit_date) as month,
IFNULL(SUM(cc.credit_amount),0) AS credit_amount
FROM
cust_credit cc
WHERE YEAR(cc.credit_date) = YEAR(NOW())
GROUP BY DATE(cc.credit_date)) cc
INNER JOIN
(SELECT
DATE(cd.debit_date) as date,
MONTHNAME(cd.debit_date) as month,
IFNULL(SUM(cd.debit_amount),0) AS debit_amount
FROM
cust_debit cd
WHERE YEAR(cd.debit_date) = YEAR(NOW())
GROUP BY DATE(cd.debit_date)) cd ON cc.month=cd.month
The problem is that this query repeats the rows multiple times. I don't know what thing cause this repetition and how can fix this. The image of result is given below.
My required result is
My credit table is give below
My debit table is give below

Yon should use a UNION in the subquery and then GROUP BY in the SELECT:
SELECT my_date,
my_month,
SUM(credit),
SUM(debit)
FROM (
SELECT cc.credit_date as my_date,
MONTHNAME(cc.credit_date) as my_month,
ROUND(SUM(IFNULL(cc.credit_amount,0)),2) AS credit,
0 AS debit
FROM cust_credit cc
WHERE YEAR(cc.credit_date) = YEAR(NOW())
GROUP BY cc.credit_date,
MONTHNAME(cc.credit_date)
UNION
SELECT
cd.debit_date ,
MONTHNAME(cd.debit_date) ,
0 ,
ROUND(SUM(IFNULL(cd.debit_amount,0)),2)
FROM cust_debit cd
WHERE YEAR(cd.debit_date) = YEAR(NOW())
GROUP BY cd.debit_date,
MONTHNAME(cd.debit_date)
) as TT
GROUP BY my_date,my_month
This is the Fiddle

Basically, your result is cross join between two query and that's the reason your are getting cross product i.e. total number of rows from credit x total number of rows from debit.
Because, month for all rows in debit and credit tables are same (i.e. January). But, you have unique date in both table
So, making below change should give you expected output.
ON cc.month=cd.month
to
ON cc.date = cd.date

Related

SQL Consecutive Monthly Purchases

I'm having great difficulty writing this query and cannot find any answers online which could be applied to my problem.
I have a couple of tables which looks similar to the below with. Each purchase date corresponds with an item purchased.
Cust_ID
Purchase_Date
123
08/01/2022
123
08/20/2022
123
09/05/2022
123
10/08/2022
123
12/25/2022
123
01/26/2023
The result I am looking for should contain the customers ID, a range of the purchases, the number of consecutive months they had made a purchase (regardless of which day they purchased), and a count of how many purchases they had made in the time frame. The result should look something like the below for my example.
Cust_ID
Min Purchase Date
Max Purchase Date
Consecutive Months
No. Items Purchased
123
08/01/2022
10/08/2022
3
4
123
12/25/2022
01/26/2023
2
2
I have tried using CTEs with querys similar to
WITH CTE as
(
SELECT
PaymentDate PD,
CustomerID CustID,
DATEADD(m, -ROW_NUMBER() OVER (PARTITION BY c.CustomerID ORDER BY
DATEPART(m,PaymentDate)), PaymentDate) as TempCol1,
FROM customers as c
LEFT JOIN payments as p on c.customerid = p.customerid
GROUP BY c.CustomerID, p.PaymentDate
)
SELECT
CustID,
MIN(PD) AS MinPaymentDate,
MAX(PD) AS MaxPaymentDate,
COUNT(*) as ConsecutiveMonths,
FROM CTE
GROUP BY CustID, TempCol1
However, the above failed to properly count consecutive months. When the payment dates matched a month apart (e.g. 1/1/22 - 2/1/22), the query properly counts the consecutive months. However, if the dates do not match from month to month (e.g. 1/5/22 - 2/15/22), the count breaks.
Any guidance/help would be much appreciated!
This is just a small enhancement on the answer already given by ahmed. If your date range for this query is more than a year, then year(M.Purchase_Date) + month(M.Purchase_Date) will be 2024 for both 2022-02-01 and 2023-01-01 as YEAR() and MONTH() both return integer values. This will return incorrect count of consecutive months. You can change this to use CONCAT() or FORMAT(). Also, the COUNT(*) for ItemsPurchased should be counting the right hand side of the join, as it is a LEFT JOIN.
WITH consecutive_months AS
(
SELECT *,
DATEADD(
month,
-DENSE_RANK() OVER (
PARTITION BY CustomerID
ORDER BY YEAR(PaymentDate), MONTH(PaymentDate)
),
PaymentDate
) AS grp_date
FROM payments
)
SELECT
C.CustomerID AS CustID,
MIN(M.PaymentDate) AS MinPaymentDate,
MAX(M.PaymentDate) AS MaxPaymentDate,
COUNT(DISTINCT FORMAT(M.PaymentDate, 'yyyyMM')) AS ConsecutiveMonths,
COUNT(M.CustomerID) AS ItemsPurchased
FROM customers C
LEFT JOIN consecutive_months M
ON C.CustomerID = M.CustomerID
GROUP BY C.CustomerID, YEAR(M.grp_date), MONTH(M.grp_date)
Here's a db<>fiddle
You need to use the dense_rank function instead of the row_number, this will give the same rank for the same months and avoid breaking the grouping column. Also, you need to aggregate for 'year-month' of the grouping date column.
with consecutive_months as
(
select *,
Purchase_Date - interval
dense_rank() over (partition by Cust_ID order by year(Purchase_Date), month(Purchase_Date))
month as grp_date
from payments
)
select C.Cust_ID,
min(M.Purchase_Date) as MinPurchaseDate,
max(M.Purchase_Date) as MaxPurchaseDate,
count(distinct year(M.Purchase_Date), month(M.Purchase_Date)) as ConsecutiveMonthsNo,
count(M.Cust_ID) as ItemsPurchased
from customers C left join consecutive_months M
on C.Cust_ID = M.Cust_ID
group by C.Cust_ID, year(M.grp_date), month(M.grp_date)
See demo on MySQL
You tagged your question with MySQL, while it seems that you posted an SQL Server query syntax, for SQL Server just use dateadd(month, -dense_rank() over (partition by Cust_ID order by year(Purchase_Date), month(Purchase_Date)), Purchase_Date).
See demo on SQL Server.

How to calculate the percentage of different values in same column and group by month of current year?

I want to take the percentage of only SLA's in the sla_osla table and group them by all the months of current year
Here is my table
http://sqlfiddle.com/#!9/179518/2
I have tried the following query
SELECT MONTH(service_date),((nums/(count(*)))*100) AS perc
FROM orders CROSS JOIN (SELECT MONTH(service_date) AS months, COUNT(*) AS
nums FROM orders WHERE YEAR(service_date) = year(CURRENT_DATE()) AND sla_osla =
'sla'
GROUP BY MONTH(CURRENT_DATE())) table1
WHERE YEAR(service_date) = YEAR(current_date) AND sla_osla IN ('sla','osla')
GROUP BY MONTH(service_date)
I want output like this
month percentage
1 75%
2 50%
http://sqlfiddle.com/#!9/179518/76
This is more simple sql
Select year(service_date), month(service_date), sum(sla_osla = "sla")/ count(*) * 100 FROM orders group by year(service_date), month(service_date)
I've also grouped by year here.
Edit if you want the percent sign etc then use this
http://sqlfiddle.com/#!9/179518/82
SELECT month(service_date) as dmonth,count(1)
FROM orders where sla_osla='sla'
group by dmonth
http://sqlfiddle.com/#!9/179518/129
SELECT MONTH(service_date) month, CONCAT(FORMAT(SUM(sla_osla='sla')/COUNT(*)*100,0),'%') percentage FROM orders WHERE YEAR(CURDATE())=YEAR(service_date) GROUP BY MONTH(service_date);

How to find the maximum counts of clicks made by customers for a particular date range?

I have two tables.
Customers Table
ID, CustomerName
8977, John
8978, Oliver
8976, Graham
ClickInfo Table
Date, ID, CustomerID
2019-11-05, 123422, 8977
2019-11-05, 123433, 8977
2019-11-07, 123463, 8978
2019-11-05, 134556, 8976
What I need is for a date range from 2019-11-01 to 2019-11-10 (1st to 10th November),
Number of unique customers who click the website atleast once.
Print the CustomerID and CustomerName of the person who clicked maximum number of clicks each day. If there is more than one person who have the same maximum number of clicks, print the customer with the lowest CustomerID(in case of 8976 and 8978 we'll choose 8976 because it is lower). All this should be printed in ascending order of dates.
Now for the second part I have tried doing the following :
select ClickInfo.Date,
count(ClickInfo.CustomerID) as clicks,
Customers.ID,
Customers.CustomerName
from ClickInfo
left join Customers
on Customers.ID = ClickInfo.CustomerID
where Date between '2019-11-01' and '2019-11-10'
group by ClickInfo.Date
order by ClickInfo.Date ASC;
Now the result is giving me the Count of clicks for a particular day but not the max clicks and who did it. I am new to SQL and not able to think beyond this. Can you help here with explanation?
Group first by customerId not by date.
select ClickInfo.Date,
sum(case when coalesce(ClickInfo.CustomerID, 0) != 0 then 1 else 0 end) as clicks,
Customers.ID,
Customers.CustomerName
from ClickInfo
left join Customers on Customers.ID = ClickInfo.CustomerID
where Date between '2019-11-01' and '2019-11-10'
group by Customers.ID,
Customers.CustomerName, ClickInfo.Date
order by ClickInfo.Date ASC;
You can do what you want with window functions and aggregation:
select cc.*
from (select ci.Date, c.ID, c.CustomerName
count(*) as num_clicks_customer,
sum(count(*)) over (partition by ci.date) as total_clicks,
row_number() over (partition by ci.Date order by count(*) desc, c.id) as seqnum
from ClickInfo ci left join
Customers c
on c.ID = ci.CustomerID
where ci.Date between '2019-11-01' and '2019-11-10' and
ci.CustoemrId <> 0
group by ci.Date, c.ID
) cc
where seqnum = 1
order by ci.Date ASC;

add a column in MySQL rank by deal by day

I am just learning MySQL. I need to find out rank of deals by day. Here I am adding the corresponding MYSQL query for my requirement that currently ranks all sales highest to lowest by day. Please help me to add a column that gives the rank to the deal highest to lowest and resetting the next day.
Here is my current working query,..
single day with title, price
SELECT
DATE(order_items.created_at) AS the_day,
order_items.deal_id,
SUM(order_items.item_total) AS daily_total,
SUM(order_items.qty) AS units_sold,
deals.price,
deals.title
FROM
order_items JOIN deals ON order_items.deal_id = deals.id
WHERE
order_items.created_at >= '2016-01-01 00:00:00' AND order_items.created_at < '2016-01-30 00:00:00'
AND
order_items.status=1
AND
order_items.paid=1
GROUP BY
order_items.deal_id
ORDER BY
the_day,
daily_total DESC;
The easiest way to do is that:
Use your existing SQL - I guess you need to update your SQL, make sure any non-aggregated columns in select should be in group by as well
Use PHP to loop (1-5), it works for multiple days
If you are happy to get top 5 for a single day, you can add limit 5 at end of your SQL
If you need top 5 results for each day in multiple days in one SQL, you need to update SQL to be more complicated. And here is a hint to use row id see example:
select increment counter in mysql
OK - Since you updated your question to return top 1 result per day, this is easier:
Step 1: get each day, each deal, report:
SELECT deal_id, date(created_at) ymd, sum(item_total) daily_total, sum(qty) units_sold
FROM order_items
WHERE substr(created_at,1,7) = '2016-01'
AND status = 1
AND paid = 1
GROUP BY 1,2
Step 2: Find the best deal of each day from step 1:
SELECT aa.ymd, max(aa.daily_total) max_total
FROM (
SELECT deal_id, date(created_at) ymd, sum(item_total) daily_total, sum(qty) units_sold
FROM order_items
WHERE substr(created_at,1,7) = '2016-01'
AND status = 1
AND paid = 1
GROUP BY 1,2
) as aa
GROUP BY 1;
Please note that max(item_total) not necessary same row as max(unit_sold), so you need to choose one, and cannot run togather
Step 3: Join step 2 with step 1 and deals to find out the rest of information:
SELECT aa.*, deals.price, deal.title
FROM (
SELECT aa.ymd, max(aa.daily_total) max_total
FROM (
SELECT deal_id, date(created_at) ymd, sum(item_total) daily_total, sum(qty) units_sold
FROM order_items
WHERE substr(created_at,1,7) = '2016-01'
AND status = 1
AND paid = 1
GROUP BY 1,2
) as aa
GROUP BY 1
) as bb
JOIN (
SELECT deal_id, date(created_at) ymd, sum(item_total) daily_total, sum(qty) units_sold
FROM order_items
WHERE substr(created_at,1,7) = '2016-01'
AND status = 1
AND paid = 1
GROUP BY 1,2
) as aa ON bb.ymd = aa.ymd and bb.max_total = aa.daily_total
JOIN deals ON aa.deal_id = deals.id
ORDER BY aa.ymd, aa.max_total

MySQL Query with Multiple Dates

I'm trying to pull some activity reports for an application based on date ranges (number of "Stars" for each post)
It has a post table that includes and account id, and an "affiliate" table that ties that id to an account.
I know that I can do:
SELECT
posts.affid,
affiliates.name
sum(posts.stars) AS SEPT_2012
from posts
JOIN affiliates on posts.affid = affiliates.id
WHERE posts.timestamp BETWEEN '2012-09-01' AND '2012-10-01'
group by affid
That will pull a result that has the affiliate ID, Name and total "stars" from September. A single month
However, I'd like to do a pull that goes back and gets numbers for August, July, June and May that would display in a single query result (so the result would include affid, name, SEPT_2012, AUG_2012, JUL_2012...etc). Essentially, doing subqueries for those other date ranges, I assume.
Any suggestions?
Thanks for your help!
You probably want to GROUP BY EXTRACT(YEAR_MONTH FROM timestamp) (in addition to whatever else you want to do). Of course it will not get you the SEPT_2012, AUG_2012, etc. columns, but the data will be there.
While you won't be able to dynamically create the columns, you can "fake" them and use a UNION for each date range. Inside each UNION, you select 0 for the other date columns and the SUM() for the correct column.
Something similar to this should work:
SELECT
posts.affid,
affiliates.name,
sum(posts.stars) AS SEPT_2012,
0 AS AUG_2012,
0 AS JUL_2012
from
posts
JOIN affiliates on posts.affid = affiliates.id
WHERE
posts.timestamp BETWEEN '2012-09-01' AND '2012-10-01'
group by affid
UNION (
SELECT
posts.affid,
affiliates.name
0 AS SEPT_2012,
sum(posts.stars) AS AUG_2012,
0 AS JUL_2012
from
posts
JOIN affiliates on posts.affid = affiliates.id
WHERE
posts.timestamp BETWEEN '2012-08-01' AND '2012-09-01'
group by affid
)
UNION (
SELECT
posts.affid,
affiliates.name
0 AS SEPT_2012,
0 AS AUG_2012,
sum(posts.stars) AS JUL_2012
from
posts
JOIN affiliates on posts.affid = affiliates.id
WHERE
posts.timestamp BETWEEN '2012-07-01' AND '2012-08-01'
group by affid
)
UPDATE (to combine all results for each affid on a single row)
Per a comment, you would like to combine the results for each posts.affid on a single row with all of the data in each column. You can achieve this by putting an outer-query around the full query above and then using GROUP BY affid again. With this, you should have a single row for each affid and all of the columns as requested. I've updated the above query to select 0 for each empty column instead of null for "nicer" output too:
SELECT affid, name, SEPT_2012, AUG_2012, JUL_2012 FROM (
... full query above ...
) AS q
GROUP BY affid
UPDATE
To get the sum of all "stars" from all subqueries, the outer select statement works with:
SELECT affid, name, sum(SEPT_2012), sum(AUG_2012), sum(JUL_2012) FROM (
... full query above ...
) AS q
GROUP BY affid