[英]SQL Query - Need to improve performance
我有一个数据加载场景,我创建动态 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 分钟:!! :(
所以,我基本上需要一种方法来让我的第一次尝试查询更好。 我也想知道这么多的数据,这是我应该期待的最佳检索速度吗?
我运行sp_helpindex ProductHistory并发现索引如下:
PK_ProductHistoryNew - 位于 PRIMARY-Id、版本上的集群的、唯一的主键
我将第一个查询包装在 SP 中,但仍然没有变化。
那么,想知道我们可以通过哪些其他方式来提高此操作的性能?
谢谢, Mani ps:我只是在 SQL 管理工作室中运行这些查询来查看时间。
从 Sql Server Management Studio 运行查询并查看查询计划以了解瓶颈在哪里。 任何你看到“表扫描”或“索引扫描”的地方都必须通过所有数据 go 找到它正在寻找的内容。 如果您创建可用于这些操作的适当索引,它应该会提高性能。
我看到的一些东西:
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
的索引。
好吧,将所有内容都存储在表中并不是正确的方法。 最好将最后一个版本存储在一个表中,并使用另一个(具有相同结构)的历史记录(因为我猜你对当前产品比对旧产品更感兴趣)。 概念问题将产生许多解决方法......
此外,不要使用 DISTINCT,因为它通常会隐藏查询中的问题(通常,如果检索到重复项,则意味着您可以更好地优化)。
现在,最好的部分:如何解决您的问题? principle giving something like this:我想你应该使用原则给出这样的东西:
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 和版本......
我不确定我的要求是否完全正确,因为我无法测试,但我想你可以做一些测试。
试试这个 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))
另外 - 显然减少您返回的列数,然后在运行查询之前运行以下命令并检查消息 output:
设置统计 IO ON
寻找具有高逻辑读取的表,并找出索引可以帮助您的地方。
提示:如果我的代码适合您,那么根据您需要的列,您可以执行以下操作:
在 ProductHistory 上创建索引 ix1 (id, version desc) include (changeid, .... )。
我希望这有帮助!
我认为您需要在(Id, changeId, version)
上为此查询建立索引。 请提供表定义、现在表上的索引以及查询的查询计划。
这有点时髦,但我想知道分区是否可行:
SELECT Id
FROM (
SELECT Id,
MAX(version) OVER (PARTITION BY changeId) max_version
FROM ProductHistory
) s
where version = s.max_version
我感觉随着行数的增加,这个查询会花费更长的时间,但值得一试:
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
一般来说,select max() 需要对整个表进行排序。 你做了两次
SELECT TOP 1 要快得多,但您需要确保您的索引是正确的并且您有正确的 ORDER BY。 看看你能不能玩这个。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.