使用动态参数查询 - 更好的方法?

时间:2017-02-12 08:46:15

标签: sql-server tsql

由于动态@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 + '%'

4 个答案:

答案 0 :(得分:0)

我猜连接列上的搜索操作有时不会占用索引。我得到了上面的情况,我用OR替换了连接搜索,这使我在大多数时候都有更好的表现。

如果不存在,则在FirstNameLastName上创建非群集索引。

修改上述程序后检查性能,如下所示

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 |
+----------+----------------+