SSRS. How to group in a group? - sql-server

I have SSRS report like below with Boolean parameter to show 12h view or 24h view. To fit report into single screen the 24h report need to group by every 2hr.
07:00 08:00 09:00 10:00 11:00 12:00 13:00 14:00 ...
Line 1 25 30 24 26 25 25 30 30 ...
08:00 10:00 12:00 14:00 ...
Line 1 55 50 50 60 ...
The query for the dataset is:
SELECT LineID
,Hour
,HourValue
,Target
FROM vwData
ORDER BY LineID, CASE WHEN [Hour] > 6 THEN - 1 ELSE [Hour] END
How can I achieve this?

This declares your bit variable (which should be true when they want the 24 hour view - false when 12 hour)
DECLARE #24Hour bit = 0
SELECT CASE WHEN #24Hour = 0
THEN Hour
ELSE Hour + (Hour % 2)
END AS [HourGroup]
,SUM(Target) AS [TargetTotal]
FROM vwData
GROUP BY CASE WHEN #24Hour = 0
THEN Hour
ELSE Hour + (Hour % 2)
END
If they want the 24 hour view, we make hour = hour + hour % 2. (7 = 8, 8=8, 9=10, etc., etc.). If you had a more complex query, I would suggest reading up on cross apply, but this is so simple I think this will suffice. The grouping by makes sure to aggregate the REAL 7 and REAL 8 hour records (which will both be returned as "8", if using the 24 hour view). If you don't group your results, you will get two 8 oclock records - one with the REAL 7 hour total and one with the REAL 8 hour total.
EDIT:
Since you didn't include the schema of your DB, I'm guessing that 'Target' is the value being summated, but it could just as easily be 'HourValue'. Furthermore, I have no idea why you would need LineID, so I omitted it from my answer. But you can easily modify that if it's inaccurate. In the future, you should provide some sample data and your database schema so that others aren't forced to make assumptions or guess.

