性能优化 Row_Number() 通过分区:

发布于 2025-01-09 01:19:40 字数 2032 浏览 4 评论 0原文

我一直在尝试优化表(Table1)和子查询之间存在左连接的存储过程的性能。当部门表中的记录增加时,速度会变慢;我认为,这是由于 ROW_NUMBER 函数造成的。它适用于部门表中记录数量较少的情况。 Table1 和存储过程末尾的子查询之间有一个左联接。

下面是示例表 Table1EmployeeDepartment,涉及运行缓慢的查询。

CREATE TABLE [Table1]
(
    [ID] [int] NOT NULL PRIMARY KEY, 
    [Name] VARCHAR(250) NOT NULL,
    [DepartmentID] int 
) ON [PRIMARY] 

INSERT [Table1] ([ID], [Name], [DepartmentID])  
VALUES (1, N'A', 1), (2, N'D', 2),  
       (3, N'C', 3), (4, N'E', 4),
       (5, N'D', 5), (6, N'A', 6),
       (7, N'B', 7)  
GO 

CREATE TABLE [Department]
(
    [DepartmentID] [int] NOT NULL PRIMARY KEY, 
    [Name] VARCHAR(250) NOT NULL, 
) ON [PRIMARY] 

INSERT [Department] ([DepartmentID], [Name])  
VALUES (1, N'Engineering'), (2, N'Administration'),  
       (3, N'Sales'), (4, N'Marketing'), 
       (5, N'Finance') 
GO 

CREATE TABLE [Employee]
( 
    [EmployeeID] [int] NOT NULL PRIMARY KEY, 
    [FirstName] VARCHAR(250) NOT NULL, 
    [LastName] VARCHAR(250) NOT NULL, 
    [DepartmentID] [int] NOT NULL 
         REFERENCES [Department](DepartmentID), 
) ON [PRIMARY] 
GO

INSERT [Employee] ([EmployeeID], [FirstName], [LastName], [DepartmentID]) 
VALUES (1, N'Orlando', N'Gee', 1), (2, N'Keith', N'Harris', 2), 
       (3, N'Donna', N'Carreras', 3), (4, N'Janet', N'Gates', 3) 

运行缓慢的示例查询。

SELECT 
    *
FROM
    Table1 AS t
LEFT JOIN 
    (SELECT 
         D.Name,
         E.DepartmentID,
         ROW_NUMBER() OVER (PARTITION BY E.DepartmentID
                            ORDER BY D.DepartmentID ASC,
                                     CASE WHEN D.Name IS NULL THEN 1 ELSE 0 END ASC, D.Name ASC) AS ord_index
     FROM 
         Department AS D
     INNER JOIN 
         Employee AS E ON D.DepartmentID = E.DepartmentID) AS x ON x.DepartmentID = t.DepartmentID
WHERE 
    x.ord_index = 1 OR x.ord_index IS NULL

我尝试根据调整顾问的建议添加索引,但它并没有提高性能。我也尝试使用 APPLY,但由于 ROW_NUMBER 函数而无法弄清楚。不过,我一直在尝试浏览平台上的相关标题。

我很感谢任何优化此类查询的指南。

I have been trying to optimize performance of a stored procedure that has left join between a table (Table1) and a subquery. It gets slow when the records in the department table increase; I think, it is due to the ROW_NUMBER function. It works fine with lower number of records in the Department table. There is a left join between Table1 and a subquery at the end of the stored procedure.

Below is sample tables Table1, Employee and Department, involved in the query that is running slow.

CREATE TABLE [Table1]
(
    [ID] [int] NOT NULL PRIMARY KEY, 
    [Name] VARCHAR(250) NOT NULL,
    [DepartmentID] int 
) ON [PRIMARY] 

INSERT [Table1] ([ID], [Name], [DepartmentID])  
VALUES (1, N'A', 1), (2, N'D', 2),  
       (3, N'C', 3), (4, N'E', 4),
       (5, N'D', 5), (6, N'A', 6),
       (7, N'B', 7)  
GO 

CREATE TABLE [Department]
(
    [DepartmentID] [int] NOT NULL PRIMARY KEY, 
    [Name] VARCHAR(250) NOT NULL, 
) ON [PRIMARY] 

INSERT [Department] ([DepartmentID], [Name])  
VALUES (1, N'Engineering'), (2, N'Administration'),  
       (3, N'Sales'), (4, N'Marketing'), 
       (5, N'Finance') 
GO 

