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


9
select value 
from persons p join persons2 p2 
    on left(p.lastname,1) = left(p2.lastname,1)

SQL Server。有什么方法可以使此SARGable /运行速度更快?我无法在人员表上创建列,但可以在人员2上创建列。


3
您知道该查询的结果实际上是一种CROSS JOIN吗?
ypercubeᵀᴹ

1
桌子多大?如果每个人仅说1万行,那么结果将至少是400万行。我不知道这种查询的用途是什么。
ypercubeᵀᴹ

1
@ypercubeᵀᴹ可能是一些使用模糊匹配的重复数据删除过程的初始输入?
马丁·史密斯

听起来是个坏主意。您想在这里实现什么?
DavidדודוMarkovitz

这只是举例。谓词更多。马丁·史密斯(Martin Smith)有一个正确的想法,那就是重复数据删除。
lastchancexi

Answers:


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);

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

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

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

这是SELECT查询:

SELECT *
FROM dbo.PersonsView pv1
    INNER JOIN dbo.PersonsView2 pv2 ON pv1.LastNameInitial = pv2.LastNameInitial;

结果:

+ ---------- + ----------- + ---------- + --------------- -+ ---------- + ----------- + ---------- + ------------- ---- +
| 人名| 名| 姓氏| LastNameInitial | 人名| 名| 姓氏| LastNameInitial |
+ ---------- + ----------- + ---------- + --------------- -+ ---------- + ----------- + ---------- + ------------- ---- +
| 2 | 乔| 黑色| B | 2 | 乔| 黑色| B |
| 1 | 最高| 弗农| V | 1 | 最高| 弗农| V |
+ ---------- + ----------- + ---------- + --------------- -+ ---------- + ----------- + ---------- + ------------- ---- +

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

在此处输入图片说明


11

如果该lastname列在至少一个表中被索引,那么您也可以使用LIKE

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

在此处输入图片说明

该计划可以在类似表格左侧指定的表格上查找。

ON p.lastname LIKE LEFT(p2.lastname, 1) + '%'不能够利用该指数的persons2是上面使用,但可能会寻求一个上persons

但是,在另一个答案中建议在两边都对已计算的列建立索引的建议更为灵活。至于嵌套循环计划,任何一个表都可以位于内部,并且还可以进行多对多合并联接而无需排序。


这种方法呢?如果有任何好处,请随时将其添加到您的答案中。它会在两个表上都使用索引吗?如果这样,会更有效吗?
ypercubeᵀᴹ

@ypercubeᵀᴹ如果索引覆盖i.stack.imgur.com/RSzcT.png,则可以给出这样的计划。不过,我在回答中没有看到该计划有任何优势。由于仍然需要读取外部表中的所有行,因此现在仅通过26次搜索而不是一次扫描。
马丁·史密斯

2

我碰巧有一个表,其中包含3423行和195个不同的值Name。我将这个表称为P(人员)并复制它以创建P2(人员2)。在整数ID列上有一个唯一的集群主键。我正在具有32GB RAM的Windows 10 Pro 6.3上使用Microsoft SQL Server 2016(KB3194716)开发人员版(64位)。

与基本查询

select
    p.pid
from dbo.p
inner join dbo.p2 
    on LEFT(p.name, 1) = LEFT(p2.name, 1);

我在3200-3300ms(从统计io)中返回了150万行。

在此处输入图片说明

通过这样重写-

select
    p.pid
from dbo.p
where exists
(
    select 1
    from dbo.p2 
    where LEFT(p.name, 1) = LEFT(p2.name, 1)
);

经过时间减少到50-60ms,计划是:

在此处输入图片说明

由于匹配算法,返回的行较少(3,423)。通过将基本查询更改为,可以实现相同的计划和行数select distinct

通过创建索引的计算列

alter table dbo.p2
add Name1 as Left(Name, 1);

create index ix1 on dbo.p2(Name1);

经过的时间降至45-50ms。

在此处输入图片说明

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.