Bisakah saya melakukan pertandingan huruf pertama yang cukup besar di dua meja?

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

SQL Server. Apakah ada cara untuk membuat SARGable ini / berjalan lebih cepat? Saya tidak bisa membuat kolom di tabel orang, tapi saya bisa membuat kolom di orang2.

lastchancexi
sumber
3
Anda tahu bahwa hasil dari permintaan itu akan menjadi semacam SALIB GABUNG, sebenarnya?
ypercubeᵀᴹ
1
Seberapa besar tabelnya? Jika mereka masing-masing mengatakan hanya 10 ribu baris, hasilnya akan setidaknya 4 juta baris. Saya ingin tahu apa yang akan menjadi penggunaan query tersebut.
ypercubeᵀᴹ
1
@ ypercubeᵀᴹ mungkin input awal ke dalam beberapa proses deduplikasi menggunakan pencocokan fuzzy?
Martin Smith
Kedengarannya seperti ide yang buruk. Apa yang ingin Anda capai di sini?
David דודו Markovitz
Ini hanya sebagai contoh. Ada lebih banyak predikat. Martin Smith memiliki ide yang tepat, ini untuk deduplikasi.
lastchancexi

Jawaban:

9

Buat tampilan pada tabel dengan kolom yang dihitung tetap didefinisikan sebagai LEFT(lastname, 1)masing-masing tabel, lalu bandingkan nilai-nilai kolom tetap yang dihitung.

Berikut adalah test-bed yang menunjukkan cara melakukannya:

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

Di sini, kami akan memasukkan beberapa data sampel:

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

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

Inilah SELECTpertanyaannya:

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

Dan hasilnya:

+ ---------- + ----------- + ---------- + --------------- - + ---------- + ----------- + ---------- + ------------- ---- +
| PersonID | Nama Depan | Nama Belakang | LastNameInitial | PersonID | Nama Depan | Nama Belakang | LastNameInitial |
+ ---------- + ----------- + ---------- + --------------- - + ---------- + ----------- + ---------- + ------------- ---- +
| 2 | Joe | Hitam | B | 2 | Joe | Hitam | B |
| 1 | Maks. | Vernon | V | 1 | Maks. | Vernon | V |
+ ---------- + ----------- + ---------- + --------------- - + ---------- + ----------- + ---------- + ------------- ---- +

Rencana eksekusi, dengan hanya dua baris per tabel (harus diakui tidak banyak baris!)

masukkan deskripsi gambar di sini

Max Vernon
sumber
11

Jika lastnamekolom diindeks setidaknya dalam satu tabel maka Anda juga bisa menggunakanLIKE

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

masukkan deskripsi gambar di sini

Rencana untuk ini dapat memiliki pencarian di atas meja yang ditentukan di sebelah kiri sejenisnya.

yaitu ON p.lastname LIKE LEFT(p2.lastname, 1) + '%'tidak akan dapat menggunakan indeks pada persons2yang digunakan di atas tetapi bisa mencari satu persons.

Namun, saran dalam jawaban lain untuk mengindeks kolom terhitung di kedua sisi lebih fleksibel. Adapun rencana loop bersarang tabel baik dapat di dalam dan itu juga akan memungkinkan banyak bergabung banyak bergabung tanpa memerlukan semacam.

Martin Smith
sumber
bagaimana dengan pendekatan ini ? Silakan menambahkannya di jawaban Anda jika ada manfaatnya. Apakah akan menggunakan indeks pada kedua tabel - dan jika demikian, apakah akan lebih efisien?
ypercubeᵀᴹ
@ ypercubeᵀᴹ Ini bisa memberikan rencana seperti ini jika indeksnya mencakup i.stack.imgur.com/RSzcT.png . Saya tidak melihat adanya keuntungan atas rencana dalam jawaban saya. Karena masih akan perlu membaca semua baris di tabel luar, barusan melalui 26 mencari daripada satu pemindaian.
Martin Smith
2

Saya kebetulan memiliki tabel dengan 3.423 baris dan 195 nilai berbeda di Name. Saya akan memanggil tabel ini P(orang) dan menduplikasinya untuk membuat P2(person2). Ada kunci primer unik dan berkerumun di kolom ID integer. Saya menggunakan Microsoft SQL Server 2016 (KB3194716) Edisi Pengembang (64-bit) pada Windows 10 Pro 6.3 dengan 32GB RAM.

Dengan kueri basis

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

Saya mendapatkan baris 1,5M yang dikembalikan dalam 3200-3300ms (dari statistik io).

masukkan deskripsi gambar di sini

Dengan menulis ulang demikian -

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

berkurang berkurang menjadi 50-60 ms dan rencananya adalah:

masukkan deskripsi gambar di sini

Lebih sedikit baris dikembalikan (3.423) karena algoritma pencocokan. Paket dan jumlah baris yang sama dicapai dengan mengubah kueri basis menjadi select distinct.

Dengan membuat indeks, kolom dihitung

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

create index ix1 on dbo.p2(Name1);

Waktu yang berlalu turun menjadi 45-50ms.

masukkan deskripsi gambar di sini

Michael Green
sumber