我在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知识,问题与workflow
和MIN
函数有关,这些函数需要计算才能到达给定类别的每个项目的第一行和最后一行。< / p>
我已经省略了有关工作流程表和SP实现的几个细节,但我希望我所描述的内容足以让我们了解问题。
最后一个问题:
你知道任何sql策略甚至sql-server专有解决方案来处理这种情况吗?
我有什么样的限制?
好吧,SP用在BackOffice函数上,应该返回所有实时记录而不是预处理子集。
*我不是dba;其中一个dba正在他的黑暗实验室里研究这个小怪物。
答案 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
您可能不需要临时表。此查询将获得所需的输出。 索引类别和项目代码。