2つのテーブルで検索可能な最初の文字の一致を実行できますか?


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

SQLサーバー。これをSARG可能/より速く実行する方法はありますか?Personsテーブルには列を作成できませんが、Persons2には列を作成できます。


3
そのクエリの結果が一種のCROSS JOINになることをご存じですか?
ypercubeᵀᴹ

1
テーブルの大きさは?それぞれがたった10K行であるとすると、結果は少なくとも400万行になります。このようなクエリの使用法は何でしょうか。
ypercubeᵀᴹ

1
@ypercubeᵀᴹ多分ファジーマッチングを使用した重複排除プロセスへの初期入力?
マーティン・スミス

悪い考えのように聞こえます。ここで何を達成しようとしていますか?
DavidדודוMarkovitz 2017

これはほんの一例です。より多くの述語があります。マーティン・スミスは正しい考えを持っています、それは重複排除のためです。
lastchancexi 2017年

回答:


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;

そして結果:

+ ---------- + ----------- + ---------- + --------------- -+ ---------- + ----------- + ---------- + ------------- ---- +
| PersonID | FirstName | 姓| LastNameInitial | PersonID | FirstName | 姓| LastNameInitial |
+ ---------- + ----------- + ---------- + --------------- -+ ---------- + ----------- + ---------- + ------------- ---- +
| 2 | ジョー| 黒| B | 2 | ジョー| 黒| B |
| 1 | マックス| ヴァーノン| V | 1 | マックス| ヴァーノン| V |
+ ---------- + ----------- + ---------- + --------------- -+ ---------- + ----------- + ---------- + ------------- ---- +

テーブルごとに2行のみの実行プラン(確かに多くの行はありません!)

ここに画像の説明を入力してください


11

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ます。

ただし、計算された列の両側にインデックスを付ける別の答えの提案は、より柔軟です。ネストされたループ計画に関しては、どちらのテーブルも内側に配置でき、ソートを必要とせずに多対多のマージ結合も可能になります。


およそ何このアプローチ?メリットがある場合は、自由に回答に追加してください。両方のテーブルでインデックスを使用していますか?使用している場合、より効率的でしょうか?
ypercubeᵀᴹ

@ypercubeᵀᴹインデックスがi.stack.imgur.com/RSzcT.pngをカバーしている場合、次のような計画が得られます。私の答えでは、計画に対する利点は何も見当たらない。それでも、外部テーブルのすべての行を読み取る必要があるため、1回のスキャンではなく26回のシークを実行します。
マーティンスミス

2

たまたま、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に下がります。

ここに画像の説明を入力してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.