AVG duplication SQL - sql

I'm currently having issues creating two columns with AVG for different date ranges.
I've tried the below code to try and resolve this.
WITH Tbl AS(
SELECT FORMAT(SaleDate,'MM')+'.'+FORMAT(SaleDate,'yyyy') AS SALE_MY, Employee, NewScheme
FROM Salereport
WHERE Business Area='Sales'
)
SELECT
AgentName,
(SELECT AVG(NewScheme) FROM Tbl WHERE SALE_MY='01.2019' OR SALE_MY='02.2019' OR SALE_MY='03.2019'),
(SELECT AVG(NewScheme) FROM Tbl WHERE SALE_MY='04.2019' OR SALE_MY='05.2019' OR SALE_MY='06.2019')
FROM Tbl
GROUP BY Employee
Result is just the same AVG for everyone.

Use conditional aggregation:
SELECT Employee,
AVG(CASE WHEN SaleDate >= '2019-01-01' AND SaleDate < '2019-04-01'
THEN NewScheme
END),
AVG(CASE WHEN SaleDate >= '2019-01-04' AND SaleDate < '2019-04-07'
THEN NewScheme
END),
FROM Salereport
WHERE Business Area = 'Sales'
GROUP BY Employee;
When working with dates, you should be using date operations. The only time you normally need to convert to a string is to format dates in the result set.
Incidentally, your version is taking the average across all employees. No subquery is needed, but if you were to use one, you would want a correlated subquery.

Try this:
WITH Tbl AS(
SELECT FORMAT(SaleDate,'MM')+'.'+FORMAT(SaleDate,'yyyy') AS SALE_MY, Employee, NewScheme
FROM Salereport
WHERE Business Area='Sales'
)
SELECT Employee,
AVG(CASE WHEN SALE_MY IN ('01.2019', '02.2019', '03.2019') THEN NewScheme ELSE NULL END) AS Q1_Avg,
AVG(CASE WHEN SALE_MY IN ('04.2019', '05.2019', '06.2019') THEN NewScheme ELSE NULL END) AS Q2_Avg
FROM Tbl
GROUP BY Employee

Related

CASE WHEN condition with MAX() function

There are a lot questions on CASE WHEN topic, but the closest my question is related to this How to use CASE WHEN condition with MAX() function query which has not been resolved.
Here is some of my sample data:
date
debet
2022-07-15
57190.33
2022-07-14
815616516.00
2022-07-15
40866.67
2022-07-14
1221510.00
So, I want to all records for the last two dates and three additional columns: sum(sales) for the previous day, sum for the current day and the difference between them:
SELECT
[debet],
[date] ,
SUM( CASE WHEN [date] = MAX(date) THEN [debet] ELSE 0 END ) AS sum_act,
SUM( CASE WHEN [date] = MAX(date) - 1 THEN [debet] ELSE 0 END ) AS sum_prev ,
(
SUM( CASE WHEN [date] = MAX(date) THEN [debet] ELSE 0 END )
-
SUM( CASE WHEN [date] = MAX(date) - 1 THEN [debet] ELSE 0 END )
) AS diff
FROM
Table
WHERE
[date] = ( SELECT MAX(date) FROM Table WHERE date < ( SELECT MAX(date) FROM Table) )
OR
[date] = ( SELECT MAX(date) FROM Table WHERE date = ( SELECT MAX(date) FROM Table ) )
GROUP BY
[date],
[debet]
Further, of course, it informs that I can't use the aggregate function inside CASE WHEN. Now I use this combination: sum(CASE WHEN [date] = dateadd(dd,-3,cast(getdate() as date)) THEN [debet] ELSE 0 END). But here every time I need to make an adjustment for weekends and holidays. The question is, is there any other way than using 'getdate' in 'case when' Statement to get max date?
Expected result:
date
sum_act
sum_prev
diff
2022-07-15
97190.33
0.00
97190.33
2022-07-14
0.00
508769.96
-508769.96
You can use dense_rank() to filter the last 2 dates in your table. After that you can use either conditional case expression with sum() to calculate the required value
select [date],
sum_act = sum(case when rn = 1 then [debet] else 0 end),
sum_prev = sum(case when rn = 2 then [debet] else 0 end),
diff = sum(case when rn = 1 then [debet] else 0 end)
- sum(case when rn = 2 then [debet] else 0 end)
from
(
select *, rn = dense_rank() over (order by [date] desc)
from tbl
) t
where rn <= 2
group by [date]
db<>fiddle demo
Two steps:
Get the sums for the last three dates
Show the results for the last two dates.
Well, we could also get all daily sums in step 1, but we just need the last three in order to calculate the sums for the last two days, so why aggregate more data than necessary?
Here is the query. You may have to put the date column name in brackets in SQL Server, as date is a keyword in SQL.
select top(2)
date,
sum_debit_current,
sum_debit_previous,
sum_debit_current - sum_debit_previous as diff
(
select
date,
sum(debet) as sum_debit_current,
lag(sum(debet)) over (order by date) as sum_debit_previous
from table
where date in (select distinct top(3) date from table order by date desc)
group by date
)
order by date desc;
(SQL Server uses TOP(n) instead of standard SQL FETCH FIRST 3 ROWS and while SELECT DISTINCT TOP(3) date looks like "get the top 3 rows, then apply distinct on their date", it is really "apply distinct on the dates, then get the top 3" like in standard SQL.)

