Dynamic Grouping based on date field SSRS - sql

In my SSRS report which has a dataset with date field as below
The dataset returns the following type of data (just an example)
Voucher Amount Date
R3221 € 3,223.00 1-Dec-17
R3222 € 123.00 28-Nov-17
R3223 € 1,233.00 19-Oct-17
R3224 € 442.00 27-Sep-17
R3225 € 123.00 17-Nov-17
R3226 € 423.00 29-Oct-17
R3227 € 1,234.00 8-Oct-17
What I would like to know is how to show this data grouped by Voucher and Due Date
User should be able to select the Start Date and the period type (Day, Week, month,) and the interval between the two columns (e.g 3 , 10 or 30 or any other number)
so the user should be able to select the period type, e.g if he select Day and interval as 3 then the report should show
**voucher start date <Dynamic grouping columns based on the selection criteria>**
R3221
R3222
R3223
R3224
R3225
R3226
R3227
Any kind of hint will be much appreciated!

This is only part of the solution. First generate date periods you want in the report, then do a LEFT JOIN against your table, filter and group.
declare #dateFrom date
declare #periodInDays int
declare #periods int --how many periods of 10 days
set #periodInDays = 10;
set #periods = 15; --how many periods of 10 days
set #dateFrom = getdate();
with [dates] as (
select #dateFrom as date --start
union all
select dateadd(day, #periodInDays, [date]) as date
from [dates]
where [date] < DATEADD(day, #periodInDays * #periods, #dateFrom) --end
)
select [date]
from [dates]
option (maxrecursion 0)
In this example returns 16 dates (1 per row), from start date (2017-12-01), every 10 days
2017-12-01, 2017-12-11, 2017-12-21, 2017-12-31, 2018-01-10, 2018-01-20
2018-01-30, 2018-02-09, 2018-02-19, 2018-03-01, 2018-03-11, 2018-03-21
2018-03-31, 2018-04-10, 2018-04-20, 2018-04-30
To filter by period get start date from the above date table, so the end date for that period can be calculated as DATEADD(day, #periodInDays, [date]) and we don't need to look into next row at this point.
Just let me know if someone find more straight forward solution.

Related

How to create monthly snapshots for the last 6 months?

I'm trying to get detailed data (snapshot) for each month on Business Day=1 for the last 6 months and need to pass 6 different dates (BD1's only) through two date variables.
Two variables will be BOM which will be BD1 for the last 6 months and EOM which will be BD1+1.
For e.g
First snapshot will be
declare #BOM date ='2022-08-01'
declare #EOM date ='2022-09-01'
Second snapshot will be
declare #BOM date ='2022-09-01'
declare #EOM date ='2022-10-01'
and so on for the last 6 months from the current month
Here is what I'm trying to do:
declare #BOM date
set #BOM=
(
select top 6 cast(date_datetime as date) date_datetime
from date_dim
where
datediff(month, date_datetime, getdate()) <= 6
and bd=1
order by date_datetime asc);
declare #EOM date
set #EOM=
(
select top 6 date_datetime
from date_dim
where
datediff(month, date_datetime, getdate()) <= 5
and bd=1
order by date_datetime asc);
But my query does not process it as I'm passing more than 1 value through my BOM & EOM variables in my main query WHERE clause.
I need some help with defining and using these variables in my query so that they can take different snapshots and store it in a table.
As you discovered, you cannot store multiple values in a scalar variable. What you possibly need is to use a table variable (which behaves similarly to a temp table). The table variable can have multiple rows (one for each selected month) and multiple columns (BOM and EOM).
The following code defines such a table variable and populates it with BOM and EOM of the most recent 6 full months from the date_dim table. I used the LEAD() window function to select the corresponding EOM for each BOM.
Lacking any provided sample data to actually query, I added a simple query at the end to just list the selected date ranges and calculated number of business days in each.
-- Table variable to hold selected month information
DECLARE #selected_months TABLE (BOM DATE, EOM DATE)
-- Select last 6 full months
INSERT #selected_months
SELECT *
FROM (
SELECT
date_datetime AS BOM,
LEAD(date_datetime) OVER(ORDER BY date_datetime) AS EOM
FROM date_dim
) D
WHERE DATEDIFF(month, BOM, GETDATE()) BETWEEN 1 AND 6
ORDER BY BOM
-- Sample usage
SELECT M.*, DATEDIFF(day, M.BOM, M.EOM) business_days
FROM #selected_months M
-- JOIN your_data D
-- ON D.your_data_date >= SM.BOM
-- AND D.your_data_date < SM.EOM
GROUP BY M.BOM, M.EOM
ORDER BY M.BOM
Sample results:
BOM
EOM
business_days
2022-08-01
2022-09-05
35
2022-09-05
2022-10-03
28
2022-10-03
2022-11-07
35
2022-11-07
2022-12-05
28
2022-12-05
2023-01-02
28
2023-01-02
2023-02-06
35
See this db<>fiddle for a working demo.

Generate List of dates between 2 dates for each Id

I have a table with PersonId's that each have a FirstSubscription date and LastSubscriptionDate.
What I need to do is between those 2 dates, generate 1 date for each month. This is for reporting purposes on the front end, as this data will end up inside PowerBI and I need these dates to join to a ReportingCalendar.
This Calendar is accessible by SQL so it can be used in this calculation. I am using it to generate the dates (using first of the month) between the First and LastSubDate but I need to find a way to join this with the rest of the ID's that way I get a list of date for each ID.
Here is my code to generate the dates.
DECLARE #MinDate DATE
DECLARE #MaxDate DATE
SET #MinDate = '2020-08-31'
SET #MaxDate = '2022-08-30'
SELECT DATEADD(month, DATEDIFF(month, 0, date), 0)
FROM dbo.ReportingCalendar
WHERE Date >= #MinDate
AND Date < #MaxDate
GROUP BY
DATEADD(month, DATEDIFF(month, 0, date), 0)
My PersonSubscription table looks like this
|PersonId|FirstSubDate|LastSubDate|
|--------|------------|-----------|
|1186 |8/31/2020 |8/30/2022 |
|2189 |7/30/2019 |7/31/2021 |
So I would want to end up with an output where each PersonId has 1 entry for each month between those 2 dates. So PersonId has 25 entries from 8/2020 until 8/2022. We don't care about the actual date of the sub since this data is looked at monthly and will primarily be looked at using a Distinct Count each month, so we only care if they were subbed at any time in that month.
I just needed to do a Cross Apply.
I took my code that got me all of the PersonId's and their FirstSubDate and LastSubDate and then did a cross apply to the code I listed above, referencing the MinDate and MaxDate with the FirstSubDate and LastSubDate.

SQL Start Date End Date Spread difference across months

I currently have a database which has a start date and and end date when when a Car is being held for Maintenance. I am able to figure the difference between these dates using datediff but the problem is that the value when across multiple months is linked either to the month of the start date or the end date. I am looking to spread the difference between the months so in my analysis I can see how long the cars have been held each month in days.
Example:
StartDate: '2022-04-28 06:33:34.000'
EndDate: '2022-06-20 14:09:45.000'
Days Difference: 53 days 7 Hours 36 Minutes 11 Seconds
What I need to do is spread the 54 rounded up as
April: 3
May: 31
June: 20
I currently calculating the difference either as day percentage or in days using this logic.
'CAST(CAST(DATEDIFF(s, STARTDATE, ENDDATE)AS float)/86400 AS DECIMAL(16,3)) AS CAR_TOTAL_DAYS_PERC' returns 53.317
'DATEDIFF(s, STARTDATE, ENDDATE) / 86400 AS CAR_TOTAL_DAYS' returns 53
Any assistance would be greatly appreciated.
You may want to implement this as a stored procedure, something like GetDateSpreadString
--Variables for start and end date
DECLARE #startDate AS DATETIME
DECLARE #endDate AS DATETIME
SET #startDate='2022-04-28 06:33:34.000'
SET #endDate ='2022-06-20 14:09:45.000';
--CTE to expand the count of date numbers to be used for DATEADD
WITH numbers
as
(
Select 1 as value
Union ALL
Select value + 1 from numbers
where value + 1 <= DATEDIFF( dd,#startDate, #endDate)
),
--CTE to group individual days covered by spread in each of the months.
MonthsDays AS (
Select EOMONTH(DATEADD(d,value, #startDate)) as MonthSpread, DATEPART(dd,DATEADD(d,value, #startDate)) as DaySpread
From numbers
)
--USE FOR XML PATH() and STUFF() to make it a single string result
SELECT STUFF(
(
Select CONCAT(' ',COUNT(daySpread),','), CONCAT( DATENAME(month,MonthSpread),',')
FROM MonthsDays
GROUP BY MonthSpread
FOR XML PATH('')
),1,1,'') AS Result

Count # of Saturdays given a date range

I have a datetime field and a net field. The Sat Count field is done by =IIf(DatePart("w",Fields!DespatchDate.Value)=7,1,0)
I want to total the count of the Saturdays given a starting date and end date (typically a month).
I tried =Sum(IIf(DatePart("w",Fields!DespatchDate.Value)=7,1,0) but the total is wrong.
I also want to count Saturdays for rest of the month, e.g there's a missing 3rd Saturday in the picture.
I also want to do a total of the Net for Saturdays.
Can you point me in the direction. I can do it in SQL or in SSRS
Considering that we do not have any Input or desired output provided, I am assuming that You just want to count Saturdays in a given range:
Select COUNT(*), SUM(Net)
FROM table
WHERE Day# = 7 AND Date BETWEEN '2021-02-16' AND '2021-02-23'
Assuming you want to count saturdays even if it is not part of your dataset, what you need to do is pad out all your dates for the given range and then join it to your base data set.
This would ensure that it accounts for ALL days of the week regardless of a dispatch event occuring on that date / day.
Below is some SQL code that might help you make a start.
declare #startdate date = '2021-02-01'
declare #enddate date = '2021-02-28'
if OBJECT_ID ('tempdb..#dates') is not null
drop table #dates
;WITH mycte AS
(
SELECT CAST(#startdate AS DATETIME) DateValue
UNION ALL
SELECT DateValue + 1
FROM mycte
WHERE DateValue + 1 < #enddate
)
SELECT DateValue into #dates
FROM mycte
OPTION (MAXRECURSION 0)
select
d.DateValue
, datepart(weekday,d.DateValue) as day_no
,case when datepart(weekday,d.DateValue) = 7 then isnull(t.net,0) else 0 end as sat_net
,case when datepart(weekday,d.DateValue) = 1 then isnull(t.net,0) else 0 end as sun_net
from #dates d
left join your_table t
on d.DateValue = t.some_date
drop table #dates
Since I don't know what your required output is, I cannot summarise this any further. But you get the idea!

Calculating last 7 days for each day in range

My table (products) is as follow:
id, productId, price, saleDate
I want to build a query that for specific productId and specific data range will return the average of the price for the last 7 days for each day in the range.
The following query will give me all the data for specific productId between 1/1/16 and 02/01/16
SELECT * FROM products WHERE productId='PS54434' AND saleDate BETWEEN '2016-01-01' AND '2016-02-01'
BUT I want that for each day 01/01/16 until 02/01/16 I will receive the last 7 days
so for data 01/01/16 I will receive the average of prices from the 12/26/16-01/01/16
One way to do it is using a loop. You declare a start date and go back 7 days from that date and get the average price for that date range. Then add 1 day and repeat the process. If you need all the results as a table, create a temp table and store the result from each iteration.
declare #startdate date = '2016-01-01';
create table #tmpavgprice (price numeric(10,2));
while #startdate <= '2016-02-01'
begin
SELECT avg(price)
INTO #tmpavgprice
FROM products
WHERE productId='PS54434'
AND saleDate BETWEEN dateadd(dd,-7,#startdate) AND #startdate;
set #startdate = select dateadd(dd,1,#startdate);
end;
select * from #tmpavgprice; --to get the final result