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-01-01,项目Foo1已达到状态01;在2012-04-01已达到状态03等等。

StoredProcedurePR_GetCategoryItemsInformation将给定的值Category作为输入,读取 Workflow 表并给出如下结果:

@输入:Cat1
输出:

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

SP,给定一个Category,对于每个ItemCode需要获取工作流的第一行来读取的InsertDate和工作流的最后一行来获取当前的Status

它归结为如下所示的 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 management studio 不建议在 SP 上建立任何进一步的索引。
不使用临时表的同一个 SP 速度要慢 4 倍。
此时的临时表在每次调用时平均填充 150 万行。

就我有限的 dba 知识而言,这个问题与MIN需要MAX计算以到达给定类别的每个项目的第一行和最后一行的函数有关。

我省略了有关工作流表和 SP 实现的一些细节,但我希望我所描述的内容足以让您了解问题所在。

最后一个问题:
您知道处理这种情况的任何 sql 策略甚至 sql-server 专有解决方案吗?

我有什么样的限制?
好吧,SP 用于 BackOffice 函数,应该返回所有实时记录,而不是预处理的子集。

* 我不是 dba;其中一位 dba 目前正在他黑暗的实验室里研究这个小怪物。

4

5 回答 5

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 datetime 变量。

于 2012-05-22T13:12:27.207 回答
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

您建议的转换可以通过一个相对简单的查询来完成:

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 

您可能不需要临时表。此查询将获得所需的输出。索引类别和项目代码。

于 2012-05-22T13:19:31.417 回答