我可以在两张桌子上进行 sargable 首字母匹配吗?

las*_*exi 9 sql-server

select value 
from persons p join persons2 p2 
    on left(p.lastname,1) = left(p2.lastname,1)
Run Code Online (Sandbox Code Playgroud)

SQL 服务器。有没有办法让这个 SARGable/运行得更快?我无法在persons 表上创建列,但我可以在persons2 上创建列。

Mar*_*ith 11

如果该lastname列至少在其中一个表中建立索引,那么您还可以使用LIKE

SELECT *
FROM   persons p
       INNER JOIN persons2 p2
               ON p2.lastname LIKE LEFT(p.lastname, 1) + '%' 
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

此计划可以在诸如左侧指定的表上进行查找。

ieON p.lastname LIKE LEFT(p2.lastname, 1) + '%'将无法persons2使用上面使用的索引,但可以persons.

然而,在两侧索引计算列的另一个答案中的建议更灵活。至于嵌套循环计划,任何一个表都可以在内部,它也允许多对多合并连接而无需排序。


Han*_*non 9

使用定义为LEFT(lastname, 1)每个表的的持久计算列在表上创建视图,然后比较计算的持久列值。

这是一个测试台,展示了如何做到这一点:

CREATE TABLE dbo.Persons
(
    PersonID int NOT NULL
        CONSTRAINT PK_Persons
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , FirstName nvarchar(500) NOT NULL
    , LastName nvarchar(500) NOT NULL
);

CREATE TABLE dbo.Persons2
(
    PersonID int NOT NULL
        CONSTRAINT PK_Persons2
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , FirstName nvarchar(500) NOT NULL
    , LastName nvarchar(500) NOT NULL
);

GO
CREATE VIEW dbo.PersonsView
WITH SCHEMABINDING
AS
SELECT p1.PersonID
    , p1.FirstName
    , p1.LastName 
    , LastNameInitial = LEFT(p1.LastName, 1)
FROM dbo.Persons p1;
GO
CREATE VIEW dbo.PersonsView2
WITH SCHEMABINDING
AS
SELECT p2.PersonID
    , p2.FirstName
    , p2.LastName 
    , LastNameInitial = LEFT(p2.LastName, 1)
FROM dbo.Persons p2;
GO
CREATE UNIQUE CLUSTERED INDEX CX_PersonsView
ON dbo.PersonsView(PersonID);
CREATE NONCLUSTERED INDEX IX_PersonsView_LastNameInitial
ON dbo.PersonsView(LastNameInitial)
INCLUDE (FirstName, LastName);

CREATE UNIQUE CLUSTERED INDEX CX_PersonsView2
ON dbo.PersonsView2(PersonID);
CREATE NONCLUSTERED INDEX IX_PersonsView2_LastNameInitial
ON dbo.PersonsView2(LastNameInitial)
INCLUDE (FirstName, LastName);

CREATE STATISTICS ST_PersonsView_001
ON dbo.PersonsView(LastName);

CREATE STATISTICS ST_PersonsView2_001
ON dbo.PersonsView2(LastName);
Run Code Online (Sandbox Code Playgroud)

在这里,我们将插入一些示例数据:

INSERT INTO dbo.Persons(FirstName, LastName)
VALUES ('Max', 'Vernon')
    , ('Joe', 'Black');

INSERT INTO dbo.Persons2(FirstName, LastName)
VALUES ('Max', 'Vernon')
    , ('Joe', 'Black');
Run Code Online (Sandbox Code Playgroud)

这是SELECT查询:

SELECT *
FROM dbo.PersonsView pv1
    INNER JOIN dbo.PersonsView2 pv2 ON pv1.LastNameInitial = pv2.LastNameInitial;
Run Code Online (Sandbox Code Playgroud)

结果:

+-----------+-----------+----------+-------------- ---+-----------+-----------+-----------+------------- ----+
| 人名 | 名字 | 姓氏 | 姓氏首字母 | 人名 | 名字 | 姓氏 | 姓氏首字母 |
+-----------+-----------+----------+-------------- ---+-----------+-----------+-----------+------------- ----+
| 2 | 乔| 黑色 | 乙 | 2 | 乔| 黑色 | 乙 |
| 1 | 最大 | 弗农 | V | 1 | 最大 | 弗农 | V |
+-----------+-----------+----------+-------------- ---+-----------+-----------+-----------+------------- ----+

执行计划,每个表只有两行(当然行数不多!)

在此处输入图片说明