SSRS Expressions Nested IIF with SUM not working - sql-server

I am using SSRS 2016 / Report Builder and I am trying to get a nested IIF expression to SUM number of minutes spent in one particular Room (P241). There are some other conditions to deal with as well, which complicate matters.
I am able to successfully sum total minutes for ALL Rooms using the following expression, but I can't seem to come up with an expression to separate out the rooms:
Expression That Calculates Total Minutes Spent in ALL Rooms (works)
=Sum(IIF(IsNothing(Fields!ServiceEndDate.Value),
IIF((Parameters!EffectiveDateTime.Value <= Fields!EffectiveDateTime.Value),
DATEDIFF("n", Fields!EffectiveDateTime.Value, DATEADD("d", 1, Parameters!EffectiveDateTime2.Value)),
DATEDIFF("n", Parameters!EffectiveDateTime.Value, DATEADD("d", 1, Parameters!EffectiveDateTime2.Value))),
IIF((Parameters!EffectiveDateTime.Value <= Fields!EffectiveDateTime.Value),
DATEDIFF("n", Fields!EffectiveDateTime.Value, Fields!ServiceEndDate.Value),
DATEDIFF("n", Parameters!EffectiveDateTime.Value, Fields!ServiceEndDate.Value))))
Data Table
ACCT#
Room
Service Effective Date
Service End Date
1
P147
08-Dec-2021 20:13
07-Feb-2022 11:44
2
P241
28-Jan-2022 16:41
06-Feb-2022 19:20
3
P147
31-Jan-2022 13:51
04-Mar-2022 10:15
4
P241
06-Mar-2022 23:58
Useful info: I have two parameters #EffectiveDateTime and #EffectiveDateTime2 --> the user running the report uses these to specify a beginning and end dates from which to calculate the number of minutes.
Attempt #1
Based on the working expression that totals minutes for all rooms. I added 'AND Fields!Room.Value = "P241"' into the IIF conditions. Result: ended up with total minutes of ALL ROOMS. I concluded (right or wrong) that the issue might be because there was no way to account for rooms that were NOT P241
Expression that calculates total minutes for ALL ROOMS
=Sum(
IIF(IsNothing(Fields!ServiceEndDate.Value),
IIF((Parameters!EffectiveDateTime.Value <= Fields!EffectiveDateTime.Value AND Fields!Room.Value = "P241"),
DATEDIFF("n", Fields!EffectiveDateTime.Value, DATEADD("d", 1, Parameters!EffectiveDateTime2.Value)),
DATEDIFF("n", Parameters!EffectiveDateTime.Value, DATEADD("d", 1, Parameters!EffectiveDateTime2.Value))),
IIF((Parameters!EffectiveDateTime.Value <= Fields!EffectiveDateTime.Value AND Fields!Room.Value = "P241"),
DATEDIFF("n", Fields!EffectiveDateTime.Value, Fields!ServiceEndDate.Value),
DATEDIFF("n", Parameters!EffectiveDateTime.Value, Fields!ServiceEndDate.Value))))
Attempt #2
My second attempt was based on the idea that I needed an "else" option for the room selection to work. RESULT: I triple-checked commas and brackets, but kept failing with ERROR: "The Value expression for the textrun ‘Textbox275.Paragraphs[0].TextRuns[0]’ contains an error: [BC30516] Overload resolution failed because no accessible 'IIf' accepts this number of arguments." The expression below has "" as the else part, but I also tried 0 and Nothing after scouring forums for suggestions
=IIF(Fields!Room.Value = "P241"),
SUM(
IIF((IsNothing(Fields!ServiceEndDate.Value)),
IIF((Parameters!EffectiveDateTime.Value <= Fields!EffectiveDateTime.Value),
DATEDIFF("n", Fields!EffectiveDateTime.Value,DATEADD("d",1,Parameters!EffectiveDateTime2.Value)),
DATEDIFF("n",Parameters!EffectiveDateTime.Value,DATEADD("d",1,Parameters!EffectiveDateTime2.Value))),
IIF((Parameters!EffectiveDateTime.Value <= Fields!EffectiveDateTime.Value),
DATEDIFF("n",Fields!EffectiveDateTime.Value,Fields!ServiceEndDate.Value),
DATEDIFF("n",Parameters!EffectiveDateTime.Value,Fields!ServiceEndDate.Value)))
),""
Attempt 3
Tried rearranging IIF/SUM and it really hated that. Result: ERROR:The Value expression for the textrun ‘Textbox275.Paragraphs[0].TextRuns[0]’ has a scope parameter that is not valid for an aggregate function. The scope parameter must be set to a string constant that is equal to either the name of a containing group, the name of a containing data region, or the name of a dataset.
=SUM(
IIF(Fields!Room.Value = "P241"),
IIF((IsNothing(Fields!ServiceEndDate.Value)),
IIF((Parameters!EffectiveDateTime.Value <= Fields!EffectiveDateTime.Value),
DATEDIFF("n", Fields!EffectiveDateTime.Value,DATEADD("d",1,Parameters!EffectiveDateTime2.Value)),
DATEDIFF("n",Parameters!EffectiveDateTime.Value,DATEADD("d",1,Parameters!EffectiveDateTime2.Value))),
IIF((Parameters!EffectiveDateTime.Value <= Fields!EffectiveDateTime.Value),
DATEDIFF("n",Fields!EffectiveDateTime.Value,Fields!ServiceEndDate.Value),
DATEDIFF("n",Parameters!EffectiveDateTime.Value,Fields!ServiceEndDate.Value)))
),0)

