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
4

2 に答える 2

2

2つの提案があります。

最初に、「select」ステートメントを case ステートメントから from 句に移動するようにクエリを書き直します。

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 

そうこうしているうちに、怪しいものを見つけました。case ステートメントは「ItemNo」のレベルで動作していますが、グループ化は「GrpCode」によるものです。したがって、「Count(*)」は実際にはグループ レベルで合計を返しています。これはあなたが意図したものですか?

2 つ目は、複数の週がある場合に WHILE ループを省くことです。これを行うには、DatePurch を適切な週に変換するだけです。ただし、コードが通常 1 ~ 2 週間で実行される場合、この作業はあまり役に立たない可能性があります。

于 2012-06-25T13:25:51.153 に答える
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 ステートメントで使用します。

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

また、外側の where 句で...

次に、テーブルのインデックスが正しく作成されていることを確認する必要があります。

于 2012-06-25T10:56:29.537 に答える