CREATE TABLE [Employee]
( 
    [EmployeeID] [int] NOT NULL PRIMARY KEY, 
    [FirstName] VARCHAR(250) NOT NULL, 
    [LastName] VARCHAR(250) NOT NULL, 
    [DepartmentID] [int] NOT NULL 
         REFERENCES [Department](DepartmentID), 
) ON [PRIMARY] 
GO

INSERT [Employee] ([EmployeeID], [FirstName], [LastName], [DepartmentID]) 
VALUES (1, N'Orlando', N'Gee', 1), (2, N'Keith', N'Harris', 2), 
       (3, N'Donna', N'Carreras', 3), (4, N'Janet', N'Gates', 3) 

Sample query that is running slow.

SELECT 
    *
FROM
    Table1 AS t
LEFT JOIN 
    (SELECT 
         D.Name,
         E.DepartmentID,
         ROW_NUMBER() OVER (PARTITION BY E.DepartmentID
                            ORDER BY D.DepartmentID ASC,
                                     CASE WHEN D.Name IS NULL THEN 1 ELSE 0 END ASC, D.Name ASC) AS ord_index
     FROM 
         Department AS D
     INNER JOIN 
         Employee AS E ON D.DepartmentID = E.DepartmentID) AS x ON x.DepartmentID = t.DepartmentID
WHERE 
    x.ord_index = 1 OR x.ord_index IS NULL

I tried adding indexes per the suggestion of tuning advisor, but it doesn't improve the performance. I was also trying to use APPLY, but couldn't get it clear due to the ROW_NUMBER function. I have been trying to go through the related titles on the platform though.

I appreciate any guide in optimizing such query.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

○愚か者の日 2025-01-16 01:19:40

您可以重构查询以使用外部应用,这可能会产生更好的执行计划(取决于支持的索引),例如:

select * from Table1 t 
outer apply (
    select D.Name,
        Row_Number() over (partition by E.DepartmentID order by D.DepartmentID asc, 
        case when D.Name is null then 1 
        else 0 end asc, D.Name asc) as ord_index 
    from Department D 
    join Employee E on D.DepartmentID = E.DepartmentID
    where E.DepartmentID = t.DepartmentID 
) x 
where x.ord_index = 1 or x.ord_index is null

请参阅DB<>Fiddle 显示更好的计划

You could refactor the query to use an outer apply which may result in a better execution plan (depending on the supporting indexes) such as :

select * from Table1 t 
outer apply (
    select D.Name,
        Row_Number() over (partition by E.DepartmentID order by D.DepartmentID asc, 
        case when D.Name is null then 1 
        else 0 end asc, D.Name asc) as ord_index 
    from Department D 
    join Employee E on D.DepartmentID = E.DepartmentID
    where E.DepartmentID = t.DepartmentID 
) x 
where x.ord_index = 1 or x.ord_index is null

See DB<>Fiddle showing better plan

Spring初心 2025-01-16 01:19:40

在查询中,难以索引的部分是CASE WHEN D.Name IS NULL THEN 1 ELSE 0 END。但是,如果您愿意在 Department 表中添加额外的生成列,则可以对其建立索引。例如:

alter table Department add name_flag as 
CASE WHEN D.Name IS NULL THEN 1 ELSE 0 END;

create index ix1 on Department (DepartmentID, name_flag, name);

有了新列和索引,查询可以改写为:

SELECT * 
FROM Table1 t 
LEFT JOIN (
  SELECT D.Name, D.DepartmentID,  
    ROW_NUMBER() OVER (
      PARTITION BY D.DepartmentID  ORDER BY name_flag, D.Name
    ) AS ord_index
  FROM Department D 
  INNER JOIN Employee E ON D.DepartmentID = E.DepartmentID
) x ON x.DepartmentID = t.DepartmentID and x.ord_index = 1 

In the query, the section that is difficult to index is CASE WHEN D.Name IS NULL THEN 1 ELSE 0 END. This can, however, be indexed if you are willing to add an extra generated column in the Department table. For example:

alter table Department add name_flag as 
CASE WHEN D.Name IS NULL THEN 1 ELSE 0 END;

create index ix1 on Department (DepartmentID, name_flag, name);

With the the new column in place and the index, the query can be rephrased as:

SELECT * 
FROM Table1 t 
LEFT JOIN (
  SELECT D.Name, D.DepartmentID,  
    ROW_NUMBER() OVER (
      PARTITION BY D.DepartmentID  ORDER BY name_flag, D.Name
    ) AS ord_index
  FROM Department D 
  INNER JOIN Employee E ON D.DepartmentID = E.DepartmentID
) x ON x.DepartmentID = t.DepartmentID and x.ord_index = 1 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文