combining dates when doing a sum query - sql

I would like to sum values over time. The difficulty that i am having with this is if there is a way to assign a date to the end value.
To clarify, here is my example table.
Object Observation Date
1 215 10/1/2015
2 125 10/1/2015
1 225 10/4/2015
2 150 10/4/2015
1 250 10/8/2015
The idea is to sum the observation by Object and Date. This is easy, the hard part is something I'm not sure how i would do. Currently I sum by month using this query
SELECT Object, Sum(Observation) AS [Total], Month([Date]) AS [Month],
FROM Records
GROUP BY Month([Date]), Object;
This gives my the total for the month. Ideally what I would like to do though is to make it so that rather than getting a numeric month, mm, I get a date mm/dd/yyyy. So the Date post sum would show up as 10/01/2015.
So that output would look something like this:
Object Total Date
1 690 10/1/2015
2 275 10/1/2015
I have build out a query that uses a calendar to select a start and end date for pulling multiple months at a time that I might want to compare.
SELECT Object, Date, Total
FROM Table1
WHERE [Month] Between Month(Forms![Main Form]![Start Date]) And Month(Forms![Main Form]![End Date])
GROUP BY Object, Month, Total;
My current method doesn't allow for me to roll over into the next calendar year. The hope is that this would fix my issue with the roll over issue. Is this possible to assign the first of the month to the date after the month is summed?

That could be:
SELECT
Object,
DateSerial(Year([Date]), Month([Date]), 1) As YearMonth,
Sum(Observation) AS [Total]
FROM
Table1
WHERE
[Date]
Between Forms![Main Form]![Start Date]
And Forms![Main Form]![End Date]
GROUP BY
Object,
DateSerial(Year([Date]), Month([Date]), 1)

You can use DateSerial() to determine the first day of the month.
SELECT Object, Sum(Observation) AS [Total], DateSerial(Year(Date()), Month(Date()),1) AS [Month],
FROM Records
GROUP BY DateSerial(Year(Date()), Month(Date()),1), Object;

Related

How to spread annual amount and then add by month in SQL

Currently I'm working with a table that looks like this:
Month | Transaction | amount
2021-07-01| Annual Membership Fee| 45
2021-08-01| Annual Membership Fee| 145
2021-09-01| Annual Membership Fee| 2940
2021-10-01| Annual Membership Fee| 1545
the amount on that table is the total monthly amount (ex. I have 100 customers who paid $15 for the annual membership, so my total monthly amount would be $1500).
However what I would like to do (and I have no clue how) is divide the amount by 12 and spread it into the future in order to have a monthly revenue per month. As an example for 2021-09-01 I would get the following:
$2490/12 = $207.5 (dollars per month for the next 12 months)
in 2021-09-01 I would only get $207.5 for that specific month.
On 2021-10-01 I would get $1545/12 = $128.75 plus $207.5 from the previous month (total = $336.25 for 2021-10-01)
And the same operation would repeat onwards. The last period that I would collect my $207.5 from 2021-09-01 would be in 2022-08-01.
I was wondering if someone could give me an idea of how to perform this in a SQL query/CTE?
Assuming all the months you care about exist in your table, I would suggest something like:
SELECT
month,
(SELECT SUM(m2.amount/12) FROM mytable m2 WHERE m2.month BETWEEN ADD_MONTHS(m1.month, -11) AND m1.month) as monthlyamount
FROM mytable m1
GROUP BY month
ORDER BY month
For each month that exists in the table, this sums 1/12th of the current amount plus the previous 11 months (using the add_months function). I think that's what you want.
A few notes/thoughts:
I'm assuming (based on the column name) that all the dates in the month column end on the 1st, so we don't need to worry about matching days or having the group by return multiple rows for the same month.
You might want to round the SUMs I did, since in some cases dividing by 12 might give you more digits after the decimal than you want for money (although, in that case, you might also have to consider remainders).
If you really only have one transaction per month (like in your example), you don't need to do the group by.
If the months you care about don't exist in your table, then this won't work, but you could do the same thing generating a table of months. e.g. If you have an amount on 2020-01-01 but nothing in 2020-02-01, then this won't return a row for 2021-02-01.
CTE = set up dataset
CTE_2 = pro-rate dataset
FINAL SQL = select future_cal_month,sum(pro_rated_amount) from cte_2 group by 1
with cte as (
select '2021-07-01' cal_month,'Annual Membership Fee' transaction ,45 amount
union all select '2021-08-01' cal_month,'Annual Membership Fee' transaction ,145 amount
union all select '2021-09-01' cal_month,'Annual Membership Fee' transaction ,2940 amount
union all select '2021-10-01' cal_month,'Annual Membership Fee' transaction ,1545 amount)
, cte_2 as (
select
dateadd('month', row_number() over (partition by cal_month order by 1), cal_month) future_cal_month
,amount/12 pro_rated_amount
from
cte
,table(generator(rowcount => 12)) v)
select
future_cal_month
, sum(pro_rated_amount)
from
cte_2
group by
future_cal_month

