索引引用的表列

And*_*sch 3 sql-server index-tuning string-searching

我想跨连接表的列进行搜索。以下查询的最佳索引配置是什么?

Table A
------------
Id
ValueA
-- Table A has many more columns

Table B
------------
Id
AId
ValueB
-- Table B has many more columns
Run Code Online (Sandbox Code Playgroud)

此刻,我对主键和非聚集索引(不包括任何列)列的聚簇索引AIdValueAValueB

SELECT *
FROM TableA a
INNER JOIN TableB b ON a.Id = b.AId
WHERE 
    a.ValueA LIKE 'SearchTerm%' OR
    b.ValueB LIKE 'SearchTerm%'
Run Code Online (Sandbox Code Playgroud)

更新 表 A 可能会遇到数百万个,而表 B 将只有数万个条目。

在现实世界中,表 A 将是一个交易列表,其中的值是某种交易引用。表 B 将是一个用户列表,其中的值可以是任何名称或任何其他描述性信息。

目前我们使用的是一个 ORM,它本质上会做一个SELECT *. 当然,这可以改变,但会对应用程序产生相当大的影响。

Pau*_*ite 7

您的查询对于优化器来说是一个棘手的问题,原因有二。

  1. 优化器目前没有将跨表的析取 (OR) 转换为联合(相关问答)的逻辑。
  2. 优化器不会考虑推迟查找(直到最后一刻才处理键)。这与给SELECT *定查询的组件有关。

总之,这些限制意味着几乎没有索引可以帮助写入(ORM 生成)的查询,撇开(非聚集)列存储索引的可能性。

如果性能不足,您可能不得不硬着头皮编写自定义查询。在这种情况下,修改现有索引ValueB包含 AId将很有用,再加上如下重写:

SELECT TA.*, TB.* 
FROM 
(
    -- Disjunction as union
    SELECT A.Id, b.Id
    FROM TableA a 
    INNER JOIN TableB b 
        ON b.AId = a.Id
    WHERE 
        a.ValueA LIKE 'SearchTerm%'

    UNION

    SELECT A.Id, b.Id
    FROM TableB b 
    INNER JOIN TableA a 
        ON a.Id = b.AId
    WHERE 
        b.ValueB LIKE 'SearchTerm%'
) AS M (A_Id, B_Id)
-- Lookups
JOIN dbo.TableA AS TA ON TA.Id = M.A_Id
JOIN dbo.TableB AS TB ON TB.Id = M.B_Id;
Run Code Online (Sandbox Code Playgroud)

目标计划形状为:

执行计划

这在实践中有多好取决于几个因素,包括LIKE谓词的选择性(以及它们总是前缀搜索)。


猜测的架构:

DROP TABLE IF EXISTS dbo.TableB, dbo.TableA;

CREATE TABLE dbo.TableA 
(
    Id integer NOT NULL PRIMARY KEY, 
    ValueA varchar(256) NULL INDEX iValueA,
    Padding char(4000) NOT NULL DEFAULT '', -- other columns

    /*INDEX i (ValueA) INCLUDE (Id)*/
);

CREATE TABLE dbo.TableB 
(
    Id integer NOT NULL PRIMARY KEY,
    AId integer NOT NULL REFERENCES dbo.TableA (Id) INDEX iAId,
    ValueB varchar(256) NULL /*INDEX iValueB*/,
    Padding char(2000) NOT NULL DEFAULT '', -- other columns

    INDEX i (ValueB) INCLUDE (AId)
);

UPDATE STATISTICS dbo.TableA WITH ROWCOUNT = 3000000, PAGECOUNT = 3000000;
UPDATE STATISTICS dbo.TableB WITH ROWCOUNT = 30000, PAGECOUNT = 30000;
Run Code Online (Sandbox Code Playgroud)