SQL table 有许多特定的日期时间。我如何使它更具可扩展性?
SQL table with many specific datetimes. How do I make this more scalable?
我有一个比较麻烦的 table 看起来像这样:
事件时间日志
Id (bigint) | Time (datetime) | LogId (FK to Log tables Id column)
此 table 概述了日志事件发生的时间。这种只写一个唯一的日志事件的方法和这些日志发生的时间可以从这个table中得到。
问题是日期太具体了,这些日期可能有重复的值。例如:
2015-08-03 23:54:58.000 | 1983
2015-08-03 23:54:58.000 | 1934
2015-08-03 23:54:56.000 | 1647
一段时间后查询变得非常困难。通常大约 500k 行左右它开始突突,即使我在 LogId 和时间上放置索引。当我达到 1mill 范围及以上时,查询速度变慢了......
我确实需要这些特定时间,因此无法选择在一小时或一天的开始前进行汇总。我也怀疑为重复时间值实施计数列是否会有很大帮助,因为这些日期非常精细。该指数仍将不得不跳过所有那些非常缓慢的特定日期。
我不确定如何使这个 table 更具可扩展性。也许将其分解为每月 tables?
根据要求,这里是开始使用的查询
SELECT b.User, b.Ip, b.AccountId, a.Time FROM
EventTimeLog a
inner join [Log] b on a.LogId = b.Id
WHERE
b.UserId = '<param>' AND
a.Time >= '<param>' AND
a.Time <= '<pamam>'
如果时差 > 2 天,它就会像疯了似的发出声音。是的,我在 Log 上有 UserId 的索引。
您的 table 中有很多次只是略有不同。这会使索引变得庞大而无用。
因此请考虑计算列而不是给您一个不那么精确的时间。然后为logid+这个计算列创建索引
不知道你平时查询的时间跨度是多少。让我们以小时为例。您会将日期截断为小时(例如 dateadd(hour, datediff(hour, 0, time), 0)
或作为字符串:substring(convert(varchar(25), time, 120), 1, 13)
):
新增栏目(我这里用的是字符串):
alter table mytable add comp_hour as substring(convert(varchar(25), time, 120), 1, 13);
所以你会得到例如:
time comp_hour
2015-09-03 14:12:10.2158145 '2015-09-03 14'
2015-09-03 14:45:27.4457813 '2015-09-03 14'
指数:
create index index_comp_hour on mytable(logid, comp_hour);
查询:
select l.user, l.ip, l.accountid, e.time
from log l
join eventtimelog e on e.logid = l.id and e.comp_hour in ('2015-09-03 13', '2015-09-03 14')
where l.userid = 123;
(不过我不确定索引 mytable(logid, comp_hour)
或 mytable(comp_hour, logid)
会更好,或者它是否重要。您可以同时创建两者,然后查看执行计划并删除没有使用的那个。)
我有一个比较麻烦的 table 看起来像这样:
事件时间日志
Id (bigint) | Time (datetime) | LogId (FK to Log tables Id column)
此 table 概述了日志事件发生的时间。这种只写一个唯一的日志事件的方法和这些日志发生的时间可以从这个table中得到。
问题是日期太具体了,这些日期可能有重复的值。例如:
2015-08-03 23:54:58.000 | 1983
2015-08-03 23:54:58.000 | 1934
2015-08-03 23:54:56.000 | 1647
一段时间后查询变得非常困难。通常大约 500k 行左右它开始突突,即使我在 LogId 和时间上放置索引。当我达到 1mill 范围及以上时,查询速度变慢了......
我确实需要这些特定时间,因此无法选择在一小时或一天的开始前进行汇总。我也怀疑为重复时间值实施计数列是否会有很大帮助,因为这些日期非常精细。该指数仍将不得不跳过所有那些非常缓慢的特定日期。
我不确定如何使这个 table 更具可扩展性。也许将其分解为每月 tables?
根据要求,这里是开始使用的查询
SELECT b.User, b.Ip, b.AccountId, a.Time FROM
EventTimeLog a
inner join [Log] b on a.LogId = b.Id
WHERE
b.UserId = '<param>' AND
a.Time >= '<param>' AND
a.Time <= '<pamam>'
如果时差 > 2 天,它就会像疯了似的发出声音。是的,我在 Log 上有 UserId 的索引。
您的 table 中有很多次只是略有不同。这会使索引变得庞大而无用。
因此请考虑计算列而不是给您一个不那么精确的时间。然后为logid+这个计算列创建索引
不知道你平时查询的时间跨度是多少。让我们以小时为例。您会将日期截断为小时(例如 dateadd(hour, datediff(hour, 0, time), 0)
或作为字符串:substring(convert(varchar(25), time, 120), 1, 13)
):
新增栏目(我这里用的是字符串):
alter table mytable add comp_hour as substring(convert(varchar(25), time, 120), 1, 13);
所以你会得到例如:
time comp_hour 2015-09-03 14:12:10.2158145 '2015-09-03 14' 2015-09-03 14:45:27.4457813 '2015-09-03 14'
指数:
create index index_comp_hour on mytable(logid, comp_hour);
查询:
select l.user, l.ip, l.accountid, e.time
from log l
join eventtimelog e on e.logid = l.id and e.comp_hour in ('2015-09-03 13', '2015-09-03 14')
where l.userid = 123;
(不过我不确定索引 mytable(logid, comp_hour)
或 mytable(comp_hour, logid)
会更好,或者它是否重要。您可以同时创建两者,然后查看执行计划并删除没有使用的那个。)