2012-06-25 47 views
0

我有一個相當複雜(並非常低效)的方式來從大量的時間段(代碼如下)獲取利用率。更好的方法來計算利用率

目前我運行這個爲期8周,它需要30至40秒之間才能返回數據。

我需要定期運行6個月,1年和2年,這顯然需要大量的時間。

是否有更智能的方式來運行此查詢以降低表掃描的數量? 我已經嘗試了幾種加入數據的方式,都似乎返回垃圾數據。

我試着儘可能多地評論代碼,但如果有什麼不清楚的話請告訴我。

表尺寸:

[Stock]  ~12,000 records 
[Contitems] ~90,000 records 

僞代碼爲清楚:

For each week between Start and End: 
    Get list of unique items active between dates (~12,000 rows) 
     For each unique item 
      Loop through ContItems table (~90,000 rows) 
      Return matches 
     Group 
    Group 
Return results 

守則

DECLARE @WEEKSTART DATETIME; -- Used to pass start of period to search 
DECLARE @WEEKEND DATETIME; -- Used to pass end of period to search 
DECLARE @DC DATETIME; -- Used to increment dates 
DECLARE @INT INT; -- days to increment for each iteration (7 = weeks) 
DECLARE @TBL TABLE(DT DATETIME, SG VARCHAR(20), SN VARCHAR(50), TT INT, US INT); -- Return table 

SET @WEEKSTART = '2012-05-01'; -- Set start of period 
SET @WEEKEND = '2012-06-25'; -- Set end of period 
SET @DC = @WEEKSTART; -- Start counter at first date 
SET @INT = 7; -- Set increment to weeks 

WHILE (@DC < @WEEKEND) -- Loop through dates every [@INT] days (weeks) 
BEGIN 
    SET @DC = DATEADD(D,@INT,@DC); -- Add 7 days to the counter 

     INSERT INTO @TBL (DT, SG, SN, TT, US) -- Insert results from subquery into return table 
     SELECT @DC, SUB.GRPCODE, SubGrp.NAME, SUM(SUB.TOTSTK), SUM(USED) 

     FROM 
     (
     SELECT STK.GRPCODE, 1 AS TOTSTK, CASE (SELECT COUNT(*) 
                  FROM ContItems -- Contains list of hires with a start and end date 
                  WHERE STK.ITEMNO = ContItems.ITEMNO -- unique item reference 
                  AND ContItems.DELDATE <= DATEADD(MS,-2,DATEADD(D,@INT,@DC)) -- Hires starting before end of week searching 
                  AND (ContItems.DOCDATE#5 >= @DC -- Hires ending after start of week searching 
                   OR ContItems.DOCDATE#5 = '1899-12-30 00:00:00.000')) -- Or hire is still active 
                WHEN 0 THEN 0 -- None found return zero 
                WHEN NULL THEN 0 -- NULL return zero 
                ELSE 1 END AS USED -- Otherwise return 1 

     FROM Stock STK - List of unique items 

     WHERE [UNIQUE] = 1 AND [TYPE] != 4 -- Business rules 
     AND DATEPURCH < @DC AND (DATESOLD = '1899-12-30 00:00:00.000' OR DATESOLD > DATEADD(MS,-2,DATEADD(D,@INT,@DC))) -- Stock is valid between selected week 
     ) SUB 
     INNER JOIN SubGrp -- Used to get 'pretty' names 
     ON SUB.GRPCODE = SubGrp.CODE 
     GROUP BY SUB.GRPCODE, SubGrp.NAME 






END 

-- Next section gets data from temp table 
SELECT SG, SN, SUM(TT) AS TOT, SUM(US) AS USED, CAST(SUM(US) AS FLOAT)/CAST(SUM(TT) AS FLOAT) AS UTIL 
FROM @TBL 
GROUP BY SG, SN 
ORDER BY TOT DESC 
+0

如果您的日期只有日期,您可能會從代表儒略日期的整數列中獲得更好的性能;或取決於你查詢這些數據的多少,去完整[日期維度](http://www.amadeus-software.com/html/articles/date_dimension.html)可以付清 – AakashM

回答

2

我有兩個建議。

首先,重寫查詢,以from子句擺脫case語句中的「選擇」語句來:

SELECT @DC, SUB.GRPCODE, SubGrp.NAME, SUM(SUB.TOTSTK), SUM(USED) 
FROM (SELECT STK.GRPCODE, 1 AS TOTSTK, 
       (CASE MAX(Contgrp.cnt) -- Or hire is still active 
        WHEN 0 THEN 0 -- None found return zero 
        WHEN NULL THEN 0 -- NULL return zero 
        ELSE 1 
       END) AS USED -- Otherwise return 1 
     FROM Stock STK left outer join -- List of unique items 
      (SELECT itemno, COUNT(*) as cnt 
       FROM ContItems -- Contains list of hires with a start and end date 
       WHERE ContItems.DELDATE <= DATEADD(MS,-2,DATEADD(D,@INT,@DC)) AND -- Hires starting before end of week searching 
        (ContItems.DOCDATE#5 >= @DC OR -- Hires ending after start of week searching 
         ContItems.DOCDATE#5 = '1899-12-30 00:00:00.000' 
        ) 
       group by ITEMNO 
      ) ContGrp 
      on STK.ITEMNO = ContItems.ITEMNO 
     WHERE [UNIQUE] = 1 AND [TYPE] != 4 AND -- Business rules 
       DATEPURCH < @DC AND (DATESOLD = '1899-12-30 00:00:00.000' OR DATESOLD > DATEADD(MS,-2,DATEADD(D,@INT,@DC))) -- Stock is valid between selected week 
     ) SUB INNER JOIN SubGrp -- Used to get 'pretty' names 
     ON SUB.GRPCODE = SubGrp.CODE 
GROUP BY SUB.GRPCODE, SubGrp.NAME 

在這一過程中,我發現了一個可疑的東西。案例陳述在「ItemNo」級別運行,但分組由「GrpCode」進行。因此,「Count(*)」實際上是在團體級別返回總和。這是你的意圖嗎?

第二個是免除WHILE循環,如果你有多個星期。要做到這一點,你只需要將DatePurch轉換爲適當的一週。但是,如果代碼通常只運行一兩個星期,這一努力可能無濟於事。

1

嗯,首先替換where子句中的DATEADD功能。

你已經有

SET @DC = DATEADD(D,@INT,@DC); 

爲什麼不聲明另一個局部變量刪除日期:

WHILE (@DC < @WEEKEND) -- Loop through dates every [@INT] days (weeks) 
BEGIN 
    SET @DC = DATEADD(D,@INT,@DC); 

    DECLARE @DeletionDate DATETIME = DATEADD(MS,-2,DATEADD(D,@INT,@DC)); 

而且使用它的情況下聲明:

CASE (SELECT COUNT(*) .... AND ContItems.DELDATE <= @DeletionDate .... 

而且也是在外條款...

然後,您需要確保您已正確索引您的表。

+0

+1 - 這是剃鬚少量的查詢,但仍然需要太長的時間。索引在所有日期字段和唯一項目代碼上設置。 – bendataclear