从sproc中的字符串参数构建动态T-SQL查询

时间:2012-09-20 06:44:33

标签: sql sql-server arrays tsql

假设我有一个包含varchar字段的表:

CREATE TABLE [MyTable] (
    [MyId]    varchar(3)    NOT NULL,
    .....
)

[MyId] 列包含连续的alphanum值,如A1,A2 ... A99,B1,B2..B99,C1等等(最高为Z99)。

我想要做的是从 MyId 字段与某些特定前缀匹配的表中提取行...例如我想从A,C,P和X系列中获取行。

我希望能够使用一个sproc,它会根据参数中提供的前缀字母动态构建查询。

我正在考虑这样的事情......

CREATE PROCEDURE [dbo].[uspFilterMyTable]
  @prefixArray varchar(max)
AS
  ... -- split individual characters from @prefixArray into an array

  SELECT * FROM [MyTable] 
  WHERE 
    [MyId] LIKE ....
    OR
    [MyId] LIKE ....  -- iterate all characters from @prefixArray

我认为存储过程的主要部分将类似于以下伪代码:

DECLARE @sql nvarchar(max)
-- iterate through all the characters
SET @sql = 'SELECT * FROM [MyTable] WHERE [MyId] LIKE ' + @charInTheArray + '%'
SET @sql = @sql + ' OR [MyId] LIKE ' + @nextCharInArray + '%'


EXEC (@sql)

上述行为将被称为:

EXEC uspFilterMyTable("A,C,P,X")

...或者也许是这样(如果它更容易拆分字母表):

EXEC uspFilterMyTable("ACPX")

有什么想法吗?指针?


更新好的,这就是我提出来的(从Chhatrapati Sharma借来的[Split]功能):

-- [MyTable] contains these rows: 'A7', 'A87', 'B16', 'C51', 'H99', 'X12'

-- the "input" parameter
DECLARE @prefixArray NVARCHAR(100)= 'H,A,C'

-- split the string into SQL wild-card patterns
DECLARE charCursor CURSOR FOR
    select items + N'%' from dbo.Split(@prefixArray, ',')


OPEN charCursor;
DECLARE @pattern CHAR(2)

-- create temp table if necessary
IF NOT EXISTS(SELECT * FROM TEMPDB.SYS.TABLES WHERE NAME LIKE '#tmpTable%')
    CREATE TABLE #tmpTable ([Id] VARCHAR(3) NOT NULL)

-- purge old data
DELETE FROM #tmpTable

FETCH NEXT FROM charCursor into @pattern
WHILE @@FETCH_STATUS = 0
BEGIN
      --SELECT * INTO #tmpTable FROM [MyTable] WHERE [MyId] LIKE @pattern   
      Insert Into #tmpTable Select * FROM [MyTable] WHERE [MyId] LIKE @pattern  
    FETCH NEXT FROM charCursor into @pattern
END

CLOSE charCursor;
DEALLOCATE charCursor;

-- return the values
SELECT * FROM #tmpTable    

我知道这很难看,但它有效......任何即兴创作代码的提示?

3 个答案:

答案 0 :(得分:2)

首先你应该创建以下函数,然后在这样的查询中使用它

SELECT * FROM [MyTable] WHERE  [MyId] in (select items from dbo.split(@prefixArray,','))



CREATE FUNCTION [dbo].[Split](@String varchar(8000), @Delimiter char(1))         
returns @temptable TABLE (items varchar(8000))         
as         
begin         
    declare @idx int         
    declare @slice varchar(8000)         

    select @idx = 1         
    if len(@String)<1 or @String is null  return         

    while @idx!= 0         
     begin         
      set @idx = charindex(@Delimiter,@String)         
      if @idx!=0         
      set @slice = left(@String,@idx - 1)         
     else         
      set @slice = @String         

      if(len(@slice)>0)    
       insert into @temptable(Items) values(@slice)         

       set @String = right(@String,len(@String) - @idx)         
       if len(@String) = 0 break         
       end     
    return         
    end 

答案 1 :(得分:2)

这里有一个基于XML的漂亮而快速的分割方法:

DECLARE @str NVARCHAR(100)= 'A1,B3,C4,B12,K19', @separator VARCHAR(1)= ','
DECLARE @SplitedList  TABLE (code NVARCHAR(30))

DECLARE @XMLList XML
SET @XMLList=CAST('<i>'+REPLACE(@str, @separator,'</i><i>')+'</i>' AS XML)

INSERT INTO @SplitedList
SELECT x.i.value('(./text())[1]','varchar(100)')
FROM @XMLList.nodes('i') x(i)

SELECT * FROM @SplitedList

结果将是一个包含分割值的表:

code
A1
B3
C4
B12
K19

从这里开始,您可以继续使用此表格,并按照您的提议使用LIKE加入原始表格。

答案 2 :(得分:1)

我建议你使用表值参数来调用你的存储过程。我想你是从.net调用的。但我认为EF无法处理它,尽管你可能会检查它。如果没有,我认为最好的方法是首先将字符串解析为临时表或表值,然后再加入它。

TVP:

CREATE PROCEDURE [dbo].[uspFilterMyTable]
  @prefixArray tvp_idlist readonly
as
  select 
   t.* 
  from MyTable t
  join @prefixArray pa on pa.id = t.myid

使用分割功能(根据您的选择,您可以在网上找到很多示例)

CREATE PROCEDURE [dbo].[uspFilterMyTable]
  @prefixArray varchar(max)
as

  create @prefixArray tvp_idlist
  insert into @prefixArray (id)
  select id from dbo.myCustomSplit(@prefixArray,',')


  select 
   t.* 
  from MyTable t
  join @prefixArray pa on pa.id = t.myid

对于两种情况,@ prefixArray是一个表变量,其中Id = varchar(3)

作为一个编辑,经过一番挖掘后,似乎只需要一点点工作,EF就可以和TVP一起使用。检查一下:Entity Framework Stored Procedure Table Value Parameter。所以最好的方法是直接将表发送到存储过程,然后发送一个字符串进行解析。