You could add a calculated field with a value given by something like this: `Fields!Hour.Value + Fields!Hour.Value Mod 2' and then group on that field, using a parameter to choose the Group By field in the report (Your new field or the actual hour value).

Related

Google Data Studio / Looker Studio : scorecard comparing vs prior month based on a filter

1) Data I have a following dataset in google sheets link. In the sheets sample I have only 4 months of data but normally there would be many, many more to come in the future.
MONTH
DATE
KATEGORIE
DOWNTIME
TIME (min)
9
01/09/2021
01 DURCHLAUF
0
50
9
02/09/2021
01 DURCHLAUF
0
65
9
03/09/2021
01 DURCHLAUF
0
91
9
04/09/2021
01 DURCHLAUF
0
52
9
05/09/2021
01 DURCHLAUF
0
72
9
06/09/2021
01 DURCHLAUF
0
44
9
07/09/2021
01 DURCHLAUF
0
55
9
08/09/2021
01 DURCHLAUF
0
30
9
09/09/2021
01 DURCHLAUF
0
42
2) Expected output table and desired output
I want to create a scorecard for 02 Downtime to show total time for a given month.
If I filter for November, I would like the scorecard to compare vs October. (abs=1180, %=45)
Similarly, if I select December, I want to see the amount vs November (abs=940, %=25)
As a safety measure, if someone selects 2 months simultaneously, then perhaps it should not show any comparison. (unless it's possible to even do 2 vs 2 months, but it's not a necessity.)
3) Chart: Configuration + Setup
I have created a simple scorecard and a pivot table. I filtered out only Downtime.
4) Issue: Attempt at solving + Output and 5) Report: Publicly editable Looker Studio with 1-4.
In my file link you see the mentioned scorecard but I fail to include any comparison that is kind of "dynamic" that changes the month in question.
Added solution to your dashboard page 2
Cross join the datasource with itself
use the following calculated fields for expected output.
Daet01:
CAST(CONCAT(DATE(EXTRACT(YEAR FROM DATE123),EXTRACT(MONTH FROM DATE123),01)) AS DATE)
Previous Month:
TIME (min) (Table 1)-CASE WHEN Daet01 (Table 2) = DATETIME_SUB(Daet01 (Table 1), INTERVAL 1 MONTH) THEN TIME (min) (Table 2) ELSE 0 END
% Difference:
(TIME (min) (Table 1)-CASE WHEN Daet01 (Table 2) = DATETIME_SUB(Daet01 (Table 1), INTERVAL 1 MONTH) THEN TIME (min) (Table 2) ELSE 0 END)/TIME (min) (Table 2)
-

Avoid multiple counting of overlapped times

I have calculated the overlap times between start_time and end_time for groups of IDs for multiple user logins sessions. ID is unique to a user. A user can have multiple sessions from different browsers, devices, etc.
Here's the dataset I have
row id start_time end_time overlap_in_seconds
1 1 08:41:27 08:47:26 359
2 1 08:39:31 08:40:42 71
3 1 08:41:37 08:47:26 349
If you notice for rows 1 and 3 the time overlap time between 08:41:37 and 08:47:26 has been counted twice.
There are 2 options how I would like to show the result:
Option 1:
row id start_time end_time overlap_in_seconds
1 1 08:41:27 08:47:26 10 (the extra overlap time from 08:41:27 to 08:41:37)
2 1 08:39:31 08:40:42 71
3 1 08:41:37 08:47:26 349
Option 2:
I use this table as a temp table and in the outer query when I do a sum on Overlap_in_seconds, I get the total overlap time as 430 seconds (10+71+349), not 779(349+359+71).
meeting_id. correct_overlap
1 430
Below is my outer query.
select temp.id as meeting_id, sum(temp.Overlap_in_seconds) as correct_overlap
from temp
group by temp.id
Any ideas how to do this?
I'm not sure if this is the quickest way to do it, but what i would is to convert every date to a 86400 bit array, every position correspond to each second in the day, next just simply join all the arrays and it will be the seconds for each group.

SAS do loop with if statement

I am trying to group by dataset in three month groups, or quarters, but as I'm starting from an arbitrary date, I cannot use the quarter function in sas.
Example data below of what I have and quarter is the column I need to create in SAS.
The start date is always the same, so my initial quarter will be 3rd Sep 2018 - 3rd Dec 2018 and any active date falling in that quarter will be 1, then quarter 2 will be 3rd Dec 2018 - 3rd Mar 2019 and so on. This cannot be coded manually as the start date will change depending on the data, and the number of quarters could be up to 20+.
The code I have attempted so far is below
data test_Data_op;
set test_data end=eof;
%let j = 0;
%let start_date = start_Date;
if &start_Date. <= effective_dt < (&start_date. + 90) then quarter = &j.+1;
run;
This works and gives the first quarter correctly, but I can't figure out how to loop this for every following quarter? Any help will be greatly appreciated!
No need for a DO loop if you already have the start_date and actual event dates. Just count the number of months and divide by three. Use the continuous method of the INTCK() function to handle start dates that are not the first day of a month.
month_number=intck('month',&start_date,mydate,'cont')+1;
qtr_number=floor((month_number-1)/3)+1;
Based on the comment by #Lee. Edited to match the data from the screenshot.
The example shows that May 11 would be in the 3rd quarter since the seed date is September 3.
data have;
input mydate :yymmdd10.;
format mydate yymmddd10.;
datalines;
2018-09-13
2018-12-12
2019-05-11
;
run;
%let start_date='03sep2018'd;
data want;
set have;
quarter=floor(mod((yrdif(&start_date,mydate)*4),4))+1;
run;
If you want the number of quarters to extend beyond 4 (e.g. September 4, 2019 would be in quarter 5 rather than cycle back to 1), then remove the "mod" from the function:
quarter=floor(yrdif(&start_date,mydate)*4)+1;
The traditional use of quarter means a 3 month time period relative to Jan 1. Make sure your audience understands the phrase quarter in your data presentation actually means 3 months relative to some arbitrary starting point.
The funky quarter can be functionally computed from a months apart derived using a mix of INTCK for the baseline months computation and a logical expression for adjusting with relation to the day of the month of the start date. No loops required.
For example:
data have;
do startDate = '11feb2019'd ;
do effectiveDate = startDate to startDate + 21*90;
output;
end;
end;
format startDate effectiveDate yymmdd10.;
run;
data want;
set have;
qtr = 1
+ floor(
( intck ('month', startDate, effectiveDate)
-
(day(effectiveDate) < day(startDate))
)
/ 3
);
format qtr 4.;
run;
Extra
Comparing my method (qtr) to #Tom (qtr_number) for a range of startDates:
data have;
retain seq 0;
do startDate = '01jan1999'd to '15jan2001'd;
seq + 1;
do effectiveDate = startDate to startDate + 21*90;
output;
end;
end;
format startDate effectiveDate yymmdd10.;
run;
data want;
set have;
qtr = 1
+ floor( ( intck ('month', startDate, effectiveDate)
- (day(effectiveDate) < day(startDate))
) / 3 );
month_number=intck('month',startDate,effectiveDate,'cont')+1;
qtr_number=floor((month_number-1)/3)+1;
format qtr: month: 4.;
run;
options nocenter nodate nonumber;title;
ods listing;
proc print data=want;
where qtr ne qtr_number;
run;
dm 'output';
-------- OUTPUT ---------
effective month_ qtr_
Obs seq startDate Date qtr number number
56820 31 1999-01-31 1999-04-30 1 4 2
57186 31 1999-01-31 2000-04-30 5 16 6
57551 31 1999-01-31 2001-04-30 9 28 10
57916 31 1999-01-31 2002-04-30 13 40 14
58281 31 1999-01-31 2003-04-30 17 52 18
168391 90 1999-03-31 1999-06-30 1 4 2
168483 90 1999-03-31 1999-09-30 2 7 3
168757 90 1999-03-31 2000-06-30 5 16 6
168849 90 1999-03-31 2000-09-30 6 19 7
169122 90 1999-03-31 2001-06-30 9 28 10
169214 90 1999-03-31 2001-09-30 10 31 11
169487 90 1999-03-31 2002-06-30 13 40 14
169579 90 1999-03-31 2002-09-30 14 43 15
169852 90 1999-03-31 2003-06-30 17 52 18
169944 90 1999-03-31 2003-09-30 18 55 19
280510 149 1999-05-29 2001-02-28 7 22 8
280875 149 1999-05-29 2002-02-28 11 34 12
281240 149 1999-05-29 2003-02-28 15 46 16
282035 150 1999-05-30 2000-02-29 3 10 4
282400 150 1999-05-30 2001-02-28 7 22 8
282765 150 1999-05-30 2002-02-28 11 34 12

Get week number of month SQL Server

I am trying to find the week number of a particular month given a date, so I want to know which week is that for given month
Example if I enter
2016 Feb 2 ---> Week 1
2016 Feb 9 ---> Week 2
2016 June 2 ---> week 1
2016 Jan 25 ---> week 5
Can I achieve this in a T-SQL query?
I have seen the following option
DATEPART(wk, BookingTimeStamp)
But that gives the week number of the year, not the month
The idea is to build result per week for a given month
The first result using "SQL Server and Weeks in Month" returned this article. It shows two ways using DATEPART along with other date parsing functions. Here is one solution:
DECLARE #MyDate DATETIME =GETDATE()
SELECT DATEDIFF(WEEK, DATEADD(MONTH, DATEDIFF(MONTH, 0, #MyDate), 0), #MyDate) +1
I ran into the same problem when I wanted to get not the number of the week in the year, but the number of the week in relation to the month.
In my solution, you can divide every day of date by 7. Then all the days of week 1 will be between 0 and 1. All days of week 2 are between 1 and 2, and so on.
This is a simple example of a practice query where you can use the case construct to find the number of the week.
Solution 1 (a bit confusing)
SELECT
case
when CAST(strftime('%d', date) as real) / 7 BETWEEN 0 AND 1 then 'week_1'
when CAST(strftime('%d', date) as real) / 7 BETWEEN 1 AND 2 then 'week_2'
when CAST(strftime('%d', date) as real) / 7 BETWEEN 2 AND 3 then 'week_3'
when CAST(strftime('%d', date) as real) / 7 BETWEEN 3 AND 4 then 'week_4'
when CAST(strftime('%d', date) as real) / 7 BETWEEN 4 AND 5 then 'a_bit_of_week_5'
end as week
FROM cost
WHERE strftime('%Y', date) = '2022';
Solution 2 (simple)
SELECT
case
when CAST(strftime('%d', date) as integer) BETWEEN 1 AND 7 then 'week_1'
when CAST(strftime('%d', date) as integer) BETWEEN 7 AND 14 then 'week_2'
when CAST(strftime('%d', date) as integer) BETWEEN 14 AND 21 then 'week_3'
when CAST(strftime('%d', date) as integer) BETWEEN 21 AND 28 then 'week_4'
when CAST(strftime('%d', date) as integer) BETWEEN 28 AND 31 then 'a_bit_of_week_5'
end as week
FROM cost
WHERE strftime('%Y', date) = '2022';
Deduction of solution 1
If you're just looking for a solution to an issue, then you don't need to read any further. I just want to tell you how I arrived at this solution and why you can trust it.
This solution can be visualized using the Python programming language.
from matplotlib import pyplot as plt
days = [x for x in range(1, 32)]
days_on_seven = [x / 7 for x in range(1, 32)] # Divide each day by 7
print(days_on_seven)
## Result is below
[0.14285714285714285,
0.2857142857142857,
0.42857142857142855,
0.5714285714285714,
0.7142857142857143,
0.8571428571428571,
1.0,
1.1428571428571428,
1.2857142857142858,
1.4285714285714286,
1.5714285714285714,
1.7142857142857142,
1.8571428571428572,
2.0,
2.142857142857143,
2.2857142857142856,
2.4285714285714284,
2.5714285714285716,
2.7142857142857144,
2.857142857142857,
3.0,
3.142857142857143,
3.2857142857142856,
3.4285714285714284,
3.5714285714285716,
...
3.857142857142857,
4.0,
4.142857142857143,
4.285714285714286,
4.428571428571429]
Visualisation
As you can see, we get an array of increasing numbers. Let's graph the resulting numbers for each day of the month.
from matplotlib import pyplot as plt
import seaborn as sns
sns.lineplot(x=days, y=days_on_seven)
plt.xlabel("Number of day in month")
plt.ylabel("Result of division by 7")
plt.title("day / 7 plot")
The graph below by link
https://i.stack.imgur.com/SIVfT.png
The graph is very crude, but you can see, as noted earlier, that all the days of week 1 will be between 0 and 1. All days of week 2 are between 1 and 2, and so on.
I hope my decision was helpful and interesting to you.

How do I get Google Sheets to give me the average time of day in a series of dates?

I need to find the average time of day over the course of several days.
Column A
Row 1 - 07/13/14 02:45 PM
Row 2 - 07/12/14 10:45 PM
Row 3 - 07/12/14 04:07 PM
Row 4 - 07/11/14 12:30 AM
Row 5 - 07/11/14 06:15 PM
Row 6 - 07/10/14 05:30 PM
Row 7 - 07/10/14 01:00 AM
Row 8 - 07/10/14 04:00 AM
=AVERAGE(A1:A8) returns 7/11/14 2:51 PM. The average of these times should be 8:51 PM. AVERAGE seems to be incorporating the date in the average and I can't have that.
=AVERAGE(TIMEVALUE((A1:A8)) returns #VALUE.
The only way I can get 8:51 PM is by first converting each cell in Column A to either 1/1/14 or 1/2/14 so the dates remain in the 24 hour range by using
=IF(AND(TIMEVALUE(A1) > TIMEVALUE("12:00 AM"), TIMEVALUE(A1) < TIMEVALUE("5:00 AM")),DATEVALUE("1/2/14") + TIMEVALUE(A1), DATEVALUE("1/1/14") + TIMEVALUE(A1))
and then taking the average of the converted cells, which are in Column B
Column B
Row 1 - 01/01/14 02:45 PM
Row 2 - 01/01/14 10:45 PM
Row 3 - 01/01/14 04:07 PM
Row 4 - 01/02/14 12:30 AM
Row 5 - 01/01/14 06:15 PM
Row 6 - 01/01/14 05:30 PM
Row 7 - 01/02/14 01:00 AM
Row 8 - 01/02/14 04:00 AM
I tried using
=IF(AND(TIMEVALUE(A1:A8) > TIMEVALUE("12:00 AM"), TIMEVALUE(A1:A8) < TIMEVALUE("5:00 AM")),DATEVALUE("1/2/14")+DATEVALUE(A1:A8), datevalue("1/1/14")+TIMEVALUE(A1:A8))
but that also returns #VALUE.
Thanks in advance.
From what I can understand, the "correct average" you are providing is in fact wrong. You said
The average of these times should be 8:51 PM.
While I am finding it to be 11:51:30 AM.
In any case, when extracting timevalues from dates, this formula should do the trick:
=ARRAYFORMULA(AVERAGE(TIMEVALUE(A1:A8)))
You may want to take a look at ARRAYFORMULA API, but here's a description extract:
[ARRAYFORMULA] Enables the display of values returned from an array formula into multiple rows and/or columns and the use of non-array functions with arrays.

Resources