SQL MAX()函数很慢,有没有更好的方法?

时间:2013-02-27 14:28:30

标签: asp.net sql sql-server max min

我有一个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列。

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

提前致谢...

5 个答案:

答案 0 :(得分:2)

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

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

e.g。

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,那么它们可能不是最合适的数据类型吗?

答案 1 :(得分:1)

您需要做几件事:

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

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

要采取的最后一项是CONVERT(FLOAT,ISNULL(Runhrs, Runho)):如果前两次修改后的查询速度仍然不足,请将MAX(CONVERT(FLOAT,ISNULL(Runhrs,Runho)))更改为CONVERT(FLOAT, MAX(ISNULL(Runhrs,Runho))),并对{{进行相同的更改1}}。这可能有效,也可能无效,具体取决于MINRunhrs的类型。

答案 2 :(得分: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)

答案 3 :(得分: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

答案 4 :(得分:0)

AND CONVERT(VARCHAR,TIME_STAMP,102)

这不是这样做的方法 - 它会导致缓慢。不要对DATA应用不必要的函数 - 它会使优化器无法使用此字段上的索引。反之亦然,将@STARTDATE + @ENDDATE转换为与字段相同的数据类型。 (或者,按照这种方式考虑,你正在做5,000,000次转换,以便你可以比较2个变量)

[time_stamp] BETWEEN @STARTDATE AND @ENDDATE

请理解这实际上意味着:

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

即。 BOTH&#34;边界&#34;日期时间包括在内,通常它更容易,更准确,以避免包容性,并拼写出来:

[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)等,然后进行减法。

相关问题