select value
from persons p join persons2 p2
on left(p.lastname,1) = left(p2.lastname,1)
เซิร์ฟเวอร์ SQL มีวิธีใดที่ทำให้ SARGable นี้ / ทำงานได้เร็วขึ้น? ฉันไม่สามารถสร้างคอลัมน์ในตารางบุคคล แต่ฉันสามารถสร้างคอลัมน์ใน person2
select value
from persons p join persons2 p2
on left(p.lastname,1) = left(p2.lastname,1)
เซิร์ฟเวอร์ SQL มีวิธีใดที่ทำให้ SARGable นี้ / ทำงานได้เร็วขึ้น? ฉันไม่สามารถสร้างคอลัมน์ในตารางบุคคล แต่ฉันสามารถสร้างคอลัมน์ใน person2
คำตอบ:
สร้างมุมมองบนตารางที่มีคอลัมน์ที่คำนวณแล้วซึ่งถูกกำหนดเป็น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 | ชื่อ นามสกุล | LastNameInitial | PersonID | ชื่อ นามสกุล | LastNameInitial | + + ----------- ---------- ---------- + + --------------- - + + ----------- ---------- ---------- + + ------------- ---- + | 2 | โจ | ดำ | B | 2 | โจ | ดำ | B | | 1 | สูงสุด | เวอร์นอน | V | 1 | สูงสุด | เวอร์นอน | V | + + ----------- ---------- ---------- + + --------------- - + + ----------- ---------- ---------- + + ------------- ---- +
แผนการดำเนินการโดยมีเพียงสองแถวต่อตาราง (ยอมรับกันไม่มากนัก!)
หาก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ได้
คำแนะนำในคำตอบอื่น ๆ ของการจัดทำดัชนีคอลัมน์จากการคำนวณทั้งสองด้านนั้นมีความยืดหยุ่นมากกว่า สำหรับแผนการลูปซ้อนกันทั้งสองตารางสามารถอยู่ด้านในและมันยังอนุญาตให้รวมหลาย ๆ กลุ่มเข้าร่วมโดยไม่ต้องเรียงลำดับ
ฉันเกิดขึ้นจะมีตารางที่มี 3,423 แถวและ 195 Nameค่าแตกต่างกันใน ฉันจะเรียกตารางนี้P(คน) และทำซ้ำเพื่อสร้างP2(person2) มีคีย์หลักที่ไม่ซ้ำกันและคลัสเตอร์ในคอลัมน์ ID จำนวนเต็ม ฉันใช้ Microsoft SQL Server 2016 (KB3194716) Developer Edition (64 บิต) บน Windows 10 Pro 6.3 พร้อม 32GB RAM
ด้วยแบบสอบถามฐาน
select
p.pid
from dbo.p
inner join dbo.p2
on LEFT(p.name, 1) = LEFT(p2.name, 1);
ฉันได้รับแถว 1.5M ที่ส่งคืนใน 3200-3300ms (จากสถิติ io)
โดยการเขียนใหม่จึง -
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