由于动态@Name参数和子查询,我有以下存储过程非常广泛。
有更好的更有效的方法吗?
CREATE PROCEDURE [dbo].[spGetClientNameList]
@Name varchar(100)
AS
BEGIN
SET NOCOUNT ON;
SELECT
*
FROM
(
SELECT
ClientID,
FirstName + ' ' + LastName as Name
FROM
Client
) a
where a.Name like '%' + @Name + '%'
答案 0 :(得分:0)
我猜连接列上的搜索操作有时不会占用索引。我得到了上面的情况,我用OR
替换了连接搜索,这使我在大多数时候都有更好的表现。
如果不存在,则在FirstName
和LastName
上创建非群集索引。
修改上述程序后检查性能,如下所示
CREATE PROCEDURE [dbo].[spGetClientNameList]
@Name varchar(100)
AS
BEGIN
SET NOCOUNT ON;
SELECT
ClientID,
FirstName + ' ' + LastName as Name
FROM
Client
WHERE FirstName LIKE '%' + @Name + '%'
OR LastName LIKE '%' + @Name + '%'
END
并检查执行计划以验证是否使用了这些索引。
答案 1 :(得分:0)
问题实际上归结为必须计算列(连接名字和姓氏),这几乎迫使sql server对表进行全面扫描以确定什么是匹配,什么不匹配。如果您不允许添加索引或更改表,则必须更改查询(分别提供firstName和lastName)。如果是,您可以添加计算列和索引:
Create Table client (
ClientId INT NOT NULL PRIMARY KEY IDENTITY(1,1)
,FirstName VARCHAR(100)
,LastName VARCHAR(100)
,FullName AS FirstName + ' ' + LastName
)
Create index FullName ON Client(FullName)
这至少可以通过索引搜索而不是全表扫描来加快查询速度。这值得么?如果不查看有多少数据等,很难说。
答案 2 :(得分:0)
where a.Name like '%' + @Name + '%'
此语句永远不能使用索引。在这种情况下,使用全文搜索
会更好如果您可以将like
限制为
where a.Name like @Name + '%'
它会自动使用索引。此外,您可以使用REVERSE()
函数来索引语句,如:
where a.Name like '%' + @Name
答案 3 :(得分:0)
Aaron Bertrand最近的两篇文章无耻地窃取:
jist是创建一个我们可以使用的类似于trigram
(or trigraph
) in PostgreSQL的东西。
Aaron Bertrand还包括以下免责声明:
"在我开始展示我提出的解决方案如何工作之前,让我绝对清楚,这个解决方案不应该用于LIKE'%通配符%'搜索很慢。由于我们采取的方式爆炸"爆炸"将源数据分成片段,实际上可能会限制较小的字符串,例如地址或名称,而不是较大的字符串,如产品描述或会话摘要。"
测试设置: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
Aaron Bertrand用于爆炸字符串片段的函数(针对输入大小进行了修改):
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
快速触发测试:
/* 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 |
+----------+----------------+