SQL Query question (count dates for each month) - sql

Hope you can help me with the following, i have the following view availble:
DD/MM/YYYY
ENTITY | StartDate | EndDate | CodeA | CodeB | Revenue | Currency
AZERT | 01/01/2011 | 02/01/2011 | SU | BOLD | 100 | EUR
AZERT | 28/01/2011 | 02/02/2011 | SU | BOLD | 500 | EUR
Can someone help with a query to pull the data so that I get the following summed?
ENTITY | YYYY.MM | CodeA | CodeB | DAYS | TIMES | Revenue | Currency
AZERT | 2011.01 | SU | BOD | 5 | 2 | 500 | EUR
AZERT | 2011.02 | SU | BOD | 1 | 0 | 100 | EUR
Where YYYY.MM is created depending on the difference between Sdate and EDate.
And DAYS is the variance between the start and end day in the right month
And TIMES is the number of times that the StartDate occurs in that month
Revenue splitted depening how many days there are.

Assuming you are using SQL Server 2005 or later, would this work?
SELECT
DATEADD(dd, -DAY(EndDate + 1,EndDate)) -- Get first day of month for EndDate
,CodeA
,CodeB
,SUM(DATEDIFF(dd,StartDate, EndDate)) AS 'DAYS'
FROM
TABLE1
GROUP BY
DATEADD(dd, -DAY(EndDate + 1,EndDate))
,CodeA
,CodeB

Related

SQL - Average monthly amount

I have a table with "amount" and "date" columns, and I want to display the average by month.
The table looks like this:
amount | date |
100 | 2017-04-22 20:39:24 |
300 | 2017-04-25 16:14:08 |
200 | 2017-04-28 17:51:16 |
100 | 2017-05-29 05:46:42 |
100 | 2017-05-08 16:15:13 |
100 | 2017-05-09 22:06:45 |
400 | 2017-06-10 10:57:34 |
500 | 2017-06-11 15:57:14 |
900 | 2017-06-14 16:02:36 |
This is what I have:
SELECT AVG(amount) AS avg_amount, date
FROM table
GROUP BY date
It displays the average by day so it ends up looking exactly the same as the first table but without the hour/minute/second portion, while I want it to look like this:
avg_amount | date |
200 | April |
100 | May |
600 | June |
GROUP BY MONTH(date)
Check out the date and time functions in MySQL or in PostgreSQL extract function.
I like TOvidiu's answer, but that will only work if you have a single years worth of data. I would suggest
SELECT AVG(amount) AS avg_amount, date
FROM table
GROUP BY YEAR(date), MONTH(date)

Count of files received in the last 30 days split by each day

I am trying to get a count of the number of files my company has received in the past month split up by each day. The example below shows what I want the output to look like.
| Date- | | -Received- |
|--------| |------------|
| 2/1/20 | | - 16 - |
| 2/2/20 | | - 26 - |
| 2/3/20 | | - 14 - |
| 2/4/20 | | - 32 - |
The PK for my table is RecNo and the date variable is DateRecd.
Thank you in advance!
Do you just want aggregation and filtering?
select date, count(*)
from t
where date > convert(date, dateadd(day, -30, getdate()))
group by date
order by date;

Slicing account balance data in BigQuery to generate a debit report