Matching date with calculated DATEADD date

I am trying to create a table with columns containing the current date, prior year date, and additional column for the total sum revenue as below:
cur_date | py_date | py_rev
I'm trying to compare revenue across any daily period across years. Assume all dates and revenue values are included in the same SQL Server table.
I attempted to use a case statement using [date] = DATEADD(wk,-52,[date]) as the condition to return the appropriate total. The full line code is below:
select
[date] as cur_date,
DATEADD(wk,-52,[date]) py_date,
SUM(case when [date] = DATEADD(wk,-52,[date]) then sum_rev else 0 end) as py_rev
from summary
group by [date]
When running this code the py_date is as expected but py_rev returns 0 as if there is no match. What's most confusing is if I hard code a random date in place of the DATEADD portion then a total is returned. I have also tried CAST to format both date and the DATEADD portion as date with no luck.
Is there something about DATEADD that will not match to other date columns that I'm missing?
If you want the previous years revenue, then lag() is one method. This works assuming that "previous year" means 52 weeks ago and you have records for all dates:
select [date] as cur_date,
dateadd(week, -52, [date]) as py_date,
lag(sum_rev, 52 * 7) over (order by date) as py_rev
from summary;
If you do not have records for all dates, then another approach is needed. You can use a LEFT JOIN:
select s.date, dateadd(week, -52, s.[date]),
sprev.sum_rev as py_rev
from summary s left join
summary sprev
on sprev.date = dateadd(week, -52, s.[date]);

Query who looks if employee has more than 14 vacation days

I want a query that checks if a employee has more than 14 vacation days in a year. I want to make a trigger of it. It is important that this is 14 days in total. But the employee could have 4 days in one month and 10 days in another. He don't need to take the days in one go.
I have something like this (query) I'm using SQL Server
select employeeid
from time
where datediff(day, dateStart, dateEnd) >=1
and year(dateEnd) = 2018
and timecat= 'vacationdays'
group by employeeid
having count(*) >=13
I thought I could use a datediff and have this count like 13 times. But it doesn't work.
You need to sum the date differences:
select employeeid, sum(datediff(day, dateStart, dateEnd) + 1) AS total
from time
where year(dateEnd) = 2018
and timecat= 'vacationdays'
group by employeeid
having SUM(datediff(day, dateStart, dateEnd) + 1) > 14
Consider the +1 in SUM because datediff returns 1 for two dates like '2018-11-16' and '2018-11-17', but if these are the dateStart and dateEnd you want the result to be 2.
There is still one problem remaining: what happens if dateStart and dateEnd are not dates of the same year!
Count(*) just counts the number of rows.
This means if have an employee with two vacations one of 10 days and one of 4 days, (i assume that) this are only two rows in rows in your table and count star returns 2.
You can you the function SUM()
select employeeid, sum( datediff(day, dateStart, dateEnd) ) as sum
from time
where datediff(day, dateStart, dateEnd) >=1
and year(dateEnd) = 2018
and timecat= 'vacationdays'
group by employeeid
having sum >=13
I don't know the structure of your table, therefore I'm not quite sure if the query is correct, but here a further helpful links.
https://www.w3schools.com/sql/sql_count_avg_sum.asp
Getting the sum of a datediff result