I believe #3 is the correct approach if you only want to sum values for Room.Value = "P241". Your problem is that your parenthesis are misplaced, starting with the one immediately after your room test.
Below is a slightly reformatted version with adjusted parenthesis placement.
=SUM(
IIF(Fields!Room.Value = "P241",
IIF((IsNothing(Fields!ServiceEndDate.Value)),
IIF((Parameters!EffectiveDateTime.Value <= Fields!EffectiveDateTime.Value),
DATEDIFF("n", Fields!EffectiveDateTime.Value,DATEADD("d",1,Parameters!EffectiveDateTime2.Value)),
DATEDIFF("n",Parameters!EffectiveDateTime.Value,DATEADD("d",1,Parameters!EffectiveDateTime2.Value))
), -- IIF #3
IIF((Parameters!EffectiveDateTime.Value <= Fields!EffectiveDateTime.Value),
DATEDIFF("n",Fields!EffectiveDateTime.Value,Fields!ServiceEndDate.Value),
DATEDIFF("n",Parameters!EffectiveDateTime.Value,Fields!ServiceEndDate.Value)
) -- IIF #4
), -- IIF #2
0
) -- IIF #1
) -- Sum
In short, I believe you want to change:
=SUM(<original calculation>)
to:
=SUM(IIF(Fields!Room.Value = "P241", <original calculation>, 0))
I suggest that you paste this into an editor (such as Notepad++) that supports highlighting of matching parenthesis. That will help you review the placement.

Related

unable to understand the dateadd function in SQL

I have a SQL query like
SET THIS_YEAR_END = '2022-11-01';
SET THIS_YEAR_START = DATEADD(DAY, -4*7+1, $THIS_YEAR_END);
SET LAST_YEAR_END = '2021-11-02';
SET LAST_YEAR_START = DATEADD(DAY, -4*7+1, $LAST_YEAR_END);
select end_date from (
select * from data
where DATE>= DATEADD(DAY, -27 * 7, $LAST_YEAR_START))
AND END_DATE BETWEEN CUST.END_DATE - 26 * 7 AND CUST.END_DATE - 7
I'm confused with this dateadd function in SQL. Can anyone please explain what exactly it's doing?
Here is the docs: https://docs.snowflake.com/en/sql-reference/functions/dateadd.html
Basically DATEADD is adding a specified value to a certain date. The first parameter is indicating the units of time that you want to add (e.g. DAY or MONTH), the second parameter specifies the number of units (e.g. number of days or number of months) and the third paramter is the date, to which you want to add something.
In your example in line 2 you are reducing THIS_YEAR_END by -4*7+1 (=-27) days.

Convert ISO8061 Duration to a datetime or time value