I have a collection of account balances over time:
+-----------------+------------+-------------+-----------------------+
| account_balance | department | customer_id | timestamp |
+-----------------+------------+-------------+-----------------------+
| 5 | A | 1 | 2019-02-12T00:00:00 |
| -10 | A | 1 | 2019-02-13T00:00:00 |
| -35 | A | 1 | 2019-02-14T00:00:00 |
| 20 | A | 1 | 2019-02-15T00:00:00 |
+-----------------+------------+-------------+-----------------------+
Each record shows the total account balance of a customer at a specified timestamp. The account balance increases e.g. to 20 from -35, when a customer tops-up his account with 55. As a customer uses a services, his account balances decreases e.g. from 5 to -10.
I want to aggregate this data in two ways:
1) Get the debit, credit and balance (credit-debit) of a department per month and year. The results from April should be a summary of all previous months:
+---------+--------+-------+------------+-------+--------+
| balance | credit | debit | department | month | year |
+---------+--------+-------+------------+-------+--------+
| 5 | 10 | -5 | A | 1 | 2019 |
| 20 | 32 | -12 | A | 2 | 2019 |
| 35 | 52 | -17 | A | 3 | 2019 |
| 51 | 70 | -19 | A | 4 | 2019 |
+---------+--------+-------+------------+-------+--------+
A customer's account balance might not change every month. There might be account balance records of customer 1 in February, but not March.
Notes towards the solution:
use EXTRACT(MONTH from timestamp) month
use EXTRACT(YEAR from timestamp) year
GROUP BY month, year, department
2) Get the change of debit, credit and balance of a department by date.
+---------+--------+-------+------------+-------------+
| balance | credit | debit | department | date |
+---------+--------+-------+------------+-------------+
| 5 | 10 | -5 | A | 2019-01-15 |
| 15 | 22 | -7 | A | 2019-02-15 |
| 15 | 20 | -5 | A | 2019-03-15 |
| 16 | 18 | -2 | A | 2019-04-15 |
+---------+--------+-------+------------+-------------+
51 70 -19
When I create a SUM of the deltas, I should get the same values as the last row from results in 1).
Notes towards the solution:
use account_balance - LAG(account_balance) OVER(PARTITION BY department ORDER BY timestamp ASC) delta to compute deltas
Your question is unclear, but it sounds like you want to get the outstanding balance at any given point in time.
The following query does this for 1 point in time.
with calendar as (
select cast('2019-06-01' as timestamp) as balance_calc_ts
),
most_recent_balance as (
select customer_id, balance_calc_ts,max(timestamp) as most_recent_balance_ts
from <table>
cross join calendar
where timestamp < balance_calc_ts -- or <=
group by 1,2
)
select t.customer_id, t.account_balance, mrb.balance_calc_ts
from <table> t
inner join most_recent_balance mrb on t.customer_id = mrb.customer_id and t.timestamp = mrb.balance_calc_ts
If you need to calculate it at a series of points in time, you will need to modify the calendar CTE to return more dates. This is the beauty of CROSS JOINS in BQ!

How does DATEADD work when joining the same table with itself?

I have a table with monthly production values.
Example:
Outdate | Prod Value | ID
2/28/19 | 110 | 4180
3/31/19 | 100 | 4180
4/30/19 | 90 | 4180
I also have a table that has monthly forecast values.
Example:
Forecast End Date | Forecast Value | ID
2/28/19 | 120 | 4180
3/31/19 | 105 | 4180
4/30/19 | 80 | 4180
I want to create a table that has a row that contains the ID, the Prod Value, the current month (example: March) forecast, the previous month forecast, the next month forecast.
What I want:
ID | Prod Value | Outdate | Current Forecast | Previous Forecast | Next Forecast
4180 | 100 | 3/31/19 | 105 | 120 | 80
The problem is that when I used DATEADD to bring in the specific value from the Forecast table for the previous month, random months are missing from my final values.
I've tried adding in another LEFT JOIN / INNER JOIN with the DateDimension table when adding in the Next Month and Previous Month forecast, but that either does not solve the problem or adds in too many rows.
My DateDimension table that has these columns: DateKey
Date, Day, DaySuffix, Weekday, WeekDayName, IsWeekend, IsHoliday, DOWInMonth, DayOfYear, WeekOfMonth, WeekOfYear, ISOWeekOfYear, Month, MonthName, Quarter, QuarterName, Year, MMYYYY, MonthYear, FirstDayOfMonth, LastDayOfMonth, FirstDayOfQuarter, LastDayOfQuarter, FirstDayOfYear, LastDayOfYear, FirstDayOfNextMonth, FirstDayOfNextYear
My query is along these lines (abbreviated for simplicity)
SELECT A.ArchiveKey, BH.ID, d.[Date], BH.Outdate, BH.ProdValue, BH.Forecast, BHP.Forecast, BHN.Foreceast
FROM dbo.BudgetHistory bh
INNER JOIN dbo.DateDimension d ON bh.outdate = d.lastdayofmonth
INNER JOIN dbo.Archive a ON bh.ArchiveKey = a.ArchiveKey
LEFT JOIN dbo.BudgetHistory bhp ON bh.ID = bhp.ID AND bhp.outdate = DATEADD(m, - 1, bh.Outdate)
LEFT JOIN dbo.BudgetHistory bhn ON bh.ID = bhn.ID AND bhn.outdate = DATEADD(m, 1, bh.Outdate)
WHERE bh.ID IS NOT NULL
I get something like this:
+------+------------+---------+------------------+-------------------+---------------+
| ID | Prod Value | Outdate | Current Forecast | Previous Forecast | Next Forecast |
+------+------------+---------+------------------+-------------------+---------------+
| 4180 | 110 | 2/28/19 | 120 | NULL | NULL |
| 4180 | 100 | 3/31/19 | 105 | 120 | 80 |
| 4180 | 90 | 4/30/19 | 80 | NULL | NULL |
+------+------------+---------+------------------+-------------------+---------------+
And the pattern doesn't seem to follow anything reasonable.
I want the values to be filled in for each row.
You could join the tables, then use window functions LEAD() and LAG() to recover the next and previous forecast values:
SELECT
p.ID,
p.ProdValue,
p.Outdate,
f.ForecastValue,
LAG(f.ForecastValue) OVER(PARTITION BY f.ID ORDER BY f.ForecastEndDate) PreviousForecast,
LEAD(f.ForecastValue) OVER(PARTITION BY f.ID ORDER BY f.ForecastEndDate) NextForecast
FROM prod p
INNER JOIN forecast f ON p.ID = f.ID AND p.Outdate = f.ForecastEndDate
This demo on DB Fiddle with your sample data returns:
ID | ProdValue | Outdate | ForecastValue | PreviousForecast | NextForecast
---: | --------: | :------------------ | ------------: | ---------------: | -----------:
4180 | 110 | 28/02/2019 00:00:00 | 120 | null | 105
4180 | 100 | 31/03/2019 00:00:00 | 105 | 120 | 80
4180 | 90 | 30/04/2019 00:00:00 | 80 | 105 | null
DATEADD only does end of month adjustments if the newly calculated value isn't a valid date. So DATEADD(month,-1,'20190331') produces 28th February. But DATEADD(month,-1,'20190228') produces 28th January, not the 31st.
I would probably go with GMB's answer. If you want to do something DATEADD based though, you can use:
bhp.outdate = DATEADD(month, DATEDIFF(month,'20010131', bh.Outdate) ,'20001231')
This always works out the last day of the previous month from bh.OutDate, but it does it by computing it as an offset from a fixed date, and then applying that offset to a different fixed date.
You can just reverse the places of 20010131 and 20001231 to compute the month after rather than the month before. There's no significance about them other than them both having 31 days and having the "one month apart" relationship we're wishing to apply.

