MTD and YTD of Sales SQL - sql-server-2014

I have a table called Sales_History
What I need to be able to do is populate a YTD and a MTD for certain sales peoples.
Fields i have to play with are :
Salesperson_Invoiced (needs to be grouped by)
NetNet_Revenue_Func (need MTD and YTD figure)
GM_Func_Net (need MTD and YTD figure)
some other fields ive used in a where clause
my first atempt was this :
Select t.Salesperson_Invoiced,
Sum(y.NetNet_Revenue_Func) YTD_REV,
Sum(m.NetNet_Revenue_Func) MTD_REV
From Sales_History t
join Sales_History y
on y.Salesperson_Invoiced = t.Salesperson_Invoiced
and datediff(year, y.TranDate, t.TranDate) = 0
and y.TranDate <= t.TranDate
join Sales_History m
on m.Salesperson_Invoiced = t.Salesperson_Invoiced
and datediff(month, m.TranDate, t.TranDate) = 0
and m.TranDate <= t.TranDate
where t.PG1 = 'Lighting' and t.Office = 'AU' and t.Year = '2021'
Group by t.Salesperson_Invoiced, t.TranDate
but this did not give me what i wanted :(
Want i want to see is something like :
+----------------------+-------------------------+-------------------------+-----------------+-----------------+
| Salesperson_Invoiced | NetNet_Revenue_Func MTD | NetNet_Revenue_Func YTD | GM_Func_Net MTD | GM_Func_Net YTD |
+----------------------+-------------------------+-------------------------+-----------------+-----------------+
| James | 500 | 600 | | |
+----------------------+-------------------------+-------------------------+-----------------+-----------------+
| John | 600 | 700 | | |
+----------------------+-------------------------+-------------------------+-----------------+-----------------+
| Peter | 700 | 800 | | |
+----------------------+-------------------------+-------------------------+-----------------+-----------------+
| Harry | 800 | 900 | | |
+----------------------+-------------------------+-------------------------+-----------------+-----------------+
| Potter | 900 | 1000 | | |
+----------------------+-------------------------+-------------------------+-----------------+-----------------+
I know the code above only has NetNet_Revenue_Func thought i would get one workign first before adding the other.
If anyone can please let me know what im doing wrong or tell me if im going about this all wrong that would be great ! :)
Hello All,
I have re worked the code a little to the following thanks to Squirel
Would this be the best possible way to do what i'm doing :)?
Select t.Salesperson_Invoiced,
SUM (Case When t.Year = Year(getdate()) Then t.NetNet_Revenue_Func End) YTD_REV,
Sum(Case When month(t.TranDate) = Month(getdate()) Then t.NetNet_Revenue_Func
End) MTD_REV
From Sales_History t
Where t.PG1 = 'Lighting'
And t.Office = 'AU'
And t.Year = Year(getdate())
Group By t.Salesperson_Invoiced
Update -
Here is a working SQL
Select t.Salesperson_Invoiced,
Sum(Case When month(t.TranDate) = Month(getdate()) Then t.NetNet_Revenue_Func End) MTD_REV,
Sum(Case When month(t.TranDate) = Month(getdate()) Then t.GM_Func_Net End) MTD_GM,
SUM (Case When t.Year = Year(getdate()) Then t.NetNet_Revenue_Func End) YTD_REV,
SUM (Case When t.Year = Year(getdate()) Then t.GM_Func_Net End) YTD_GM
From Sales_History t
Where t.PG1 = 'Lighting'
And t.Office = 'AU'
And t.Year = Year(getdate())
Group By t.Salesperson_Invoiced
Is it possible for me to add the follow
is it possible for me to add the following
SELECT Salesperson_1,sum(Value_Func) as BO_AUD
FROM Datawarehouse.dbo.Open_Orders
where Office = 'AU' and PG1 = 'Lighting'
group by Salesperson_1
so I can have it look like this ?
+----------------------+-------------------------+-------------------------+-----------------+-----------------+--------+
| Salesperson_Invoiced | NetNet_Revenue_Func MTD | NetNet_Revenue_Func YTD | GM_Func_Net MTD | GM_Func_Net YTD | BO_AUD |
+----------------------+-------------------------+-------------------------+-----------------+-----------------+--------+
| James | 500 | 100 | | | |
+----------------------+-------------------------+-------------------------+-----------------+-----------------+--------+
| John | 600 | 200 | | | |
+----------------------+-------------------------+-------------------------+-----------------+-----------------+--------+
| Peter | 700 | 300 | | | |
+----------------------+-------------------------+-------------------------+-----------------+-----------------+--------+
| Harry | 800 | 400 | | | |
+----------------------+-------------------------+-------------------------+-----------------+-----------------+--------+
| Potter | 900 | 1 | | | |
+----------------------+-------------------------+-------------------------+-----------------+-----------------+--------+