Can't use column alias in GROUP BY

I can run this in mysql with no problem
SELECT
DATE_FORMAT(trans_date, '%Y-%m') month,
COUNTRY, COALESCE(COUNT(*), 0) trans_count,
COALESCE(SUM(CASE WHEN state ='approved' THEN 1 END), 0) approved_count,
COALESCE(SUM(amount), 0) trans_total_amount,
COALESCE(SUM(CASE WHEN state ='approved' THEN amount END), 0) approved_total_amount
FROM
Transactions
GROUP BY
month, COUNTRY
ORDER BY
month;
but the same query doesn't run in Orcale, I can't use GROUP BY using aggregation alias, and I can't aggregate without using GROUP BY.
I can call subquery over subquery or use CTE, but it is just so tedious.
What is a good query for type of issue?
As mentioned in another answer, You can not add aliases in GROUP BY but you can add aliases in ORDER BY. Also, DATE_FORMAT is MySql function. It is TO_CHAR in Oracle.
So your final query should be as following:
SELECT
TO_CHAR(TRANS_DATE, 'YYYY-MM') AS MONTH,
COUNTRY,
COUNT(*) AS TRANS_COUNT,
SUM(CASE WHEN STATE = 'approved' THEN 1 ELSE 0 END) AS APPROVED_COUNT,
SUM(AMOUNT) AS TRANS_TOTAL_AMOUNT,
SUM(CASE WHEN STATE = 'approved' THEN AMOUNT ELSE 0 END) AS APPROVED_TOTAL_AMOUNT
FROM TRANSACTIONS
GROUP BY TO_CHAR(TRANS_DATE, 'YYYY-MM'), COUNTRY
ORDER BY MONTH;
Oracle doesn't support aliases for the GROUP BY. Also, the COALESCE() is unnecessary in this case:
SELECT DATE_FORMAT(trans_date, '%Y-%m') as month, COUNTRY,
COUNT(*) as trans_count,
SUM(CASE WHEN state ='approved' THEN 1 ELSE 0 END) as approved_count,
SUM(amount) as trans_total_amount,
SUM(CASE WHEN state = 'approved' THEN amount ELSE 0 END) as approved_total_amount
FROM Transactions
GROUP BY DATE_FORMAT(trans_date, '%Y-%m'), COUNTRY
ORDER BY month;

MSSQL Group by and Select rows from grouping

