2012-05-01 72 views
1

作爲客戶的MVC 3(EF4)項目的一部分,我最初編寫幾個報表之後,我決定在Microsoft報表服務中構建報表要容易得多(SQL Server上的SSRS 2008 2008)。對於我的大多數報告來說,這已被證明是一件輕而易舉的事情,甚至不必爲我的大多數報告編寫SQL過程(基於模型的BIDS中的語義查詢)。SSRS 2008中複雜的迭代查詢

但是,由於其輸入參數的性質,最初是在返回列表的函數中計算的,而不是LINQ查詢,所以一個報告仍然令人生畏。目標是在日期a和日期b(由報告請求者輸入的變量)之間的範圍內的任何一天返回人表中小於百分之x的所有人員。此繁忙等級信息僅存在於作業表中,其中包含關於分配給哪個人的信息,作業的開始和結束日期及其任務級別。其結果是,我原來的C#的功能確實非常以下(僞代碼):

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 
} 

表結構相關的部分:

People Table: 
-PersonID (PK) 
-other stuff... 

Jobs Table: 
-JobID (PK) 
-StartDate 
-StopDate 
-Allocation 
-AssignedPersonID (FK to People Table) 

正如你所看到的,工作是對某人的計劃如果那個工作的AssignedPersonID是=有人

所以...因爲我現在在SSRS 2008工作,我正在尋找關於如何在相同的輸入參數中獲得SSRS中的相同結果的指導。一些技巧,我目前不知道的語義查詢? SQL存儲PROC與臨時表或表變量?某種數據處理擴展?任何有關此類問題的最佳方法的信息都將不勝感激。

讓我知道是否需要額外的信息/代碼。

編輯:目前正在使用遊標和臨時表的解決方案,幾乎複製我的原始功能的結構。由於遊標的複雜性和低效性,這肯定不是理想的,所以建議/替代方案會有所幫助。

編輯:找到了一個解決方案,絕對不是最有效的。如果某人有更快的方法,將會改變接受的答案。

+0

如果你發佈一個合理的輸入表格樣本和預期的輸出結果,你可以得到更多的幫助。 –

+0

添加了一些表結構的信息,讓我知道如果你需要更多。 – glockman

+0

是否可以將多個工作同時分配給一個人?在那種情況下,你的邏輯是把那個人的分配結果總計爲當天的總「忙碌%」?此外,看起來喬布斯表格假設他們在工作日期範圍內每天都會有相同的百分比,對嗎? – zpert

回答

1

對不起,它花了一段時間回到你身邊。我不認爲有一種方法可以在WHILE循環中循環使用這些日子,因爲這樣可以讓您將個人日期與用戶的輸入範圍進行比較。我認爲這個查詢應該對速度有所幫助,因爲它不使用任何遊標。我在WHILE循環中添加了邏輯,所以你只循環一次,並且你的臨時表作爲結果。變量名稱可能有點不同,您可能會切換< = <或其他,但主要邏輯在那裏。

該查詢包括開始日期和結束日期,並且設置爲包括分配小於或等於用戶輸入的任何人。我希望它可以幫助你:

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 
+0

我想添加的一件事是我最後一次查詢的DISTINCT部分。您可以測試不同的方法來查看哪個方法更快,但是您也可以在WHILE循環的每次迭代中進行檢查,以查看PersonId值是否已存在於@PeopleList中。問題在於你每次都在循環內複製你的查詢。儘管如此,我想至少提到它作爲一個選項。謝謝! – zpert

+0

對不起,花了這麼長時間回到你身邊。謝謝!雖然我已經繼續前進,並且不會立即在我的項目中實施它,但很高興知道實現速度稍快。 – glockman

+0

嘿,沒問題!希望它會幫助你在某一點下線:) – zpert

0

您的問題與SSRS無關。您只需構建查詢以在SSRS上提供數據集。在報告關注的情況下,我並不喋喋不休地詢問數據如何到達數據源,查詢,過程,臨時表,魔術,它只關心字段名稱。

所以,如果你不能將你的linq查詢轉換成T-SQL查詢,我建議你在你的數據庫上運行一個跟蹤,從你的應用程序執行linq查詢(然後你會看到跟蹤上的T-SQL) ,將其複製並粘貼到報表上的數據源中。

+0

我知道我有各種不同的方法來訪問數據,並且想知道哪些方法可能是最優的。對不起,如果我沒有說清楚。關於你對trace的建議,正如你在psuedo-code中看到的那樣,linq查詢實際上並沒有挑出最終的感興趣對象,只是初始的人員列表。因此在計算SQL查詢方面沒有多大幫助。我不知道如何編寫這個函數,因爲它完全是一個查詢,linq或SQL。 – glockman

+0

爲了防止將列表保存爲Queryable,嘗試追蹤將導致LINQ將數據作爲一個大查詢拖到最後......沒有骰子。跟蹤不會解決我的問題,我需要以某種方式將該功能複製爲語義或TSQL查詢。 – glockman

+0

我說只使用trace來複制Linq發送到數據庫的查詢,然後將其粘貼到數據集上 – Diego

0

使用TSQL存儲過程結束臨時表和嵌套遊標。最有可能不是最有效的解決方案,但希望它能幫助其他人解決同樣的問題。如果有人發佈了更有效的解決方案,我會改變接受的答案,因爲這正是我最初所要求的。

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