这个问题在很大程度上是出于好奇心,因为我确实有一个有效的查询(它只需要比我想要的时间长一点)。
我有一张包含400万行的表格。此表的唯一索引是自动增量BigInt ID。查询正在其中一列中查找不同的值,但仅返回1天。不幸的是,评估的ReportDate列不是DateTime类型,甚至不是BigInt,而是格式为YYYYMMDD的char(8)。所以查询有点慢。
SELECT Category
FROM Reports
where ReportDate = CONVERT(VARCHAR(8), GETDATE(), 112)
GROUP BY Category
请注意,上述声明中的日期转换只是将其转换为YYYYMMDD格式进行比较。
我想知道是否有办法根据我知道我感兴趣的唯一数据位于表的“底部”的事实来优化此查询。我正在考虑某种递归的SELECT函数,它逐渐增加了一个可用于最终查询的临时表。
例如,在psuedo-sql:
中N = 128
TemporaryTable = SELECT TOP {N} *
FROM Reports
ORDER BY ID DESC
/* Once we hit a date < Today, we can stop */
if(TemporaryTable does not contain ReportDate < Today)
N = N**2
Repeat Select
/* We now have a smallish table to do our query */
SELECT Category
FROM TemproaryTable
where ReportDate = CONVERT(VARCHAR(8), GETDATE(), 112)
GROUP BY Category
这有意义吗?有可能吗?
这是在MS SQL Server 2008上。
答案 0 :(得分:4)
我可能会建议您不需要转换以YYYYMMDD格式存储为char数据的Date
;这种格式本身就是可以排序的。我会改为将您的日期转换为该格式的输出。
此外,您编写转换的方式是,它会为每个单独的行转换当前的DateTime,因此即使为整个查询存储该值也可以加快速度...但我认为只需将您要搜索的日期转换为该格式的char即可。
我还建议你得到你需要创建的索引......当然......这不是你问的问题:P
答案 1 :(得分:3)
为什么不创建您需要的索引?
create index idx_Reports_ReportDate
on Reports(ReportDate, Category)
答案 2 :(得分:3)
不,这没有意义。优化此查询的唯一方法是为其设置覆盖索引:
CREATE INDEX ndxReportDateCategory ON Reports (ReportDate, Category);
更新
考虑到您无法修改架构的注释,您应该修改架构。如果你仍然不能,那么答案仍然适用:解决方案是有一个索引。
最后,为了更直接地回答你的问题,如果你在ID和ReportData之间有很强的相关性:你寻找的ID是ReportDate比你所追求的日期小的最大ID:
SELECT MAX(Id)
FROM Reports
WHERE ReportDate < 'YYYYMMDD';
这将对ID索引执行反向扫描,并停止在所需日期之前的第一个ID(即不扫描整个表)。然后,您可以根据此找到的最大ID来过滤您的报告。
答案 3 :(得分:1)
我想你会发现关于SARGability的讨论,在Rob Farley的博客上,你的帖子主题非常有趣。
http://blogs.lobsterpot.com.au/2010/01/22/sargable-functions-in-sql-server/
一种有趣的替代方法,不需要您修改现有的列数据类型,就是利用计算列。
alter table REPORTS
add castAsDate as CAST(ReportDate as date)
create index rf_so2 on REPORTS(castAsDate) include (ReportDate)
答案 4 :(得分:0)
我偶尔用来进入日志表的一种查询模式是用子查询来限制:
DECLARE @ReportDate varchar(8)
SET @ReportDate = Convert(varchar(8), GetDate(), 112)
SELECT *
FROM
(
SELECT top 20000 *
FROM Reports
ORDER BY ID desc
) sub
WHERE sub.ReportDate = @ReportDate
20k / 4M =读取表的0.5%。
这是一个循环解决方案。注意:可能希望在临时表中将ID主键和Reportdate编入索引。
DECLARE @ReportDate varchar(8)
SET @ReportDate = Convert(varchar(8), GetDate(), 112)
DECLARE @CurrentDate varchar(8), MinKey bigint
SELECT top 2000 * INTO #MyTable
FROM Reports ORDER BY ID desc
SELECT @CurrentDate = MIN(ReportDate), @MinKey = MIN(ID)
FROM #MyTable
WHILE @ReportDate <= @CurrentDate
BEGIN
SELECT top 2000 * INTO #MyTable
FROM Reports WHERE ID < @MinKey ORDER BY ID desc
SELECT @CurrentDate = MIN(ReportDate), @MinKey = MIN(ID)
FROM #MyTable
END
SELECT * FROM #MyTable
WHERE ReportDate = @ReportDate
DROP TABLE #MyTable