select value
from persons p join persons2 p2
on left(p.lastname,1) = left(p2.lastname,1)
SQLサーバー。これをSARG可能/より速く実行する方法はありますか?Personsテーブルには列を作成できませんが、Persons2には列を作成できます。
select value
from persons p join persons2 p2
on left(p.lastname,1) = left(p2.lastname,1)
SQLサーバー。これをSARG可能/より速く実行する方法はありますか?Personsテーブルには列を作成できませんが、Persons2には列を作成できます。
回答:
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;
そして結果:
+ ---------- + ----------- + ---------- + --------------- -+ ---------- + ----------- + ---------- + ------------- ---- + | PersonID | FirstName | 姓| LastNameInitial | PersonID | FirstName | 姓| LastNameInitial | + ---------- + ----------- + ---------- + --------------- -+ ---------- + ----------- + ---------- + ------------- ---- + | 2 | ジョー| 黒| B | 2 | ジョー| 黒| B | | 1 | マックス| ヴァーノン| V | 1 | マックス| ヴァーノン| V | + ---------- + ----------- + ---------- + --------------- -+ ---------- + ----------- + ---------- + ------------- ---- +
テーブルごとに2行のみの実行プラン(確かに多くの行はありません!)
lastname列が少なくとも1つのテーブルでインデックス付けされている場合は、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ます。
ただし、計算された列の両側にインデックスを付ける別の答えの提案は、より柔軟です。ネストされたループ計画に関しては、どちらのテーブルも内側に配置でき、ソートを必要とせずに多対多のマージ結合も可能になります。
たまたま、3,423行のテーブルと195個の異なる値がありNameます。このテーブルをP(person)と呼び、複製して作成しますP2(person2)。整数ID列に、一意のクラスター化された主キーがあります。32GB RAMを搭載したWindows 10 Pro 6.3でMicrosoft SQL Server 2016(KB3194716)Developer Edition(64ビット)を使用しています。
基本クエリ
select
p.pid
from dbo.p
inner join dbo.p2
on LEFT(p.name, 1) = LEFT(p2.name, 1);
(統計ioから)3200〜3300ミリ秒で150万行が返されます。
このように書き直すことで-
select
p.pid
from dbo.p
where exists
(
select 1
from dbo.p2
where LEFT(p.name, 1) = LEFT(p2.name, 1)
);
経過時間は50〜60ミリ秒に短縮され、計画は次のとおりです。
一致アルゴリズムのため、返される行が少なくなります(3,423)。ベースクエリをに変更することで、同じプランと行数が実現されますselect distinct。
インデックス付きの計算列を作成する
alter table dbo.p2
add Name1 as Left(Name, 1);
create index ix1 on dbo.p2(Name1);
経過時間が45-50msに下がります。