MIN和MAX功能导致存储过程性能下降

时间:2012-05-22 13:03:31

标签: sql sql-server sql-server-2008 stored-procedures timeout

我在Production中有一个用于存储给定项目的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 ,依此类推。

StoredProcedure PR_GetCategoryItemsInformation以给定的Category作为输入,读取工作流表并给出如下结果:

@Input:Cat1
输出:

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

对于每个Category,给定ItemCode的SP需要获取工作流的第一行以读取InsertDate以及工作流的最后一行以获取当前{ {1}}。

它在SP实现中归结为如下:

Status

SP已按预期工作多年(2005年),但几个月前它开始提供一些随机超时;由于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 .. 表的记录数量正在增长(2.5M和计数),其性能肯定会越来越差 *

表格已正确编入索引,并且对于它的价值,sql管理工作室不会在SP上建议任何其他索引。
不使用临时表的相同SP就像4倍慢 此时的临时表正在每次调用时平均填充1.5M的行。

根据我有限的dba知识,问题与workflowMIN函数有关,这些函数需要计算才能到达给定类别的每个项目的第一行和最后一行。< / p>

我已经省略了有关工作流程表和SP实现的几个细节,但我希望我所描述的内容足以让我们了解问题。

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

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

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

5 个答案:

答案 0 :(得分:1)

为什么你必须计算日期的最大值和最小值?

你可以做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个日期时间变量。

答案 1 :(得分: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)上创建一个索引,以便快速工作。

答案 2 :(得分: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优化器选择执行查询的最佳方式,而不是使用临时表。 (话虽如此,有一些非常不愉快的经历,我不得不求助于多个查询,因为优化器选择了错误的计划。)

我建议你试一试,看看它是否能解决你的性能问题。

答案 3 :(得分: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

答案 4 :(得分:0)

试试这个 -

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

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