If I understand correctly your requirement, you can simplified your query to below.
You don't need to JOIN the Sales_History multiple times. You can use CASE expression to perform the conditional SUM() to get the MTD value. Assuming by MTD you mean current month
Select t.Salesperson_Invoiced,
Sum(t.NetNet_Revenue_Func) YTD_REV,
Sum(Case When month(t.TransDate) = Month(getdate())
Then t.NetNet_Revenue_Func
End) MTD_REV
From Sales_History t
Where t.PG1 = 'Lighting'
And t.Office = 'AU'
And t.Year = 2021
Group By t.Salesperson_Invoiced
For YTD calculation, you can use below expression.
SUM (Case When t.Year = Year(getdate())
Then t.NetNet_Revenue_Func
End) YTD_REV
Since you have the Year column in the table, I am using that. Else you can also do
SUM (Case When Year(t.TransDate) = Year(getdate())
Then t.NetNet_Revenue_Func
End) YTD_REV
Anyway, for performance reason, you should also filtered the required period in the WHERE like what you are doing now with t.Year = 2021
EDIT : Also fixed the table alias typo error in earlier query.
Note : Your Year column in the table should be int right ? So you don't need to use single quote on the year And t.Year = 2021

Related

Select count date from multiple where conditions for each day

I have lot of coupons. I would like to learn for each day how many coupons from each campaign have been received by users. But I cannot write something like assigned_date for each select row
SELECT count(id) as number_of_coupons,
DATE_FORMAT(assigned_date, '%d-%m-%Y') as date,
(SELECT COUNT(id) FROM coupon WHERE campaign_id = 1 AND assigned_date=THIS MUST BE SOMETHING) as campaign_1,
(SELECT COUNT(id) FROM coupon WHERE campaign_id = 2 AND assigned_date=THIS MUST BE SOMETHING) as campaign_2
FROM coupon
GROUP BY DATE_FORMAT(assigned_date, '%d-%m-%Y')
order by STR_TO_DATE(date, '%d-%m-%Y') DESC
So the result will be something like. How can I achieve this result?
+-------------------+------------+-------------+-----------+
| number of coupons | date | campaign_1 | campaign2 |
+-------------------+------------+-------------+-----------+
| 156 | 12-10-2019 | 6980 | 100 |
| 177 | 11-10-2019 | 6980 | 100 |
| 44 | 10-10-2019 | 6980 | 100 |
| 94 | 09-10-2019 | 6980 | 100 |
| 93 | 08-10-2019 | 6980 | 100 |
+-------------------+------------+-------------+-----------+
Not knowing what your data structure looks like, I can only speculate on what the solution should be. However, my guess is that a query such as the following is what you want:
SELECT COUNT(DISTINCT cv.id) as number_of_coupons,
DATE_FORMAT(cv.assigned_date, '%d-%m-%Y') as date,
SUM(CASE WHEN c.campaign_id = 1 THEN 1 ELSE 0 END) as campaign_1,
SUM(CASE WHEN c.campaign_id = 2 THEN 1 ELSE 0 END) as campaign_2
FROM coupon_vault cv LEFT JOIN
coupons c
ON cv.coupon_id = c.coupon_id
GROUP BY DATE_FORMAT(cv.assigned_date, '%d-%m-%Y')
ORDER BY MIN(cv.assigned_date);
It is quite possible that the COUNT(DISTINCT) is unnecessary and COUNT() would suffice.
Both Postgres and MySQL (your original tag) have reasonable alternatives for the SUM(CASE . . .). However, you have not specified your database, so I'm sticking with the code that works in both databases.

