在SQL窗口函数中设置的有效极限结果

时间:2019-01-19 10:16:34

标签: sql-server indexing window-functions

我的问题最好作为对Limit result set in sql window function的评论,但我没有必要的声誉来发表评论。

给出一个移动的车辆位置表,对于每个车辆,我希望找到最近记录的位置(以及当时有关该车辆的其他数据)。根据另一个问题的答案,我可以运行以下查询:

表定义:

CREATE TABLE VehiclePositions
(
    Id BIGINT NOT NULL,
    VehicleID  NVARCHAR(12) NULL,
    Timestamp DATETIME NULL,
    PositionX FLOAT NULL,
    PositionY FLOAT NULL,
    PositionZ SMALLINT NULL,
    Speed SMALLINT NULL,
    Heading SMALLINT NULL 
)

查询:

select *
from 
    (select 
         *,
         row_number() over (partition by VehicleID order by Timestamp desc) as ranking
     from VehiclePositions) as x
where 
    ranking = 1

现在,问题在于这会进行全表扫描。我认为通过创建适当的索引,可以避免这种情况:

CREATE INDEX idx_VehicPosition ON VehiclePositions(VehicleID, Timestamp);

但是,SQL Server将愉快地忽略查询中的该索引,并仍然执行稳定的扫描。

注意:我可以让SQL Server使用索引,但是代码相当难看:

DECLARE @ids TABLE (id NVARCHAR(12) UNIQUE)

INSERT INTO @ids 
    SELECT DISTINCT VehicleID 
    FROM VehiclePositions

SELECT ep.* 
FROM VehiclePositions vp
WHERE Timestamp = (SELECT Max(TimeStamp) FROM VehiclePositions vp2 
                   WHERE vp2.VehicleID = vp.VehicleID)
  AND VehicleID IN (SELECT DISTINCT id FROM @ids)

VehicleID IN...是因为SQL Server似乎没有实现查找跳过优化。它仍然提出了一个非常不理想的查询计划,该计划两次访问索引,但至少没有这样做在线性时间内执行。

有没有一种方法可以使SQL Server智能地运行窗口函数查询?

我正在使用SQL Server 2014 ...

我们将不胜感激

3 个答案:

答案 0 :(得分:0)

我该怎么做:

  SELECT *
  FROM
     (SELECT  MAX(Timestamp) as maxtime,
             VehicleID 
     FROM VehiclePositions
     GROUP BY VehicleID ) as maxed INNER JOIN 
    (SELECT  Id ,
             VehicleID ,
             Timestamp ,
             PositionX ,
             PositionY,
             PositionZ,
             Speed ,
             Heading 
     FROM VehiclePositions) as vals
       ON maxed.maxtime = vals.Timestamp
      AND maxed.VehicleID = vals.VehicleID

据我所知,您无法绕过索引扫描两次。

答案 1 :(得分:0)

只要您从表格中选择所有车辆并选择所有列(或至少选择不在索引中的列),我希望表格扫描会不断弹出。

在许多情况下,这实际上是最有效的查询计划。仅当每辆车有很多行(如几页)时,搜索策略才可能更快。

如果每辆车确实有很多行,则可以考虑在Timestamp上对表进行分区...

答案 2 :(得分:0)

您可以使用“ qualify”在Windows函数中过滤结果,如下所示:

select *
from VehiclePositions
qualify row_number() over (partition by VehicleID order by Timestamp desc) = 1