6

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

我需要: 通过使用最大id、最大版本和最大changeid获取最新产品。

第一次尝试:

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. 我将第一个查询包装在 SP 中,但仍然没有变化。

那么,想知道我们可以通过哪些其他方式来提高此操作的性能?

谢谢, Mani ps:我只是在 SQL management stuido 中运行这些查询来查看时间。

4

8 回答 8

6

从 Sql Server Management Studio 运行查询并查看查询计划以了解瓶颈在哪里。任何你看到“表扫描”或“索引扫描”的地方都必须遍历所有数据才能找到它要查找的内容。如果您创建可用于这些操作的适当索引,它应该会提高性能。

于 2011-07-20T10:02:46.790 回答
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为此建立一个索引。

于 2011-07-20T10:19:30.000 回答
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,我很惊讶,changeid 不相关,因为您应该只处理 id 和版本......

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

于 2011-07-20T10:16:25.223 回答
0

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

于 2011-07-20T10:20:32.463 回答
0

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

  SELECT Id
  FROM (
      SELECT Id,
      MAX(version) OVER (PARTITION BY changeId) max_version
      FROM ProductHistory
  ) s
  where version = s.max_version
于 2011-07-20T11:21:42.453 回答
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
于 2011-07-20T12:45:34.807 回答
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;

注意。我认为您的代码此时可能存在错误,因此请确认并仔细检查您对我的代码所期望的结果:

  and product.changeId =  (SELECT max(changeid) from ProductHistory p3 where p2.changeId = p3.changeId))
  • 您正在尝试使用相关子查询获取 max(changeid) 但您也加入了 changeid - 这与获取每一行相同。大概你不是故意的吧?

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

设置统计 IO 开启

寻找具有高逻辑读取的表,并找出索引可以帮助您的地方。

提示:如果我的代码适合您,那么根据您需要的列,您可以执行以下操作:

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

我希望这有帮助!

于 2012-09-04T02:40:55.897 回答
-2

一般来说,select max() 需要对整个表进行排序。你做了两次

SELECT TOP 1 更快,但您需要确保您的索引是正确的并且您有正确的 ORDER BY。看看你能不能玩这个。

于 2011-07-20T10:15:40.497 回答