0

workflow特定のアイテムを格納するために使用される本番環境のデータベース テーブルがあります。テーブルの各レコードは、基本的に特定の日付のアイテムのステータスを表します。

単純化しすぎたテーブル構造は次のようなものです。

ワークフロー表

|-------------|------------|---------|----------------|
| Category    | ItemCode   | Status  | InsertDate     |
|-------------|------------|---------|----------------|
|    Cat1     |    Foo1    | 01      | 2012-01-01     |
|-------------|------------|---------|----------------|
|    Cat1     |    Foo1    | 02      | 2012-03-02     |
|-------------|------------|---------|----------------|
|    Cat1     |    Foo1    | 03      | 2012-04-01     | 
|-------------|------------|---------|----------------|
|    Cat1     |    Foo2    | 01      | 2012-04-06     |
|-------------|------------|---------|----------------|
|    Cat1     |    Foo2    | 02      | 2012-05-07     |
|-------------|------------|---------|----------------|
|    Cat1     |    Foo2    | 04      | 2012-05-09     | 
|-------------|------------|---------|----------------|
|    Cat2     |    Foo3    | 01      | 2011-02-03     |    
|-------------|------------|---------|----------------|
|    ...      |    ...     | ..      |....            |    
|-------------|------------|---------|----------------|

したがって、 2012 年 11 日にアイテムFoo1はステータス01に達しました。2012 年 4 月 1 日にステータス03に達しました。

StoredProcedurePR_GetCategoryItemsInformationは、与えられたものを入力として取り、CategoryWorkflow テーブルを読み取り、次のような結果を返します。

@入力: Cat1
出力:

|------------------|---------------|------------------|---------------------|
|   Category       |    ItemCode   | DateOfFirstRecord| StatusOfLatestRecord|
|------------------|---------------|------------------|---------------------|
|     Cat1         |     Foo1      |    2012-01-01    |         03          |    
|     Cat1         |     Foo2      |    2012-04-06    |         04          |

が与えられた SPは、ワークフローの最初の行を取得して を読み取り、ワークフローの最後の行を取得して現在の を取得するCategory必要があります。ItemCodeInsertDateStatus

これは、次のような SP 実装で要約されます。

CREATE PROCEDURE dbo.PR_GetFooItemInformation
    @Category CHAR(3)
AS
BEGIN

    CREATE TABLE #TabTemp (
            Category CHAR(3),
        ItemCode CHAR(3),       
        Status CHAR(2), 
        InsertDate DATETIME
    )

    CREATE CLUSTERED INDEX XIE1TabTemp 
        ON #TabTemp (...)

    CREATE NONCLUSTERED INDEX XIE2TabTemp 
        ON #TabTemp (...)

    INSERT INTO #TabTemp 
    SELECT
                 Category,
                 ItemCode,
                 Status,
                 InsertDate   
    FROM Workflow
    WHERE (Some rules to cut down the number of rows)

  SELECT 
      T1.Category,
      Item.ItemCode,
      T1.InsertDate,
      T2.Status
  FROM 
      Item
  INNER JOIN
      #TabTemp as T1 ON Item.ItemCode = Workflow.ItemCode
  INNER JOIN 
      #TabTemp as T2 ON Item.ItemCode = Workflow.ItemCode
  WHERE
      ...
  AND
      T1.InsertDate= SELECT 
                         MIN(InsertDate) 
                     FROM 
                         #TabTemp as T3 
                     WHERE ..
  AND
      T2.InsertDate = SELECT 
                         MAX(InsertDate) 
                      FROM 
                         #TabTemp as T4 
                      WHERE ..

SP は長年 (2005 年) 期待どおりに機能していましたが、数か月前にランダムなタイムアウトが発生し始めました。テーブルのレコード数workflowが増加しているため (2.5M とカウント)、そのパフォーマンスは確実に悪化します* .

テーブルには適切にインデックスが付けられており、その価値のために、SQL 管理スタジオは SP でそれ以上のインデックスを提案しません。
一時テーブルを使用しない同じ SP は、4 倍遅くなります。
この時点で、一時テーブルには、各呼び出しで平均 1.5M の行が取り込まれています。

