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

问题描述 投票:0回答:4

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

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

提前致谢...

asp.net sql sql-server max min
4个回答
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,那么它们难道不是最合适的数据类型吗?


1
投票

您需要做几件事:

  • 确保您搜索的列已建立索引 - 您需要在
    ID_LOCATION
    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
的类型。


0
投票

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

其次,您可以通过在

hl_logs(id_location, time_stamp)
上创建索引来加快速度。您也可以抛出
Runhrs
Runho
。通过对所有四列(按该顺序)建立索引,查询甚至不需要转到原始数据。

要使用索引,您需要将

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)

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
© www.soinside.com 2019 - 2024. All rights reserved.