I have some ISO8601 Durations (not to be confused with ISO601 datetime)
Here are some example valid values:
P1D
PT0H
PT11M
P1DT2H15M
PT10H11M
PT2H46M12S
the specification is here:
https://en.wikipedia.org/wiki/ISO_8601#Durations
Ideally, I would like to take these values and parse them as either time or datetime2 values
to make it easier to work with
I was able to brute force parse these using string functions but the code is complex and seems error prone hoping there was a better way?
with anchors as
(
SELECT
dt[duration]
, NULLIF(CHARINDEX('D', dt.duration), 0) As DLocation
, NULLIF(CHARINDEX('T', dt.duration), 0) As TLocation
, NULLIF(CHARINDEX('H', dt.duration), 0) As HLocation
, NULLIF(CHARINDEX('M', dt.duration), 0) As MLocation
, NULLIF(CHARINDEX('S', dt.duration), 0) As SLocation
, LEN(dt.duration) as TotalLength
FROM dbo.DurationTest dt
)
SELECT
duration
,DaysValue = CAST(ISNULL(SUBSTRING(duration, 2, (DLocation - 2)), 0) as tinyint)
,HoursValue = CAST(ISNULL(SUBSTRING(duration, TLocation + 1, (HLocation - TLocation) - 1 ), 0) as tinyint)
,MinutesValue = CAST(ISNULL(SUBSTRING(duration, COALESCE(HLocation, TLocation) + 1, MLocation - COALESCE(HLocation, TLocation) - 1), 0) as tinyint)
,SecondsValue = CAST(ISNULL(SUBSTRING(duration, COALESCE(MLocation, TLocation) + 1, SLocation - COALESCE(MLocation, TLocation) - 1 ), 0) as tinyint)
FROM anchors
this code gets the values into days, hours, minutes and seconds. converting to either a int value to seconds or a datetime2 is pretty well documented from that. As pointed out in the comments a time data type will only work for values <24 so I've kind of given up on that.
for further context this data is coming from ADP payroll webservice and this field tracks the daily time entry you would think it would be less than 24hrs but I have some outliers in my dataset.
I've created the following enhancement request here (not sure if it will get traction or not):
https://feedback.azure.com/d365community/idea/557e5b51-1824-ed11-a81b-6045bd853198
Here's one working example. Read the header for what it does but this one returns the ADP "ISO-Like" durations as Decimal Hours to 6 Decimal Places. If you need something else instead, the changes will be quite simple to make. Let me know.
Here's the iTVF (inline Table Valued Function). Details are in the comments. As so often happens, the comments are bigger than the code itself.
CREATE FUNCTION dbo.AdpDurToDecHours
/*********************************************************************
Purpose:
Convert ADP payroll webservice daily time entries (durations) from a
subset of the ISO Duration notation to Decimal Hours.
*** DO NOT USE THIS FUNCTION IF YEAR AND/OR MONTH CAN BE PRESENT! ***
----------------------------------------------------------------------
Usage Examples:
--===== Convert a set of ADP time entries from a table.
SELECT st.SomeOtherColumns
,adp.DecimalHours
FROM dbo.SomeTable st
CROSS APPLY dbo.AdpDurToDecHours(st.AdpDur) adp
;
--===== Convert a single value from a variable.
SELECT adp.DecimalHours FROM dbo.AdpDurToDecHours(#AdpDur) adp
;
----------------------------------------------------------------------
Dependencies:
1. None
----------------------------------------------------------------------
Programmer Notes:
1. DO NOT USE THIS FUNCTION IF YEAR AND/OR MONTH CAN BE PRESENT!!!
2. This code will fail with the following error if you pass it a
NULL, Empty String, or BLANK String. Other "non-ISO" compliant
values may also fail or create improper output but such other
things have not been tested. Ostensibly, the values passed should
be compliant for proper returns.
Msg 1014, Level 15, State 1, Line 30
A TOP or FETCH clause contains an invalid value.
3. This code follows the ISO specification for durations except for
"Year" and "Month", which have been intentionally excluded because
they're "indeterminate" for durations unless combined with a
starting date.
----------------------------------------------------------------------
References:
1. https://en.wikipedia.org/wiki/ISO_8601#Durations
2. https://stackoverflow.com/questions/73479091/convert-iso8061-duration-to-a-datetime-or-time-value
----------------------------------------------------------------------
Revision History:
Rev 00 - 25 Aug 2022 - Jeff Moden
- Create and test PoP code.
Rev 01 - 26 Aug 2022 - Jeff Moden
- Exclude "Year" and "Month".
- Convert output to Decimal Hours.
Rev 02 - 27 Aug 2022 - Jeff Moden
- Convert to documented function (iTVF).
*********************************************************************/
(#AdpDur VARCHAR(36))
RETURNS TABLE WITH SCHEMABINDING
RETURN WITH
--==== Creates an inline sequence generator
S1(N) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1))S0(N))
,cteTally(N) AS (SELECT TOP(LEN(#AdpDur))
N = ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM S1 a,S1) --1 to 36 rows max
,cteLocateDurTypes AS
(--==== Find positions/lagging positions of the duration type tokens
SELECT DurType = SUBSTRING(#AdpDur,t.N,1)
,LN = LAG(t.N,1,0) OVER (ORDER BY t.N)
,t.N
FROM cteTally t
WHERE SUBSTRING(#AdpDur,N,1) IN ('P','W','D','T','H','M','S')
)
,cteIsolateValue AS
(--==== Determine the value for each duration token present
SELECT DurType
,Value = CONVERT(INT,SUBSTRING(#AdpDur,LN+1,N-1-LN))
FROM cteLocateDurTypes
)--==== Convert DurType Values to seconds and sum as Decimal Hours
SELECT DecimalHours = SUM(
CASE DurType
WHEN 'S' THEN Value
WHEN 'M' THEN Value*60
WHEN 'H' THEN Value*3600
WHEN 'D' THEN Value*86400
WHEN 'W' THEN Value*604800
ELSE 0
END
)/3600.0
FROM cteIsolateValue
;
GO
Here's a short test table based on what #JasonHorner posted with a few additions.
--===== Drop the test table if it exists to make reruns easier in SSMS
DROP TABLE IF EXISTS #DurationTest;
GO
--===== Create and populate the test table on-the-fly.
SELECT Duration = CONVERT(VARCHAR(36),v.Duration)
INTO #DurationTest
FROM (VALUES
--(NULL) --Causes Failure
--,('') --Causes Failure
--,(' ') --Causes Failure
('PT') --It could happen
,('P1W') --Added this entry
,('P1D')
,('PT1H') --Added this entry
,('PT1M') --Added this entry
,('PT1S') --Added this entry
,('PT0H')
,('PT10H') --Added this entry
,('PT11M')
,('P1DT2H15M')
,('PT10H11M')
,('PT2H46M12S')
,('P1W2DT12H46M12S') --Added this entry
)v(Duration)
;
GO
Here's a test of the function using the test table as the source.
--===== Simple test
SELECT *
FROM #DurationTest dt
CROSS APPLY dbo.AdpDurToDecHours(dt.Duration) adp
;
GO
And, finally, here are the results from the test.

SQL Server : measuring real-time efficiency by operator

I've been working on some SQL code to measure efficiency in real-time for some production data. Here's a quick background:
Operators will enter in data for specific sub assemblies. This data looks something like this:
ID PO W/S Status Operator TotalTime Date
60129515_2000_6_S025 107294 S025 Completed A 38 05/08/2020
60129515_2000_7_S025 107294 S025 Completed A 46 05/08/2020
60129515_2000_8_S025 107294 S025 Completed A 55 05/08/2020
60129515_2025_6_S020 107295 S020 Completed B 58 05/08/2020
60129515_2025_7_S020 107295 S020 Completed B 47 05/08/2020
60129515_2025_8_S020 107295 S020 Completed B 45 05/08/2020
60129515_2000_1_S090 107294 S090 Completed C 33 05/08/2020
60129515_2000_2_S090 107294 S090 Completed C 34 05/08/2020
60129515_2000_3_S090 107294 S090 Completed C 21 05/08/2020
The relevant columns are the Operator, TotalTime and Date (note that the date is stored as varchar(50) because it plays nicer with Microsoft PowerApps that way).
What I need to do is:
Aggregate the sum of "TotalTime" grouped by Operator
Calculate the time elapsed based on a condition:
If between 7AM and 4PM, calculate the time elapsed since 7AM of the current day
If after 4PM, return the total time between 7AM and 4PM of the current day
Divide the SUM(TotalTime) by the TimeElapsed (AKA the first list item / second list item) in order to get a rough estimate of labor hours worked vs. hours passed in the day.
This calculation would change every time the query was ran. This will allow the Microsoft PowerApp that is pulling this query to refresh the efficiency measure in real time. I've taken a stab at it already - see below:
SELECT
md.Operator,
CASE
WHEN DATEADD(HOUR, -5, GETUTCDATE()) > CONVERT(DATETIME, CONVERT(DATE, DATEADD(HOUR, -5, GETUTCDATE()))) + '7:00' AND GETDATE() < CONVERT(DATETIME, CONVERT(DATE, DATEADD(HOUR, -5, GETUTCDATE()))) + '15:45'
THEN (SUM(isNull(md.TotalTime, 0)) + SUM(isNull(md.DelTime, 0))) * 1.0 / DATEDIFF(MINUTE, CONVERT(DATETIME, CONVERT(DATE, DATEADD(HOUR, -5, GETUTCDATE()))) + '7:00' , DATEADD(HOUR, -5, GETUTCDATE())) * 100.0
ELSE (SUM(isNull(md.TotalTime, 0)) + SUM(isNull(md.DelTime, 0))) / 420 * 100.0
END AS OpEfficiency
FROM
[Master Data] AS md
WHERE
md.[Date] = CONVERT(varchar(50), DATEADD(HOUR, -5, GETUTCDATE()), 101)
GROUP BY
md.Operator
Note: the DelTime is a different column regarding delay times. I am also converting back from UTC time to avoid any time zone issues when transferring to PowerApps.
However, this is horribly inefficient. I am assuming it is because the Date needs to be converted to datetime every single time. Would it work better if I had a calculated column that already had the date converted? Or is there a better way to calculate time elapsed since a certain time?
Thanks in advance.
There are a few things you can do to increase efficiency considerably. First, you want to make sure SQL can do a simple comparison when selecting rows, so you'll start by calculating a string to match your date on since your [Date] field is a string not a date.
Second, calculate the minutes in your shift (either 540 for a full shift or scaled down to 0 at 7 AM exactly) ahead of time so you aren't calculating minutes in each row.
Third, when summing for operators, use a simple sum on the minutes and calculate efficiency from that sum and your pre-calculated shift so far minutes.
One note - I'm casting the minutes-so-far as FLOAT in my example, maybe not the best type but it's clearer than other decimal types like DECIMAL(18,6) or whatever. Pick something that will show the scale you want.
My example uses a Common Table Expression to generate that date string and minutes-so-far FLOAT, that's nice because it fits in a direct query, view, function, or stored procedure, but you could DECLARE variables instead if you wanted to.
By filtering with an INNER JOIN on the [Date] string against the pre-calculated TargetDate string, I make sure the data set is pared down to the fewest records before doing any math on anything. You'll definitely want to INDEX [Date] to keep this fast as your table fills up.
All these together should give a pretty fast query, good luck
with cteNow as ( --Calculate once, up front - date as string, minutes elapsed as FLOAT (or any non-integer)
SELECT CASE WHEN 60*DATEPART(HOUR, GETUTCDATE())+DATEPART(MINUTE, GETUTCDATE()) > 60*21
--4PM in UTC-5, expressed in minutes
THEN CONVERT(float,(16-7)*60) --minutes in (4 PM-7 AM) * 60 minutes/hour
ELSE --Assume nobody is running this at 6 AM, so ELSE = between 7 and 4
CONVERT(float,60*DATEPART(HOUR, GETUTCDATE()) + DATEPART(MINUTE, GETUTCDATE()) - ((7+5)*60))
--Minutes since midnight minus minutes from midnight to 7 AM, shifted by
--UTS offset of 5 hours
END as MinutesToday --Minutes in today's shift so far
, FORMAT(DATEADD(HOUR,-5,GETUTCDATE()),'MM/dd/yyyy') as TargetDate --Date to search for
--as a string so no conversion in every row comparison. Also, index [Date] column
)
SELECT md.Operator, SUM(md.TotalTime) as TotalTime, SUM(md.TotalTime) / MinutesToday as Efficiency
FROM [Master Data] AS md INNER JOIN cteNow as N on N.TargetDate = md.[Date]
GROUP BY md.Operator, MinutesToday
BTW, you didn't make allowances for lunch or running before 7 AM, so I also ignored those. I think both could be addressed in cteNOW without adding much complexity.

Is Date between 1st of 1st Month and 29th of 7th month of any year (Sql Server 2008+)

I need to check if a Date is between 1st of 1st month and 29th of 7th month of any year.
Before going ahead and hacking extracting Month Day from the date and comparing it to being between 0101 and 0729 (or just checking if the date dd mm is less than 629) type of hideousity solution, I must ask is there a function to check wether any date falls in between particalr days and month , invariant of the it's year.
Other wise extracting months and days from the date and doing some hacky arithmatic is easy, but I want to be leave a better code for the poor programmer who will come after me and not having to guess why the hell some freaky made up arithmatic is going on, I rather be explicit even if it takes longer.
Another option:
DECLARE #TheDate date = '2016-07-29';
SELECT 1
WHERE DATEADD(Year, -(YEAR(#TheDate) - 2000), #TheDate)
BETWEEN '2000-01-01' AND '2000-07-29'
where datetimeField < dateFromParts( year( dateTimeField ), 7, 30)
EDIT: Above would work in 2012 and later. For 2008 you could do some arithmetic which is "very simple" for a developer:
where datetimeField < CAST(CAST(year(dateTimeField) AS CHAR(4))+'0730' AS DATE);
Or simplified a bit:
where datetimeField < CAST(year(dateTimeField) AS CHAR(4))+'0730';
PS: A common pitfall in using BETWEEN (which acts like x >= v1 and x <= v2) on a datetime field, where the field might have time part, is not suggested and would never help to precisely get the correct records (because there is no way to specify the ending time). Instead you should always use x >= v1 and x < v2 style where v2 is the minimum exclusive upper value. ie: To get all the sales in May 2000 (saleDate has time component):
saleDate >= '20000501' and saleDate < '20000601'
NOT:
saleDate between '20000501' and '20000531'
OR NOT:
saleDate >= '20000501' and saleDate <= '20000531'
Select * from TableWithNoName where DateField1 between DATETIMEFROMPARTS( YEAR(DateField1),1, 1, 0, 0, 0, 0 ) and DATETIMEFROMPARTS( YEAR(DateField1),7, 29, 23, 59, 59, 999 )
assuming DateField1 is a Date Value

Report Builder 3.0 - grouping rows by time of day

I am trying to create a table within a report that appears as follows:
The data set is based on this query:
SELECT
DATENAME(dw, CurrentReadTime) AS 'DAY',
DATEPART(dw, CurrentReadTime) AS 'DOW',
CAST(datename(HH, CurrentReadTime) as int) AS 'HOD',
AVG([Difference]) AS 'AVG'
FROM
Consumption
INNER JOIN Readings ON Readings.[RadioID-Hex] = Consumption.[RadioID-Hex]
WHERE
CONCAT([Building], ' ', [Apt]) = #ServiceLocation
GROUP BY
CurrentReadTime
ORDER BY
DATEPART(DW, CurrentReadTime),
CAST(DATENAME(HH, CurrentReadTime) AS INT)
The data from this table returns as follows:
In report builder, I have added this code to the report properties:
Function GetRangeValueByHour(ByVal Hour As Integer) As String
Select Case Hour
Case 6 To 12
GetRangeValueByHour = "Morning"
Case 12 to 17
GetRangeValueByHour = "Afternoon"
Case 17 to 22
GetRangeValueByHour = "Evening"
Case Else
GetRangeValueByHour = "Overnight"
End Select
Return GetRangeValueByHour
End Function
And this code to the "row group":
=Code.GetRangeValueByHour(Fields!HOD.Value)
When I execute the report, selecting the parameter for the target service location, I get this result:
As you will notice, the "Time of Day" is displaying the first result that meets the CASE expression in the Report Properties code; however, I confirmed that ALL "HOD" (stored as an integer) are being grouped together by doing a SUM on this result.
Furthermore, the actual table values (.05, .08, etc) are only returning the results for the HOD that first meets the requirements of the CASE statement in the VB code.
These are the things I need resolved, but can't figure out:
Why isn't the Report Properties VB code displaying "Morning", "Afternoon", "Evening", and "Overnight" in the Time of Day column?
How do I group together the values in the table? So that the AVG would actually be the sum of each AVG for all hours within the designated range and day of week (6-12, 12-18, etc on Monday, Tuesday etc).
To those still reading, thanks for your assistance! Please let me know if you need additional information.
I'm still not sure if I have a clear picture of your table design, but I'm imagining this as a single row group that's grouped on this expression: =Code.GetRangeValueByHour(Fields!HOD.Value). Based on this design and the dataset above, here's how I would solve your two questions:
Use the grouping expression for the value of the Time of Day cell, like:
Add a SUM with a conditional for the values on each day of the week. Example: the expression for Sunday would be =SUM(IIF(Fields!DOW.Value = 1, Fields!AVG.Value, CDec(0))). This uses CDec(0)instead of 0 because the AVG values are decimals and SSRS will otherwise throw an aggregate of mixed data types error by interpreting 0 as an int.

Resources