亞倫伯特蘭從最近的兩篇文章無恥地竊取:
的JIST是創建我們可以使用的東西類似於trigram
(or trigraph
) in PostgreSQL。
阿龍貝特朗還包括免責聲明如下:
「之前,我開始表現出我所提出的解決方案是如何工作的,讓我絕對清楚地表明該解決方案不應該在每一個情況下LIKE使用'%wildcard%'搜索速度很慢。由於我們要將源數據「分解」爲片段的方式,與較大的字符串(如產品說明或會話摘要)相比,它可能在實用性上受限於較小的字符串,例如地址或名稱。「
測試設置:http://rextester.com/IIMT54026
客戶表
使用亞倫貝特朗爆炸串片段
create table dbo.Client (
ClientId int not null primary key clustered
, FirstName varchar(50) not null
, LastName varchar(50) not null
);
insert into dbo.Client (ClientId, FirstName, LastName) values
(1, 'James','')
, (2, 'Aaron','Bertrand')
go
功能(修改後用於輸入大小):
create function dbo.CreateStringFragments(@input varchar(101))
returns table with schemabinding
as return
(
with x(x) as (
select 1 union all select x+1 from x where x < (len(@input))
)
select Fragment = substring(@input, x, len(@input)) from x
);
go
表來存儲片段爲FirstName + ' ' + LastName
:
create table dbo.Client_NameFragments (
ClientId int not null
, Fragment varchar(101) not null
, constraint fk_ClientsNameFragments_Client
foreign key(ClientId) references dbo.Client
on delete cascade
);
create clustered index s_cat on dbo.Client_NameFragments(Fragment, ClientId);
go
載入與片段表:
insert into dbo.Client_NameFragments (ClientId, Fragment)
select c.ClientId, f.Fragment
from dbo.Client as c
cross apply dbo.CreateStringFragments(FirstName + ' ' + LastName) as f;
go
創建觸發器保持片段:
create trigger dbo.Client_MaintainFragments
on dbo.Client
for insert, update as
begin
set nocount on;
delete f from dbo.Client_NameFragments as f
inner join deleted as d
on f.ClientId = d.ClientId;
insert dbo.Client_NameFragments(ClientId, Fragment)
select i.ClientId, fn.Fragment
from inserted as i
cross apply dbo.CreateStringFragments(i.FirstName + ' ' + i.LastName) as fn;
end
go
快速觸發測試S:
/* trigger tests --*/
insert into dbo.Client (ClientId, FirstName, LastName) values
(3, 'Sql', 'Zim')
update dbo.Client set LastName = 'unknown' where LastName = '';
delete dbo.Client where ClientId = 3;
--select * from dbo.Client_NameFragments order by ClientId, len(Fragment) desc
/* -- */
go
新程序:
create procedure [dbo].[Client_getNameList] @Name varchar(100) as
begin
set nocount on;
select
ClientId
, Name = FirstName + ' ' + LastName
from Client c
where exists (
select 1
from dbo.Client_NameFragments f
where f.ClientId = c.ClientId
and f.Fragment like @Name+'%'
)
end
go
exec [dbo].[Client_getNameList] @Name = 'On Bert'
回報:
+----------+----------------+
| ClientId | Name |
+----------+----------------+
| 2 | Aaron Bertrand |
+----------+----------------+
此查詢不能使用索引。您可以使用索引的每種方式都有更好的性能 - 因爲內容化字符串和以'%'開頭的類似表達式永遠不會使用索引。這種情況最簡單的方法是使用全文索引和全文搜索。最好的(但更復雜的方式)是分解和規範化你的輸入數據。 – Deadsheep39
如果使用傳統SQL排序規則('SQL [_]%')或二進制排序規則('%[_] BIN%')而不是Windows排序規則,則可以提高使用通配符的全掃描性能。由於全面掃描,查詢仍然很昂貴。 –
您能否詳細說明參數Name?它始終是第一個還是最後一個名字?它可以是部分名稱嗎?它可以是First +''+ Last組合的部分字符串嗎?因爲這個選擇還打開了新的可能性:First ='Scott'Last ='Tiger' - 在你的查詢名稱中''T''也會找到一個匹配項。而與每個人分別比較會改變結果集。 –