使用 WHERE PK IN (...) 进行缓慢查询

Slow query with WHERE PK IN (...)

我有一个非常简单但很大的 table 像这样:

CREATE TABLE tblMulti (
 pk1 bigint,
 pk2 bigint
)

其中PK是pk1-pk2的组成(按此顺序)。

然后,我有一个大的table像这样:

CREATE TABLE tbl (
 ID bigint,
 field1 int,
 ... (other fields)
)

ID是table的唯一PK。

我需要做以下查询:

SELECT  ID, COUNT(*) OVER () as TotalCount
FROM    tbl
WHERE   ID IN (
    SELECT  pk1
    FROM    tblMulti 
    WHERE   pk2 = 101)
ORDER BY  field1 DESC, ID DESC
OFFSET @start ROWS FETCH NEXT @count ROWS ONLY;

问题是过滤器 "pk2=101" 导致大量行(整个 tblMulti table 的 94%),因此 SQL 服务器决定执行索引扫描而不是索引查找。

如何提高此查询的性能?

非常感谢

cghersi

以下是我要优化的内容:

  1. pk2 列上创建 NON CLUSTERED 索引。
  2. field1 列上创建 NON CLUSTERED 索引。
  3. Rebuild/Reorganize 索引和统计数据。
  4. 使用 JOIN 而不是 IN。
  5. 使用 OPTIMIZE FOR 查询提示。

所以,试试这个:

DECLARE @C INT = 101;
SELECT ID, COUNT(*) OVER () as TotalCount
FROM tbl
INNER JOIN tblMulti
    ON tbl.ID = tblMulti.pk1        
WHERE pk2 = @C
ORDER BY  field1 DESC, ID DESC
OFFSET @start ROWS FETCH NEXT @count ROWS ONLY
OPTION (OPTIMIZE FOR (@C = 101));

试试这样的……

SELECT  t1.ID, COUNT(*) OVER () as TotalCount
FROM    tbl t1
WHERE EXISTS (SELECT  1
              FROM    tblMulti 
              WHERE   t1.ID = pk1
                AND   pk2 = 101)
ORDER BY  t1.field1 DESC, t1.ID DESC
OFFSET @start ROWS FETCH NEXT @count ROWS ONLY;

这看起来很慢的原因是因为您在 pk2 字段上没有索引,但您使用它来将结果限制为 'just one type' (101) ( **).

=> 假设您在这里只需要 table 进行查询,您可以简单地颠倒 tblMulti 的主键的顺序,它(在功能上)将是完全相同的东西。

=> 假设您需要具有所述字段顺序的 pk 用于其他内容,您可以在 table 上创建一个额外的唯一索引(或约束),字段顺序相反。

**:是的,我知道你在索引中 pk2,但这对 MSSQL 和我告诉你找到 phone-名字以"mith, John"结尾的人的数量,将phone-书编入姓氏索引对您也没有太大帮助。

此外,由于您知道组合 pk1pk2 是唯一的,因此您可以使用 JOIN 而不是 WHERE EXISTS()

SELECT  t1.ID, 
        COUNT(*) OVER () as TotalCount
FROM    tbl t1
JOIN    tblMulti tm
  ON    tm.pk1 = t1.ID
 AND    tm.pk2 = 101
ORDER BY t1.field1 DESC, 
         t1.ID DESC
OFFSET @start ROWS FETCH NEXT @count ROWS ONLY;

话虽这么说,但我对它的效果感到有点惊讶。您在不属于输出的字段上执行 ORDER BY。我以某种方式猜测 MSSQL 忽略它并简单地使用 t1.ID ?!?

下面的查询使用起来不是更直接吗?

SELECT  t1.ID, 
        COUNT(*) as TotalCount
FROM    tbl t1
JOIN    tblMulti tm
  ON    tm.pk1 = t1.ID
 AND    tm.pk2 = 101
GROUP BY t1.ID
ORDER BY t1.ID DESC
OFFSET @start ROWS FETCH NEXT @count ROWS ONLY;