私の限定されたデータベース管理者の知識によると、この問題は、特定のカテゴリの各項目の最初と最後の行に到達するために計算する必要があるMINand関数に関連しています。MAX

ワークフロー テーブルと SP の実装に関するいくつかの詳細を省略しましたが、ここで説明した内容が問題の理解に十分であることを願っています。

最後に質問:
この種のシナリオを処理するための SQL 戦略、または SQL サーバー独自のソリューションを知っていますか?

どのような制限がありますか?
SP は BackOffice 関数で使用され、前処理されたサブセットではなく、すべてのライブ レコードを返す必要があります。

* 私はデータベース管理者ではありません。データベース管理者の 1 人が現在、暗い研究室でこの小さな怪物を研究しています。

4

5 に答える 5

1
SELECT  *
FROM    item
CROSS APPLY
        (
        SELECT  MIN(insertDate) AS dateOfFirstRecord
        FROM    workflow wf
        WHERE   wf.itemCode = i.itemCode
        ) fr
OUTER APPLY
        (
        SELECT  TOP 1
                status AS statusOfLatestRecord
        FROM    workflow wf
        WHERE   wf.itemCode = i.itemCode
        ORDER BY
                wf.insertDate DESC
        ) lr

これが高速に機能するように、インデックスを作成しますworkflow (itemCode, insertDate)

于 2012-05-22T13:13:29.890 に答える
1

日付を超えて MAX と MIN を計算する必要があるのはなぜですか?

MAXでできる

 SELECT TOP 1 InsertDate FROM #TabTemp WHERE ... ORDER BY InsertDate DESC

および MIN の場合

 SELECT TOP 1 InsertDate FROM #TabTemp WHERE ... ORDER BY InsertDate ASC

それを2つの日時変数に保存します。

于 2012-05-22T13:12:27.207 に答える
1

あなたが提案する変換は、比較的単純なクエリで実行できます。

select category, ItemCode, min(InsertDate) as DateOfFirstRecord,
       max(case when seqnum = 1 then Status end) as LastStatus           
from (Select category, ItemCode, Status, InsertDate,
             row_number() over (partition by category, ItemCode order by InsertDate desc) as seqnum
      from workflow w
      where category = <category>  
     )  w
group by category, ItemCode;

条件を入れると、これはより複雑になることがわかります。

一般に、一時テーブルを使用するよりも、SQL オプティマイザーにクエリを実行するための最適な方法を選択させることを好みます。(そうは言っても、オプティマイザーが間違った計画を選択したために、複数のクエリに頼らなければならなかった非常に不快な経験がいくつかありました。)

これを試して、パフォーマンスの問題が解決するかどうかを確認することをお勧めします。

于 2012-05-22T13:18:28.673 に答える
0

挿入日はインデックスですか? 複合インデックス(カテゴリ、アイテムコード、挿入日)が必要になります

ほとんどの場合、ボトルネックは不必要に作成している一時テーブルにあります。where 条件を使用して行を除外できます。

このようにクエリを書き直すことは可能ですか?

select category, itemcode, a.InsertDate, b.Status from (
        select category, itemCode, min(InsertDate) minDate, max(insertDate) maxDate
        from table where .. group by categroy, item code) minmax
        join table a on a.category =minmax.category 
                  and a.itemcode=minmax.itemcode and a.insertDate = minmax.mindate
        join table b on b.category =minmax.category and b.itemcode=minmax.itemcode 
        and b.insertDate = minmax.max date) results
于 2012-05-22T13:19:12.497 に答える
0

これを試して -

SELECT Category,
       ItemCode,
       MIN(InsertDate),
       MAX(Status)
FROM workflow 
WHERE Category = @cat
GROUP BY ItemCode 

一時テーブルは必要ない場合があります。このクエリは、目的の出力を取得します。Category と ItemCode を INDEX します。

于 2012-05-22T13:19:31.417 に答える