[英]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.