简单的数据库查询 - 没有游标有更快的方法吗?

时间:2010-11-05 20:20:05

标签: sql sql-server

我有2个表,我正在尝试查询。第一个有一个米的列表。第二个,有那些米的数据。我想获得每米的最新读数。

最初,这是一个group by语句,但它最终处理了我们数据库中的所有700万行,并且花了一点多时间。子查询和其他一些编写方式也有相同的结果。

我有一个聚簇索引,它涵盖了MeterRecordings表中的EndTime和MeterDataConfigurationId列。

最终,这就是我写的,它在大约20毫秒内完成。看起来SQL应该足够聪明,可以在同一时间执行“分组依据”查询。

Declare @Meters Table
(
    MeterId Integer,
    LastValue float,
    LastTimestamp DateTime
)

Declare MeterCursor Cursor For
Select Id
From MeterDataConfiguration

Declare @MeterId Int

Open MeterCursor
Fetch Next From MeterCursor Into @MeterId

While @@FETCH_STATUS =  0
Begin
    Declare @LastValue int
    Declare @LastTimestamp DateTime

    Select @LastValue = mr.DataValue, @LastTimestamp = mr.EndTime
    From MeterRecording mr
    Where mr.MeterDataConfigurationId = @MeterId
        And mr.EndTime = (Select MAX(EndTime) from MeterRecording mr2 Where mr2.MeterDataConfigurationId = @MeterId)

    Insert Into @Meters
    Select @MeterId, @LastValue, @LastTimestamp

    Fetch Next From MeterCursor Into @MeterId   
End

Deallocate MeterCursor

Select *
From @Meters

以下是执行可怕的同一查询的示例:

select mdc.id, mr.EndTime
from MeterDataConfiguration mdc
inner join MeterRecording mr on
    mr.MeterDataConfigurationId = mdc.Id
    and mr.EndTime = (select MAX(EndTime) from MeterRecording mr2 where MeterDataConfigurationId = mdc.Id)

3 个答案:

答案 0 :(得分:3)

您可以使用ROW_NUMBER尝试CTE(公用表表达式):

;WITH Readings AS
(
    SELECT 
       mdc.id, mr.EndTime, 
       ROW_NUMBER() OVER(PARTIION BY mdc.id ORDER BY mr.EndTime DESC) AS 'RowID'
    FROM dbo.MeterDataConfiguration mdc
    INNER JOIN dbo.MeterRecording mr ON mr.MeterDataConfigurationId = mdc.Id
)
SELECT 
   ID, EndTime, RowID
FROM
   Readings
WHERE
   RowID = 1

这会创建数据的“分区”,每个mdc.id一个,并按顺序编号,在mr.EndTime下降,因此对于每个分区,您获得的最新读数为{{1} }行。

当然,要获得不错的表现,您需要适当的指数:

  • RowID = 1因为它是MeterDataConfiguration的外键,对吧?
  • mr.MeterDataConfigurationId,因为你在其上mr.EndTime
  • 我认为
  • ORDER BY是主键,所以它已被索引

更新:抱歉,我错过了这个小故事:

  

我有一个涵盖的聚集索引   EndTime和   MeterDataConfigurationId列   MeterRecordings表。

老实说:我会抛弃那个。您是否在mdc.Id表上有一些适合作为聚类索引的其他唯一ID? INT IDENTITY ID或其他东西??

如果您在MeterRecordings上有复合索引,则无法将其用于这两个目的 - 在(EndTime, MeterDataConfigurationId)上排序,并加入{{1 - 其中一个不可行 - 可惜!

答案 1 :(得分:0)

此查询如何执行?这个获取MeterRecording中的所有数据,忽略MeterDataConfiguration中的列表。如果这样做不安全,可以将其连接到此查询以限制输出。

SELECT Id, DataValue, EndTime
FROM (
select mr.MeterDataConfigurationId as Id,
       mr.DataValue
       mr.EndTime, 
       RANK() OVER(PARTITION BY mr.MeterDataConfigurationId 
                   ORDER BY mr.EndTime DESC) as r
from MeterRecording mr) as M
WHERE M.r = 1

答案 2 :(得分:0)

我会选择marc的答案,但是如果你需要再次使用Cursors(你应该试着避免它们)并且你需要处理很多记录,我建议你创建一个临时表(或表变量) )具有您正在读取的表中的所有列以及自动生成的标识字段(IDENTITY(1,1)),然后只需使用while循环从表中读取。基本上,在循环内增加一个int变量(称为@id)并执行

选择  @ col1Value = column1,  @ col2Value = column2,... 来自@temp_table 其中id = @id

这个行为就像一个游标,但我觉得这个速度要快得多。