After originally composing several reports by hand as part of an MVC 3 (EF4) project for a customer, I have decided it is far easier to compose reports in Microsoft reporting services (SSRS 2008 on SQL server 2008). For most of my reports this has proven to be a breeze, not even having to write SQL procs for most of my reports (semantic query in BIDS based on model).
However one report remains daunting due to the nature of its input parameters and originally being computed in a function returning a list, not a LINQ query. The goal is to return all people in the people table who are less than x percent busy on any day in the range between date a and date b (variables input by report requester). This busy level information only exists in the Job table which has information on which person it is assigned to, the start and end dates of the job, and its task level. As a result, my original C# function does pretty much the following (psuedo-code):
given startDateRange a, stopDateRange b, busyLevel x
{
for each person in people:
List returnPeople = new List()
create array consisting of days from a to b
for each job in jobs:
for each intersecting date of DateRange and job dateRange:
add task level of job to array
for each day in array:
if array[day] < x
returnPeople.Add(person)
break;
return returnPeople
}
Table structure for relevant pieces:
People Table:
-PersonID (PK)
-other stuff...
Jobs Table:
-JobID (PK)
-StartDate
-StopDate
-Allocation
-AssignedPersonID (FK to People Table)
As you can see, a job is on someones schedule if that job's AssignedPersonID is = that someone
So... since I'm now working in SSRS 2008, I am looking for guidance on how to get these same results in SSRS from the same input parameters. Some trick to semantic queries that I am currently unaware of? SQL stored proc with temp table or table variable? Some sort of data processing extension? Any information on what the best approach to this type of problem is would be much appreciated.
Let me know if additional information/code is required.
EDIT: Currently working on a solution using cursors and temp tables that pretty much copies the structure of my original function. This is DEFINITELY not ideal however due to the complexity and inefficient nature of cursors, so advice/alternatives would be helpful.
EDIT: Found a solution, definitely not the most efficient. Will change accepted answer if someone has a significantly faster method.
Sorry that it took a while to get back to you. I don't think there's a way to get around having to loop through the days with a WHILE loop, since that gives you individual days needed to compare to your users' input range. I think this query should help somewhat for speed though, since it doesn't use any cursors. I added the logic within the WHILE loop, so you only loop one time through, and you have your temp table as a result. The variable names might be a little different, and you might switch a <= to < or something, but the main logic is there.
The query includes the start date and end date, and is set to include anyone with an allocation less than or equal to the users' input. I hope it helps you:
DECLARE @startDate datetime, @endDate datetime, @maxAllocation float, @dayCounter datetime
DECLARE @PeopleList TABLE (PersonId int, PersonName varchar(50))
SET @dayCounter = @startDate
WHILE @dayCounter <= @endDate
BEGIN
INSERT INTO @PeopleList SELECT P.PersonId, P.PersonName FROM People P INNER JOIN Jobs J ON P.PersonId = J.AssignedPersonId
WHERE J.StartDate <= @dayCounter AND J.StopDate >= @dayCounter
GROUP BY P.PersonId, P.PersonName
HAVING SUM(J.Allocation) <= @maxAllocation
SET @dayCounter = DATEADD(DAY, 1, @dayCounter)
END
SELECT DISTINCT PersonId, PersonName FROM @PeopleList
your problem is not related to SSRS. You just need to build the query to feed the data set on SSRS. I doesnt mattet how the data is reaching the data source, query, procedure, temp table, magic, as far as the report is concern, it just care about field names.
So if you cant transform your linq query to a T-SQL query, I suggest that you run a trace on your database, execute the linq query from your application (you then will see the T-SQL on the trace), copy it and paste on the datasource on your report.
Ended up using a TSQL stored proc, with a combination of temp tables and nested cursors. Most likely not the most efficient solution, but hopefully it will help others with the same problem. I will change the accepted answer if someone posts a more efficient solution, as that is what I was originally asking for.
DECLARE @PeopleList TABLE(
PersonID CHAR(15)
)
DECLARE @PersonValue CHAR(10)
--Local busyList table with all dates in desired range
DECLARE @DateValue DATETIME
DECLARE @TotalAllocation FLOAT
DECLARE @BusyList TABLE(
DateInRange DATETIME,
AllocationForDate FLOAT
)
DECLARE @dayCounter DATETIME
SET @dayCounter = @startDate
INSERT INTO @BusyList VALUES (@startDate, 0)
WHILE @dayCounter<@endDate
BEGIN
SET @dayCounter = @dayCounter + 1
INSERT INTO @BusyList VALUES (@dayCounter, 0)
END
DECLARE currentUser CURSOR LOCAL FAST_FORWARD FOR
SELECT PersonID FROM [People]
OPEN currentPerson
FETCH NEXT FROM currentPerson INTO @PersonValue
WHILE (@@FETCH_STATUS = 0)
BEGIN
DECLARE currentDay CURSOR LOCAL FAST_FORWARD FOR
SELECT DateInRange FROM @BusyList
OPEN currentDay
SET @TotalAllocation = NULL
UPDATE @BusyList SET AllocationForDate = 0
FETCH NEXT FROM currentDay INTO @DateValue
WHILE (@@FETCH_STATUS = 0)
BEGIN
SET @TotalAllocation = (SELECT SUM(TaskLevel) FROM Job WHERE AssignedPersonID = @PersonValue AND @DateValue BETWEEN Opportunity.StartDate AND Job.EndDate)
UPDATE @BusyList
SET AllocationForDate = @TotalAllocation WHERE (DateInRange = @DateValue)
FETCH NEXT FROM currentDay INTO @DateValue
END
CLOSE currentDay
DEALLOCATE currentDay
IF EXISTS(SELECT * FROM @BusyList WHERE AllocationForDate <= @allocation OR AllocationForDate IS NULL)
BEGIN
INSERT INTO @PersonList VALUES (@PersonValue)
END
PRINT @PersonValue
FETCH NEXT FROM currentUser INTO @PersonValue
END
SELECT * FROM [People] WHERE EXISTS (SELECT * FROM @PersonList Where [@PersonList].PersonID = [Person].PersonID)
CLOSE currentPerson
DEALLOCATE currentPerson
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.