使用 CROSS APPLY 选择运行缓慢

Eth*_*aul 3 sql-server execution-plan sorting cross-apply top

我正在尝试优化查询以更快地运行。查询如下:

SELECT grp_fk_obj_id, grp_name
FROM tbl_groups as g1
         CROSS APPLY (SELECT TOP 1 grp_id as gid
                      FROM tbl_groups as g2
                      WHERE g1.grp_fk_obj_id = g2.grp_fk_obj_id
                      ORDER BY g2.date_from DESC, ISNULL(date_to, '4000-01-01') DESC) as a
WHERE g1.grp_id = gid
Run Code Online (Sandbox Code Playgroud)

grp_id 是主键。grp_fk_obj_id 是另一个对象的外键。这两列都有索引(我猜它是默认的)。

完成大约需要半秒钟,但我需要它来加快工作速度。我查看了执行计划,它显示“Top N 排序”的成本超过 90%。另外,我注意到,如果我删除了交叉应用中的 where 子句,那么它的运行速度至少要快 5 倍,但我需要以一种或另一种方式使用 where 子句。

您是否认为有可能提高此查询的性能?

编辑:表创建 DDL:

create table tbl_groups
(
    grp_id        bigint identity
        constraint PK_tbl_groups
            primary key,
    grp_fk_obj_id bigint      not null
        constraint FK_grp_fk_obj_id
            references tbl_other,
    grp_name      varchar(30) not null,
    date_from     date        not null,
    date_to       date
)
go

create index IDX_grp_fk_obj_id
    on tbl_groups (grp_fk_obj_id)
go

create index IDX_grp_name
    on tbl_groups (grp_name)
go
Run Code Online (Sandbox Code Playgroud)

Rob*_*ley 5

您需要更好的索引和一些重写。

因为您只使用一个表,所以您可以在子查询中使用 ROW_NUMBER(),然后从每个分区中获取第一行。

SELECT grp_fk_obj_id, grp_name, grp_id
FROM (
    SELECT grp_fk_obj_id, grp_name, grp_id, ROW_NUMBER() OVER (PARTITION BY grp_fk_obj_id ORDER BY date_from DESC, ISNULL(date_to, '4000-01-01') DESC) AS rownum
    FROM tbl_groups
) g
WHERE g.rownum = 1;
Run Code Online (Sandbox Code Playgroud)

而且,修复您的ORDER BY子句,因为它会强制它进行排序。因为您对表达式进行排序,所以无法有效地对其进行索引。如果你可以强制 date_to 有一个值 order by date_from desc, date_to desc,那么索引(grp_fk_obj_id, date_from, date_to) include (grp_name)就会真正有帮助。它甚至会使您的原始查询运行得更快。