I'm trying to figure out if what I'm trying to do is possible. Instead of resorting to multiple queries on a table, I wanted to group the records by business date and id then group by the id and select one date for a field and another date for the other field.
SELECT
*
{AMOUNT FROM DATE}
{AMOUNT FROM OTHER DATE}
FROM (
SELECT
date,
id,
SUM(amount) AS amount
FROM
table
GROUP BY id, date
AS subquery
GROUP BY id
It seems that you're looking to do a pivot query. I usually use cross tabs for this. Based on the query you posted, it could look like:
SELECT
id,
SUM(CASE WHEN date = '20190901' THEN amount ELSE 0 END) AmountFromSept01,
SUM(CASE WHEN date = '20191001' THEN amount ELSE 0 END) AmountFromOct01
FROM (
SELECT
date,
id,
SUM(amount) AS amount
FROM
table
GROUP BY id, date
)AS subquery
GROUP BY id;
You could also use a CTE.
WITH CTE AS(
SELECT
date,
id,
SUM(amount) AS amount
FROM
table
GROUP BY id, date
)
SELECT
id,
SUM(CASE WHEN date = '20190901' THEN amount ELSE 0 END) AmountFromSept01,
SUM(CASE WHEN date = '20191001' THEN amount ELSE 0 END) AmountFromOct01
FROM CTE
GROUP BY id;
Or even be a rebel and do the operation directly.
SELECT
id,
SUM(CASE WHEN date = '20190901' THEN amount ELSE 0 END) AmountFromSept01,
SUM(CASE WHEN date = '20191001' THEN amount ELSE 0 END) AmountFromOct01
FROM CTE
GROUP BY id;
However, some people have tested for performance and found that pre-aggregating can improve performance.
If I understand you correctly, then you're just trying to pivot, but only with two particular dates:
select id,
date1 = sum(iif(date = '2000-01-01', amount, null)),
date2 = sum(iif(date = '2000-01-02', amount, null))
from [table]
group by id

Aggregation between dates

I have the below query where I'm trying to calculate sum of salaries for over a period of year..
select sum(case when date_key between to_char(sysdate,'yyyymm')
and to_char(add_months(sysdate,-12), 'yyyymm')
then salary end) as annual_salary
from employee
group by emp_key
When I execute the query I'm getting null's in the result set..
I actually have valid figures for salaries in employee table.
Where am I going wrong?
Just invert the 2 bounds, they are not in the correct order:
...
between to_char(add_months(sysdate,-12), 'yyyymm')
and to_char(sysdate,'yyyymm')
select sum(case when date_key between
to_char(add_months(sysdate,-12), 'yyyymm') and to_char(sysdate,'yyyymm')
then salary end) as annual_salary
from employee
group by emp_key
select
sum(case when date_key between to_char(sysdate,'yyyymm') and to_char(add_months(sysdate,-12),'yyyymm') then salary else 0 end) as annual_salary
from employee group by emp_key
CASE WHEN THEN expr1 ELSE expr2 END
if you don't have "ELSE expr2" Oracle thinks it's NULL

SQL Query: Including Two Sums based on different criteria from the same table

I currently have the following sql query
SELECT [Date], DATENAME(dw,[Date]) AS Day, SUM(Units) AS TotalUnits
FROM tblTimesheetEntries
WHERE UserID = 'PJW'
AND Date >= '2013-01-01'
GROUP BY [Date]
ORDER BY [Date] DESC;
Which returns the Total [Units] for a given user and date.
However I would like to produce two separate Total Units based on a new criteria i.e. whether or not the [Units] where 'Chargeable or Not'. There is a field in tblTimeSheets called Chargeable which is a boolean (true for chargeable, false for not). How do I incorporate this to show two Sums one for each type of Units?
You can use a CASE with the SUM() to calculate the separate totals:
SELECT [Date],
DATENAME(dw,[Date]) AS Day,
sum(case when Chargeable = 1 then Units else 0 end) ChargeableTotal,
sum(case when Chargeable = 0 then Units else 0 end) NotChargeableTotal,
sum(Units) AS TotalUnits
FROM tblTimesheetEntries
WHERE UserID = 'PJW'
AND Date >= '2013-01-01'
GROUP BY [Date]
ORDER BY [Date] DESC;