get specific data in different time period in mysql

I have a table like this
--------------------------------
|name | time | city |value |
--------------------------------
|a | 2018| rasht | 1.5 |
--------------------------------
|a | 2017| rasht | 2 |
--------------------------------
|a | 2018| tehran| 4 |
--------------------------------
|a | 2017| rasht | 3 |
--------------------------------
|a | 2018| rasht | 5 |
--------------------------------
|a | 2017| rasht | 2 |
--------------------------------
|b | 2018| tehran| 7 |
--------------------------------
i like to get data from this table like this:
name | city | total 2018 | total 2017
a |rasht | 6.5 |7
a |theran| 4 |0
b |theran| 7 |0
I am using query like this:
select A.name,city,A.sum(value) ,B.sum(value) from
(select * from tbl where year=2018 group by name,city)A,
(select * from tbl where year=2017 group by name,city)B
where A.name=B.name and A.city=B.city
but it didn't work and for records like b that dont have value in 2017 it returns nothing
As u can see my example here is very simple i made exact db in fiddle if u can see it in this address Fiddle
I also use this code:
select subzone, mvFeederName, SUM(CASE WHEN faultStartDate >= 13970901 and faultEndDate <= 13971101 and (subzone=10 ) THEN energyLost ELSE 0 END) AS `unplannedCurrentEnergyLost`, SUM(CASE WHEN faultStartDate >= 13960901 and faultEndDate <= 13961101 and (subzone=10 ) THEN energyLost ELSE 0 END) AS `unplannedLastYearEnergyLost`, count(CASE WHEN faultStartDate >= 13970901 and faultEndDate <= 13971101 and (subzone=10 ) THEN mvFeederName ELSE 0 END) AS `unplannedCurrentCount`, count(CASE WHEN faultStartDate >= 13960901 and faultEndDate <= 13961101 and (subzone=10 ) THEN mvFeederName ELSE 0 END) AS `unplannedLastYearCount` from faults_mv group by subzone,mvFeederName
it return zero for energyLost sumation col in two time period and count of mvFeederName in two thime period is the same that is not correct.
Use conditional aggregation -
select name, city,
sum(case when time=2018 then value end) 'total 2018',
sum(case when time=2017 then value end) 'total 2017'
from tablename
group by name, city
You can use conditional aggregation to get the result you want:
SELECT name, city,
SUM(CASE WHEN time = 2018 THEN value ELSE 0 END) AS `total 2018`,
SUM(CASE WHEN time = 2017 THEN value ELSE 0 END) AS `total 2017`
FROM tbl
GROUP BY name, city
Output:
name city total 2018 total 2017
a rasht 6.5 7
a tehran 4 0
b tehran 7 0
Demo on dbfiddle

MySQL How to convert second group by parameter results as a column?

The following MySQL query result set repeats MerchantID:
select MerchantID,Category,SUM(Amount)
group by MerchantID,Category
from merchants
This will produce the following result:
MerchantID | Category | Total |
1 | gold | 450 |
1 | silver | 600 |
2 | gold | 1120 |
2 | bronze | 200 |
I want to get the result set like below:
MerchantID | gold | silver | bronze |
1 | 450 | 600 | 0 |
2 | 1120 | 0 | 200 |
I tried many queries but could not get such result. Please provide a solution for my problem. Thanks.
Use conditional aggregation:
select MerchantID,
SUM(CASE WHEN Category = 'gold' THEN Total ELSE 0 END) AS gold,
SUM(CASE WHEN Category = 'silver' THEN Total ELSE 0 END) AS silver,
SUM(CASE WHEN Category = 'bronze' THEN Total ELSE 0 END) AS bronze
from merchants
group by MerchantID
Demo here
You can use this code for example:
SELECT m.merchantId
sum(if(m.Category='gold',m.total,null)) as gold,
sum(if(m.Category='silver',m.total,null)) as silver,
sum(if(m.Category='bronze',m.total,null)) as bronze
FROM merchants m
GROUP BY m.merchantId

