我有一个包含大量数据的表格,我们特别关注date
字段。原因是数据量上升了大约30倍,旧的方式很快就会崩溃。我希望您可以帮助我优化需求的查询:
例如,当前表包含5秒(+/-一点点)间隔的数据。我需要对该表进行采样并获得最接近30秒间隔的记录。
我现在所做的工作得很好。我只是好奇是否有办法更优化它。如果我能在Linq To SQL中做到这一点,那也是很好的。考虑到日期值的数量(约200万行最小值),我甚至对索引的建议感兴趣。
declare @st datetime ; set @st = '2012-01-31 05:05:00';
declare @end datetime ; set @end = '2012-01-31 05:10:00';
select distinct
log.* -- id,
from
dbo.fn_GenerateDateSteps(@st, @end, 30) as d
inner join lotsOfLogData log on l.Id = (
select top 1 e.[Id]
from
lotsOfLogData as log -- contains data in 5 second intervals
where
log.stationId = 1000
-- search for dates in a certain range
AND utcTime between DateAdd(s, -10, dt) AND DateAdd(s, 5, dt)
order by
-- get the 'closest'. this can change a little, but will always
-- be based on a difference between the date
abs(datediff(s, dt, UtcTime))
)
-- updated the query to be correct. stadionId should be inside the subquery
lotsOfLogData的表结构如下。站点ID(可能是50个)相对较少,但每个站点都有很多记录。我们查询时知道了站号。
create table ##lotsOfLogData (
Id bigint identity(1,1) not null
, StationId int not null
, UtcTime datetime not null
-- 20 other fields, used for other calculations
)
对于给定的参数,fn_GenerateDateSteps 会返回这样的数据集:
[DT]
2012-01-31 05:05:00.000
2012-01-31 05:05:30.000
2012-01-31 05:06:00.000
2012-01-31 05:06:30.000 (and so on, every 30 seconds)
我也用这种方式用临时表做了这个,但是这样做的费用稍贵一点。
declare @dates table ( dt datetime, ClosestId bigint);
insert into @dates (dt) select dt from dbo.fn_GenerateDateSteps(@st, @end, 30)
update @dates set closestId = ( -- same subquery as above )
select * from lotsOfLogData inner join @dates on Id = ClosestId
编辑:已修复
现在可以使用200K +行了。我尝试了两种方式,交叉应用适当的索引(id / time + includes(..所有列......)工作正常。但是,我最终得到了我开始的查询,使用更简单(和现有)关于[id + time]的索引。更容易理解的查询是我为什么选择那个问题的原因。也许还有更好的方法可以做到,但我看不到它:D
-- subtree cost (crossapply) : .0808
-- subtree cost (id based) : .0797
-- see above query for what i ended up with
答案 0 :(得分:1)
只是一些想法...不会真的称之为答案,但它对于评论框来说太大了。
首先,如果你还没有这样做,我会查看查询的执行计划。
更深奥:您是否可以选择将日期表示为原始值(如表示明确定义时间后的秒/分钟整数)?即使我相信SQL Server将日期存储为数字值,但对原语的操作可能会快一些,因为它会消除对DateAdd()
和DateDiff()
的重复调用。
This (fairly old) article给出了SQL Server实际存储日期的示例。也许您可以将日期保留为DATETIME,但可以使用基本数学对其进行操作。
无论数据类型如何,我都会在日期列上尝试聚集索引,因为您的搜索可能会受益于聚簇索引提供的物理排序,尤其是在较窄范围内搜索时。再次,执行计划可能会有启发性。
我还可以看到用于表示数据的星型模式,其日期维度包含日期概括。然后,您可以搜索一般化。即使没有使用概括,实际的日期数也会减少,因为具有相同日期的所有事实都可以指向维度中的相同记录,因此日期只需要评估一次。
最后,SQL性能调优向导(我相信它是在2005年,我知道它是在2008年)建议你的查询?我不建议盲目实施其建议,但我经常在它推荐的事情中找到好的想法。
答案 1 :(得分:1)
你可以尝试
inner join
更改为cross apply
。 where log.stationid
移至子选择。SQL声明
SELECT DISTINCT log.* -- id,
FROM dbo.fn_GenerateDateSteps(@st, @end, 30) AS d
CROSS APPLY (
SELECT TOP 1 log.*
FROM lotsOfLogData AS log -- contains data in 5 second intervals
WHERE -- search for dates in a certain range
utcTime between DATEADD(s, -10, d.dt) AND DATEADD(s, 5, d.dt)
AND log.stationid = 1000
ORDER BY
-- get the 'closest'. this can change a little, but will always
-- be based on a difference between the date
ABS(DATEDIFF(s, d.dt, UtcTime))
) log