我有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)
答案 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
这个行为就像一个游标,但我觉得这个速度要快得多。