select value
from persons p join persons2 p2
on left(p.lastname,1) = left(p2.lastname,1)
Máy chủ SQL. Có cách nào để làm cho SARGable / chạy nhanh hơn không? Tôi không thể tạo cột trên bảng người, nhưng tôi có thể tạo cột trên người2.
select value
from persons p join persons2 p2
on left(p.lastname,1) = left(p2.lastname,1)
Máy chủ SQL. Có cách nào để làm cho SARGable / chạy nhanh hơn không? Tôi không thể tạo cột trên bảng người, nhưng tôi có thể tạo cột trên người2.
Câu trả lời:
Tạo một khung nhìn trên các bảng với một cột được tính toán bền vững được xác định là LEFT(lastname, 1)của mỗi bảng, sau đó so sánh các giá trị cột được tính toán bền vững.
Đây là một chiếc giường thử nghiệm cho thấy làm thế nào để làm điều đó:
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);
Ở đây, chúng tôi sẽ chèn một số dữ liệu mẫu:
INSERT INTO dbo.Persons(FirstName, LastName)
VALUES ('Max', 'Vernon')
, ('Joe', 'Black');
INSERT INTO dbo.Persons2(FirstName, LastName)
VALUES ('Max', 'Vernon')
, ('Joe', 'Black');
Đây là SELECTtruy vấn:
SELECT *
FROM dbo.PersonsView pv1
INNER JOIN dbo.PersonsView2 pv2 ON pv1.LastNameInitial = pv2.LastNameInitial;
Và kết quả:
+ ---------- + ----------- + ---------- + --------------- - + ---------- + ----------- + ---------- + ------------- ---- + | Cá nhân | Tên đầu tiên | Họ | LastNameInitial | Cá nhân | Tên đầu tiên | Họ | LastNameInitial | + ---------- + ----------- + ---------- + --------------- - + ---------- + ----------- + ---------- + ------------- ---- + | 2 | Joe | Đen | B | 2 | Joe | Đen | B | | 1 | Tối đa | Vernon | V | 1 | Tối đa | Vernon | V | + ---------- + ----------- + ---------- + --------------- - + ---------- + ----------- + ---------- + ------------- ---- +
Kế hoạch thực hiện, chỉ có hai hàng trên mỗi bảng (phải thừa nhận là không có nhiều hàng!)
Nếu lastnamecột được lập chỉ mục trong ít nhất một trong các bảng thì bạn cũng có thể sử dụngLIKE
SELECT *
FROM persons p
INNER JOIN persons2 p2
ON p2.lastname LIKE LEFT(p.lastname, 1) + '%'
Kế hoạch cho việc này có thể có một tìm kiếm trên bảng được chỉ định ở bên trái của thích.
tức là ON p.lastname LIKE LEFT(p2.lastname, 1) + '%'sẽ không thể sử dụng chỉ mục trên persons2đã được sử dụng ở trên nhưng có thể tìm kiếm trên persons.
Tuy nhiên, gợi ý trong câu trả lời khác về lập chỉ mục một cột được tính toán ở cả hai bên là linh hoạt hơn. Đối với một kế hoạch các vòng lặp lồng nhau, một trong hai bảng có thể ở bên trong và nó cũng sẽ cho phép nhiều phép nối nhiều mà không yêu cầu sắp xếp.
Tôi tình cờ có một bảng có 3.423 hàng và 195 giá trị riêng biệt trong Name. Tôi sẽ gọi bảng này P(người) và sao chép nó để tạo P2(person2). Có một khóa chính duy nhất, được nhóm trên một cột ID số nguyên. Tôi đang sử dụng Microsoft SQL Server 2016 (KB3194716) Phiên bản dành cho nhà phát triển (64-bit) trên Windows 10 Pro 6.3 với RAM 32GB.
Với truy vấn cơ sở
select
p.pid
from dbo.p
inner join dbo.p2
on LEFT(p.name, 1) = LEFT(p2.name, 1);
Tôi nhận được 1,5 triệu hàng được trả lại trong 3200-3300ms (từ thống kê io).
Bằng cách viết lại như vậy -
select
p.pid
from dbo.p
where exists
(
select 1
from dbo.p2
where LEFT(p.name, 1) = LEFT(p2.name, 1)
);
trôi qua giảm xuống 50-60ms và kế hoạch là:
Ít hàng hơn được trả về (3,423) vì thuật toán phù hợp. Kế hoạch và số hàng tương tự đạt được bằng cách thay đổi truy vấn cơ sở thành select distinct.
Bằng cách tạo cột được lập chỉ mục, tính toán
alter table dbo.p2
add Name1 as Left(Name, 1);
create index ix1 on dbo.p2(Name1);
Thời gian trôi qua giảm xuống 45-50ms.