SQL Over partition by

I basically have a case statement that displays the sum of profit and a month to date total for each person. My idea is i want to display a daily figure of that person as well as their whole month total altogether.
My issue is when i limit results to just yesterday (supposed to be a daily figure) this then effects the calculation of the month value (just calculates the sum for that day rather than the whole month).
This is because the total month values are all out of the scope of the query. Is there anyway to calculate the whole month value for each person correctly without having the limits of where effecting the result.
e.g.
The result:
08/09/17: 25
09/09/17: 25
10/09/17: 25
11/09/17: 25 <<<< but only display one day and month total
Overall Month total: 100
Can this also includes nulls too? I think im almost looking at a dynamically stored month to date value that isn't effected by where clauses.
SELECT SUM(Figure) AS 'Daily Figure',
CASE WHEN
MONTH([DATE]) = MONTH(getdate()) AND
YEAR([DATE]) = YEAR(getdate())
THEN
SUM(Figure)
OVER (PARTITION BY [Name],
MONTH([DATE]))
ELSE 0 END
as [Month To Date Total]
WHERE
dateadd(day,datediff(day,1,GETDATE()),0)
If you want month-to-date and the current amount, then use conditional aggregation:
SELECT NAME,
SUM(CASE WHEN DAY(DATE) = DAY(GETDATE()) - 1 THEN Figure ELSE 0 END) AS DailyFigure,
SUM(Figure) as MonthToDate
WHERE MONTH([DATE]) = MONTH(getdate()) AND
YEAR([DATE]) = YEAR(getdate())
GROUP BY NAME;
This works on all but the first day of the month.

SQL Server / SSRS: Calculating monthly average based on grouping and historical values

I need to calculate an average based on historical data for a graph in SSRS:
Current Month
Previous Month
2 Months ago
6 Months ago
This query returns the average for each month:
SELECT
avg_val1, month, year
FROM
(SELECT
(sum_val1 / count) as avg_val1, month, year
FROM
(SELECT
SUM(val1) AS sum_val1, SUM(count) AS count, month, year
FROM
(SELECT
COUNT(val1) AS count, SUM(val1) AS val1,
MONTH([SnapshotDate]) AS month,
YEAR([SnapshotDate]) AS year
FROM
[DC].[dbo].[KPI_Values]
WHERE
[SnapshotKey] = 'Some text here'
AND No = '001'
AND Channel = '999'
GROUP BY
[SnapshotDate]) AS sub3
GROUP BY
month, year, count) AS sub2
GROUP BY sum_val1, count, month, year) AS sub1
ORDER BY
year, month ASC
When I add the following WHERE clause I get the average for March (2 months ago):
WHERE month = MONTH(GETDATE())-2
AND year = YEAR(GETDATE())
Now the problem is when I want to retrieve data from 6 months ago; MONTH(GETDATE()) - 6 will output -1 instead of 12. I also have an issue with the fact that the year changes to 2016 and I am a bit unsure of how to implement the logic in my query.
I think I might be going about this wrong... Any suggestions?
Subtract the months from the date using the DATEADD function before you do your comparison. Ex:
WHERE SnapshotDate BETWEEN DATEADD(month, -6, GETDATE()) AND GETDATE()
MONTH(GETDATE()) returns an int so you can go to 0 or negative values. you need a user scalar function managing this, adding 12 when <= 0