SQL output based on a date range

Given the following two table scenario, how would I go about outputting the commission percentage based on the date range:
Commission Percentages
| User ID | Start Date | End Date | Percentage
| -------- | ---------- | ----------- | ----------
| 1 | 11/11/2014 | 11/30/2014 | 10%
| 1 | 11/30/2014 | NULL | 20%
| 2 | 10/10/2014 | NULL | 15%
Sales
| User ID | Sale Date |
| -------- | ---------- |
| 1 | 11/24/2014 |
| 1 | 12/1/2014 |
| 2 | 12/30/2014 |
I would like to end up with a join between the two like so (a null value in the end date field represents present - and the dates will also include a time stamp):
| User ID | Sales Date | Start Date | End Date | Percentage
| -------- | ---------- | ---------- | ---------- | ----------
| 1 | 11/24/2014 | 11/11/2014 | 11/30/2014 | 10%
| 1 | 12/1/2014 | 11/30/2014 | NULL | 20%
| 2 | 12/30/2014 | 10/10/2014 | NULL | 15%
I am using SQL Server 2012
Thanks
Something like this might work for you, however you need to figure your date logic (i.e. whether it should be greater than, or greater than/equal to) depending on how your system works:
select S.UserID, S.SalesDate, C.StartDate, C.EndDate, C.Percentage
from Sales AS S
inner join Commission AS C
on C.UserID = S.UserID
AND S.SalesDate > C.StartDate
AND S.SalesDate <= coalesce(C.EndDate, S.SalesDate)
I'm assuming the end date is the first date the percentage does not apply based on the data. User ID 1 has a vector overlap.
SELECT s.User_ID,
s.Sales_Date,
cp.Start_Date,
cp.End_Date,
cp.Pecrcentage
FROM Commission_Percentages cp
INNER JOIN Sales s
ON s.User_ID = cp.User_ID
AND s.Sale_Date >= cp.Start_Date
AND (s.Sale_Date < cp.End_Date OR cp.End_Date IS NULL)