How can I perform cumulative calculations over a date range in SQL? (e.g. calculation for 01 uses data from 01, 02 uses 01 and 02, and so on)

Context
I would like to perform cumulative calculations over a selected date range. (i.e. 2014-07-01 (henceforth referred to as 01 for simplicity) would perform a calculation on data in 01 only. 02 would perform a calculation using data from 01 and 02. 03 would use data from 01, 02, and 03. And so on.)
Detail
I have a record of every sale made in a store and whether or not a complaint has been made. It is easy enough to generate the following table with the query below -
SELECT
Date,
COUNT(*) AS Sales,
SUM(CASE WHEN ComplaintMade = 'True' THEN 1 ELSE 0 END) AS Complaints
FROM SalesRecords
WHERE Date BETWEEN '2014-07-01' AND '2014-07-05'
GROUP BY Date
ORDER BY Date
--------------------------------------
| Date | Sales | Complaints |
--------------------------------------
| 2014-07-01 | 100 | 2 |
| 2014-07-02 | 150 | 6 |
| 2014-07-03 | 180 | 9 |
| 2014-07-04 | 140 | 10 |
| 2014-07-05 | 300 | 15 |
--------------------------------------
Calculating the average number of Sales per Complaint is easy enough to calculate by extending this query and selecting the following -
COUNT(*)
/
CASE WHEN(SUM(CASE WHEN ComplaintMade = 'True' THEN 1 ELSE 0 END)) = 0
THEN 1
ELSE (SUM(CASE WHEN ComplaintMade = 'True' THEN 1 ELSE 0 END))
END)
AS SalesPerComplaint
(This shows the number of sales per complaint made (Sales / Complaints), or, if no complaints have been made, the number of Sales (Sales/1))
This would display the following table.
----------------------------------------------------------
| Date | Sales | Complaints | SalesPerComplaint |
---------------------------------------------------------|
| 2014-07-01 | 100 | 2 | 50 |
| 2014-07-02 | 150 | 6 | 25 |
| 2014-07-03 | 180 | 9 | 20 |
| 2014-07-04 | 140 | 10 | 14 |
| 2014-07-05 | 300 | 15 | 20 |
---------------------------------------------------------|
What I would like to do is show the cumulative SalesPerComplaint. So, for 2014-07-01, take the Sales for 2014-07-01 divided by the number of Complaints for 2014-07-01. But, for 2014-07-02 take the Sales for 01 and 02, divided by the Complaints for 01 and 02. And for 03 use the data from 01, 02 and 03. And so on.
So, the first couple of rows of the table would look like -
--------------------------------------------------------------------
| Date | Sales | Complaints | CumulativeSalesPerComplaint |
-------------------------------------------------------------------|
| 2014-07-01 | 100 | 2 | 50 |
| 2014-07-02 | 150 | 6 | 31.25 |
-------------------------------------------------------------------|
(The CumulativeSalesPerComplaint for 2014-07-02 is now 31.25 because it is calculated using the Sales from both Dates so far (100 + 150 = 250) divided by the number of Complaints from both Dates so far (2 + 6 = 8) (250/8 = 31.25))
(Please let me know if I could improve this question formatting. I wanted to make this as clear as possible but if I've included too much detail or a confusing structure please let me know and I will gladly improve it. Thank you.)
My sqlfiddle is at sqlfiddle.com/#!2/9e2ad/5
What about this (SqlFiddle)?
In this query, you are joining the distinct dates you have with the data you've already obtained:
SELECT sr.uniqueday Date, qry.Sales, qry.Complaints,
SUM(Sales)/SUM(Complaints) CumulativeSalesPerComplaint
FROM (SELECT DISTINCT(Date) uniqueday FROM SalesRecords) sr
LEFT JOIN (
SELECT
Date,
COUNT(*) AS Sales,
SUM(CASE WHEN ComplaintMade = 'True' THEN 1 ELSE 0 END) AS Complaints,
((COUNT(*)) / (CASE WHEN (SUM( CASE WHEN ComplaintMade = 'True' THEN 1 ELSE 0 END )) = 0
THEN 1
ELSE (SUM( CASE WHEN ComplaintMade = 'True' THEN 1 ELSE 0 END ))
END)
) AS SalesPerComplaint
FROM SalesRecords
WHERE Date BETWEEN '2014-07-01' AND '2014-07-05'
GROUP BY Date
ORDER BY Date DESC
) qry ON qry.Date <= sr.uniqueday
GROUP BY sr.uniqueday
The trick is to join the two tables (one containing only the several days, the other with your data) with a join with the condition "qry.Date <= sr.uniqueday", so for a single uniquedate you are joining all the previous rows.

SQL statement with two sums and inner joins of three tables

I have three tables in my mysql database
Table1 -> stockinfo
stock_id | stock_code | balance
1 | stock_01 | 545
2 | stock_02 | 105
3 | stock_03 | 70
Table2 -> stock_in
stock_id | quantity | date_time
1 | 50 | 2013-11-23
1 | 250 | 2013-12-25
1 | 300 | 2013-12-28
2 | 100 | 2013-11-24
2 | 150 | 2013-12-25
3 | 30 | 2013-12-29
3 | 40 | 2013-12-31
Table3 -> stock_out
stock_id | quantity | date_time
1 | 20 | 2013-12-23
1 | 35 | 2013-12-25
2 | 70 | 2013-11-24
2 | 75 | 2013-12-25
The first table stockinfo contains the stock information such as stock_code and balance, balance means the remaining quantity left for a stock. stock_in table contains all the stock in of stock item with different dates and time. stock_out tables contains all the stock out of stock item with different dates and time. I want an output similar to the table below. Total stock in and total stock out at a given date range.
Output : (I want to show total in and total out for the month of december 2013 only.)
stock_code | total_in | total_out | balance
1 | 550 | 55 | 545
2 | 150 | 75 | 105
3 | 70 | 0 | 70
Here is my sql statement :
Select stock_code,
sum(case when stock_in.date_time >= '2013-12-01' and stock_in.date_time <= '2013-12-31' then stock_in.quantity else 0 end) as Total_In,
sum(case when stock_out.date_time >= '2013-12-01' and stock_out.date_time <= '2013-12-31' then stock_out.quantity else 0 end) as Total_Out, balance from stockinfo
left join stock_in on stockinfo.stock_id = stock_in.stock_id
left join stock_out on stockinfo.stock_id = stock_out.stock_id
group by stock_code
For the output, I am getting incorrect total for total_in and total_out. I'm not sure which part I had it wrong. If I only query the total_in and remove the total_out, it is working fine. Likewise, if i only query the total_out and remove the total_in part, it is also working. Please advice me which part I might have error in my SQL. Thanks.
Try this:
SELECT s.stock_id, s.stock_code, IFNULL(si.Total_In, 0) Total_In,
IFNULL(so.Total_Out, 0) Total_Out, s.balance
FROM stockinfo s
LEFT JOIN (SELECT si.stock_id, SUM(si.quantity) Total_In
FROM stock_in si
WHERE si.date_time BETWEEN '2013-12-01' AND '2013-12-31'
GROUP BY si.stock_id
) si ON s.stock_id = si.stock_id
LEFT JOIN (SELECT so.stock_id, SUM(so.quantity) Total_Out
FROM stock_out so
WHERE so.date_time BETWEEN '2013-12-01' AND '2013-12-31'
GROUP BY so.stock_id
) so ON s.stock_id = so.stock_id
GROUP BY s.stock_id