简体   繁体   English

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

[英]Building dynamic T-SQL query from a string argument in a sproc

Let's say I have a table which contains a varchar field: 假设我有一个包含varchar字段的表:

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

The [MyId] column contains sequential alphanum values like A1, A2... A99, B1, B2..B99, C1 and so on (up to Z99). [MyId]列包含连续的字母数字值,例如A1,A2 ... A99,B1,B2..B99,C1等(直到Z99)。

What I'd like to do is to extract rows from the table whose MyId field matches some specific prefixes... eg I'd like to fetch rows from the series A, C, P and X. 我想做的是从MyId字段与某些特定前缀匹配的表中提取行...例如,我想从A,C,P和X系列中获取行。

And I'd like to this with a sproc which will dynamically construct the query based on the prefix alphabets supplied in the argument. 我想使用一个sproc,它将根据参数中提供的前缀字母动态构建查询。

I'm thinking about something like this... 我正在考虑这样的事情...

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

I think the main bulk of the stored procedure will resemble the following pseudo-code: 我认为存储过程的主要内容将类似于以下伪代码:

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)

The above proecedure will be called like this: 上面的过程将这样称呼:

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

... or perhaps like this (if it makes splitting the alphabets easier): ...或可能是这样(如果这样可以更容易地拆分字母):

EXEC uspFilterMyTable("ACPX")

Any ideas? 有任何想法吗? Pointers? 指针?


Update: OK, this is what I've come up with ([Split] function borrowed from Chhatrapati Sharma): 更新:好的,这就是我想出的(从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    

It's ugly I know, but it works... any tips to improvise the code? 我知道这很丑陋,但是可以用...有什么技巧可以即兴编写代码?

first you should create below function and then use this in query like this 首先,您应该创建下面的函数,然后像这样在查询中使用它

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 

Here you have a nice and fast split method based on XML: 在这里,您有一个很好的基于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

Result will be a table with the splitted values: 结果将是一个包含拆分值的表:

code
A1
B3
C4
B12
K19

From here you can continue and use this table on your procedure and join with you original table using LIKE as you propossed. 从这里开始,您可以继续使用此表并在您的过程中使用该表,并按照建议使用LIKE与原始表连接。

I would have suggested you to use table valued parameters to call your stored procedure. 我建议您使用表值参数来调用您的存储过程。 I guess you call it from .net. 我猜您是从.net调用它的。 But EF I think will not be able to handle it, though you might check it. 但是我认为EF无法处理它,尽管您可以检查一下。 If not, I think the best way is to first parse the string into a temporary table, or a table value and after that join with it. 如果没有,我认为最好的方法是首先将字符串解析为一个临时表或一个表值,然后将其与之连接。

With TVP: 使用TVP:

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

With a split function (of your choosing, you find many examples on the net) 使用拆分功能(您可以选择在网上找到许多示例)

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

Where for both cases @prefixArray is a table variable is Id = varchar(3) 在两种情况下,@prefixArray是一个表变量,其中Id = varchar(3)

As an edit, after a little digging, it seems that with a little work EF works fine with TVPs. 作为编辑,稍加挖掘后,看来EF可以在TVP上正常工作。 Check this : Entity Framework Stored Procedure Table Value Parameter . 检查以下内容: 实体框架存储过程表值参数 So The best thing is to send directly a table to your stored procedure, then to send a string to parse. 因此,最好的办法是直接向存储过程发送表,然后发送要解析的字符串。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM