SQL查询 - 需要提高性能

时间:2011-07-20 09:55:41

标签: sql sql-server database-performance

我有一个数据加载场景,我在其中创建动态sql查询以在我们的服务中提取数据和缓存。有1个表包含所有产品数据:ProductHistory(47列,200,000条记录+并将继续增长)

我需要什么: 使用最大ID,最大版本和最大更改来获取最新产品。

首次尝试:

SELECT distinct Product.* FROM ProductHistory product 
WHERE  product.version = 
(SELECT max(version) from ProductHistory p2 where product.Id = p2.Id 
  and product.changeId = 
(SELECT max(changeid) from ProductHistory p3 where p2.changeId = p3.changeId))

这花了超过2.51分钟。

其他失败的尝试:

select distinct product.* from ProductHistory product 
where CAST(CAST(id as nvarchar)+'0'+CAST(Version as nvarchar)+'0'+CAST(changeid as nvarchar) as decimal) = 
(select MAX(CAST(CAST(id as nvarchar)+'0'+CAST(Version as nvarchar)+'0'+CAST(changeid as nvarchar) as decimal)) from ProductHistory p2 
where product.Id = p2.Id)

它基本上使用与您订购日期时相同的原则,连接按相关性排序的数字。

For example 11 Jun 2007 = 20070711
And in our case: Id = 4 , version = 127, changeid = 32   => 40127032
The zeros are there not to mix up the 3 different ids

但是这个需要3.10分钟! :(

所以,我基本上需要一种方法来让我的第一次尝试查询更好。我也想知道这么多数据,这是我应该期待的最佳检索速度吗?

  1. 我运行了 sp_helpindex ProductHistory 并找到了如下索引:

    PK_ProductHistoryNew - 位于PRIMARY-Id,版本

  2. 上的群集唯一主键
  3. 我在SP中包装了第一个查询,但仍然没有变化。

  4. 那么,想知道我们可以通过其他方式改善这项行动的表现吗?

    谢谢, 玛尼 p.s:我只是在SQL管理stuido中运行这些查询来查看时间。

8 个答案:

答案 0 :(得分:6)

从Sql Server Management Studio运行查询并查看查询计划以查看瓶颈的位置。在任何你看到“表扫描”或“索引扫描”的地方,它必须通过所有数据来查找它正在寻找的内容。如果您创建可用于这些操作的适当索引,则应该提高性能。

答案 1 :(得分:4)

我看到的一些事情:

  • DISTINCT是否必要?如果您执行DISTINCT *,则不太可能有任何好处,但会检查所有字段中的重复项是否有开销。
  • 而不是在WHERE子句中有两个子选择,JOIN到派生表。这应该只处理一次。我怀疑你的WHERE子句正在处理多次。

< - - >

SELECT Product.* 
FROM ProductHistory product 
INNER JOIN ( SELECT P.Id, 
                    MAX(p.version) as [MaxVer], 
                    MAX(p.Changeid) as [MaxChange]
             FROM Product p
             GROUP BY p.ID) SubQ
    ON SubQ.ID = product.ID
    AND SubQ.MaxChange = Product.ChangeID
    AND SubQ.MaxVer = Product.Version

你也应该在Id, Version, ChangeID上有一个索引。

答案 2 :(得分:1)

好吧,将所有内容存储在表中并不是可行的方法。 更好的是将最后一个版本存储在一个表中,并使用另一个版本(具有相同的结构)用于历史记录(因为我猜你对当前产品比对旧产品更感兴趣)。而概念问题将创建许多变通方法......

此外,不要使用DISTINCT,因为它经常隐藏查询中的问题(通常,如果检索到重复项,则意味着您可以更好地进行优化)。

现在,最好的部分:如何解决您的问题?我想你应该使用分组原则给出这样的东西:

SELECT max(id), max(version), max(changeid) 
  FROM ProductHistory p
  WHERE <filter if necessary for old products or anything else>
  GROUP BY version, changeid
  HAVING version = max(version)
     AND changeid = max(changeid)
     AND id = max(id)

但是,如果我看看你的PK,我会感到惊讶,因为你应该只处理id和版本,所以changeid不相关......

我不确定我的请求是否完全正确,因为我无法测试,但我想你可以做一些测试。

答案 3 :(得分:0)

我认为此查询需要(Id, changeId, version)的索引。请提供表格定义,现在表格上的索引以及查询的查询计划。

答案 4 :(得分:0)

这有点时髦,但我想知道分区是否有效:

  SELECT Id
  FROM (
      SELECT Id,
      MAX(version) OVER (PARTITION BY changeId) max_version
      FROM ProductHistory
  ) s
  where version = s.max_version

答案 5 :(得分:0)

我觉得这个查询会花费更长的时间,因为它们的行数会增加,但值得一试:

SELECT * FROM 
(
SELECT Col1, Col2, Col3,
ROW_NUMBER() OVER (PARTITION BY ProductHistory.Id ORDER BY Version DESC, ChangeID DESC) AS RowNumber 
FROM ProductHistory
)
WHERE RowNumber = 1

答案 6 :(得分:0)

尝试这个CTE,它应该是最快的选择,你可能甚至不需要索引来获得更快的速度:

with mysuperfastcte as (
 select product.*, 
 row_number() over (partition by id order by version desc) as versionorder,
 row_number() over (partition by id order by changeid desc) as changeorder 
 from ProductHistory as product
)
select distinct product.*
from mysuperfastcte
where versionorder = 1
and changeorder = 1;

NB。我想你的代码中可能有一个错误,所以请确认并仔细检查我的代码所期望的结果:

  and product.changeId =  (SELECT max(changeid) from ProductHistory p3 where p2.changeId = p3.changeId))
  • 您正在尝试使用相关子查询获取max(changeid),但您也加入了changeid - 这与获取每一行相同。想必你不打算这样做?

此外 - 显然减少了您返回的列数,只需要在运行查询之前运行以下内容并检查消息输出:

SET STATISTICS IO ON

查找具有高逻辑读取的表,并找出索引可以帮助您的位置。

提示:如果我的代码适合您,那么根据您需要的列,您可以这样做:

在ProductHistory上创建索引ix1(id,版本desc)include(changeid,....)。

我希望这有帮助!

答案 7 :(得分:-1)

通常来说,选择max()需要对整个表进行排序。你正在做两次

SELECT TOP 1更快,但你需要确保你的索引是正确的,你有一个正确的ORDER BY。看看你是否可以玩。