3

我有一个 SQL 数据库和 ASP.NET Web 应用程序,我的大部分查询都涉及 SQL max 函数。

例如,下面的查询需要大约。在 ASP.NET 网站和 SSMS 上执行(使用分析器时)36 秒。

SELECT MAX(CONVERT(FLOAT,ISNULL(Runhrs,Runho))) - 
       MIN(CONVERT(FLOAT,ISNULL(Runhrs,Runho))) AS ACTUALHOURSRUN 
FROM REPORTINGSYSTEM.DBO.HL_LOGS 
WHERE ID_LOCATION = @ID_LOCATION AND 
       CONVERT(VARCHAR,TIME_STAMP,102) 
       BETWEEN @STARTDATE AND @ENDDATE

有问题的表有大约。5,000,000 条记录和 45 列。

执行查询以减少执行时间的最佳/最快/最有效方式是什么?

提前致谢...

4

5 回答 5

2

ID_LOCATION 和 TIME_STAMP 上的索引将是一个不错的选择。

但是,当您将 TIME_STAMP 字段转换为 VARCHAR 时,这将阻止它使用索引。相反,重新编写查询以消除执行 CONVERT 的需要,而是使用 DATETIME 值。

例如

SELECT MAX(CONVERT(FLOAT,ISNULL(Runhrs,Runho))) - MIN(CONVERT(FLOAT,ISNULL(Runhrs,Runho))) AS ACTUALHOURSRUN 
FROM REPORTINGSYSTEM.DBO.HL_LOGS 
WHERE ID_LOCATION = @ID_LOCATION AND TIME_STAMP >= @STARTDATE AND TIME_STAMP < @ENDDATE

确保 @STARTDATE 和 @ENDDATE 也是 DATETIME 参数(我假设这是 TIME_STAMP 列的数据类型)

我还想问 Runhrs/Runho 列是什么数据类型?如果还没有存储为 FLOAT/DECIMAL,那么它们会不会是最合适的数据类型?

于 2013-02-27T14:34:39.010 回答
1

您需要做几件事:

  • 确保您搜索的列已编入索引- 您需要在ID_LOCATIONand上建立索引TIME_STAMP(如果您有其他查询跨位置按日期或跨日期按位置进行查询,您可以考虑定义单独的索引;否则,单个组合索引将起作用)。
  • 停止将时间戳转换为字符串- 在查询中使用本机数据类型@STARTDATE@ENDDATE或将条件替换为TIME_STAMP between CONVERT(datetime,@STARTDATE) and CONVERT(datetime,@ENDDATE)

这两个更改应该使您的查询更快,尤其是第二个更改:当前,CONVERT(VARCHAR,TIME_STAMP,102)强制查询优化器对与您的位置匹配的所有内容进行全面扫描,如果没有索引,则甚至进行全表扫描ID_LOCATION。索引搜索应将记录数降低到可接受的水平。

最后要承担的一项是CONVERT(FLOAT,ISNULL(Runhrs, Runho)):如果前两次修改后的查询速度仍然不够,则更MAX(CONVERT(FLOAT,ISNULL(Runhrs,Runho)))改为CONVERT(FLOAT, MAX(ISNULL(Runhrs,Runho))),并对 进行相同的更改MIN。这可能会或可能不会起作用,具体取决于 和 的Runhrs类型Runho

于 2013-02-27T14:38:27.180 回答
0

首先,将 Rhnhrs 存储为数字类型。那么你不需要进行转换。

其次,您将通过在hl_logs(id_location, time_stamp). 你也可以扔RunhrsRunho以及。使用所有四列上的索引(按此顺序),查询甚至不需要转到原始数据。

要使用索引,您需要将where语句更改为:

 time_stamp bewteen @startTimeStamp and @EndTimeStamp

如果变量是函数的参数,SQL 引擎将不会使用索引。

生成的查询应该更像:

select max(coalesce(runhrs, runho)) - min(coalesce(runhrs, runho) as Actual
from REPORTINGSYSTEM.DBO.HL_LOGS 
WHERE ID_LOCATION = @ID_LOCATION AND 
      TIME_STAMP BETWEEN cast(@STARTDATE as datetime) AND cast(@ENDDATE as datetime)
于 2013-02-27T14:36:49.947 回答
0
SELECT CONVERT(FLOAT, MAX(Runhrs)), CONVERT(FLOAT, MAX(Runho),
       CONVERT(FLOAT, MIN(Runhrs)), CONVERT(FLOAT, MIN(Runho)
FROM REPORTINGSYSTEM.DBO.HL_LOGS 
WHERE ID_LOCATION = @ID_LOCATION
  AND TIME_STAMP BETWEEN @STARTDATE AND @ENDDATE

在调用 SQL 的代码中自己做减法。(那是 .net 代码。)

你应该有一个关于 ID_LOCATION、TIME_STAMP、Runhrs、Runho 的索引。一个包含所有四个字段的索引。也许两个索引。

CREATE INDEX REPORTINGSYSTEM.DBO.HL_LOGS ON ID_LOCATION, TIME_STAMP, Runhrs
CREATE INDEX REPORTINGSYSTEM.DBO.HL_LOGS ON ID_LOCATION, TIME_STAMP, Runho
于 2013-02-27T14:37:52.347 回答
0
AND CONVERT(VARCHAR,TIME_STAMP,102)

这不是这样做的方法 - 它会导致缓慢。不要对 DATA 应用不必要的函数 - 它会剥夺优化器在该字段上使用索引的任何机会。反过来,将@STARTDATE+@ENDDATE转换为与 field 相同的数据类型。(或者,这样想你正在做 ~ 5,000,000 次转换,以便你可以比较 2 个变量)

[time_stamp] BETWEEN @STARTDATE AND @ENDDATE

请理解这实际上意味着:

[time_stamp] > = @STARTDATE AND [time_stamp] <= @ENDDATE

即包含两个“边界”日期时间,通常更容易和更准确地避免之间的包容性并将其拼写如下:

[time_stamp] > = @STARTDATE AND [time_stamp] < @ENDDATE (with @enddate
being "next day at 00:00:00:000)

MAX(CONVERT(FLOAT,ISNULL(Runhrs,Runho))) -
MIN(CONVERT(FLOAT,ISNULL(Runhrs,Runho))) AS ACTUALHOURSRUN

哇,这是很多函数调用!这里的问题不是纯粹MAX()/MIN()的。

建议你简单地寻找Max(Runhrs)前面提到的等,然后做减法。

于 2013-02-28T11:40:07.437 回答