SQL 使用表索引优化服务器查询

SQL Server query optimization using indexes on tables

我在 SQL 服务器函数中有这个查询,它需要大约 3 秒来执行单个值。当记录数较多时,函数调用时间累加。

我正在展示表的结构和现有的索引。只是检查建议是否有任何我可以添加的索引以获得更好的查询性能

例如 - 对于 Table B 在 PId 列上与 Table U 连接。 当前索引 [IX_TableB_2]PRId 上,它在 WHERE 子句中用于 b.PRID <> @PRID.

的条件

对于这个特定查询,这些表是否有更好的索引建议?

谢谢

--Query
SELECT 
    ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC), b.BId, COUNT(*) 
FROM 
    TableB b
INNER JOIN 
    TableU u ON u.PID = b.PID
LEFT JOIN 
    TableP p ON p.BName = b.BName
WHERE 
    LEFT(b.Code, @CheckLen) = LEFT(@Code, @CheckLen)
    AND b.PRID <> @PRID                 
    AND ISNULL(p.DSID, @DSID) = @DSID   
GROUP BY 
    b.BId

Table 行数和结构

--Existing Indexes on the TableB
CREATE CLUSTERED INDEX [IX_TableB_1] 
ON [TableB] ([Code] ASC)

CREATE NONCLUSTERED INDEX [IX_TableB_2] 
ON [TableB] ([PRID] ASC)
INCLUDE([Code], [BId], [BName], [PID]) 

--Existing Indexes on TableU

ALTER TABLE TableU 
    ADD CONSTRAINT [PK_TableU] 
        PRIMARY KEY CLUSTERED ([PID] ASC)

--Existing Indexes on TableP
ALTER TABLE TableP 
    ADD CONSTRAINT [PK_TableP] 
        PRIMARY KEY CLUSTERED (BName ASC, DSID ASC)

您说 LEFT(b.Code, @CheckLen) 上的筛选器比筛选器 <> @PRID 对来自 TableB 的行的限制更多。所以最好只依赖 Code 上的聚簇索引。要命中索引,您需要更改 WHERE 子句

b.Code LIKE LEFT(@Code, @CheckLen) + '%'

此外,TableP 上的 left-join 然后在 WHERE 中使用 ISNULL 过滤。鉴于 DSID 不可为空,逻辑可以翻转为 NOT EXISTS。该索引目前是正确的:首先是 BName 上的相等性,然后是 DSID.

上的不等式
NOT EXISTS (SELECT 1
    FROM TableP p
    WHERE p.BName = b.BName
      AND p.DSID <> @DSID
)

TableU 上的查找已经在使用聚集索引,没有其他列,因此无法在那里执行任何操作。

BId 上的分组在这里无能为力。因为 Code 上的过滤器是一个范围查找,所以不能使用索引键中后面的任何列。但这应该没什么大不了的,因为在所有连接之后分组之前你似乎只有 13 行,而且只有一个 BId 值,所以性能应该没问题。

ROW_NUMBER 会给您带来麻烦,因为它需要对 COUNT(*) 进行额外的排序。如果有任何方法可以删除它,我建议您这样做。

把这些放在一起

SELECT 
    ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC), b.BId, COUNT(*) 
FROM
    TableB b
INNER JOIN
    TableU u ON u.PID = b.PID
WHERE 
    b.Code LIKE LEFT(@Code, @CheckLen) + '%'
    AND b.PRID <> @PRID
    AND NOT EXISTS (SELECT 1
        FROM TableP p
        WHERE p.BName = b.BName
          AND p.DSID <> @DSID
    )
GROUP BY 
    b.BId;

虽然您现在可以删除 TableB 上的 non-clustered 索引,但您无需更改索引。


或者,可能是 left-join 的逻辑一开始就错了,你真正需要的只是将条件移动到 ON

SELECT 
    ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC), b.BId, COUNT(*) 
FROM
    TableB b
INNER JOIN
    TableU u ON u.PID = b.PID
LEFT JOIN
    TableP p ON p.BName = b.BName
            AND p.DSID = @DSID
WHERE 
    b.Code LIKE LEFT(@Code, @CheckLen) + '%'
    AND b.PRID <> @PRID
GROUP BY 
    b.BId;

请注意,此版本有不同的结果:它不会排除其他表中的任何行。

索引应该相同。