昨天我遇到过这个问题并且认为我解决了这个问题,但我错了。出于此脚本的目的,我需要将变量放在带分隔符的字符串中(这是因为最终用户将在帐户标题中搜索术语)。
例如,假设最终用户想要搜索标题中包含“水”或“食物”的时间。他们不需要在同一个标题。结果可能会回归“世界之水”和“思考的食物”。我知道我可以使用一些AND和OR语句轻松地写这个
SELECT ...
FROM ...
WHERE 1=1
and (rpt_title LIKE '%Food%'
or rpt_title LIKE '%Water%')
变量只取一个参数,但是作为一个字符串。 (即'食物|水')。我已经有一个函数,它将采用字符分隔的字符串并将其转换为多个变量。我不认为这是解决方案。我想我需要使用substring和charindex来定位分隔符(在这种情况下为|,因为它很可能不在标题中),然后将其转换为OR语句。
有什么想法或建议吗?
编辑:最终结果是输入变量的能力 (例如 - rpt_title LIKE'%'+ @ title +'%')
答案 0 :(得分:1)
我不确定为什么你认为拆分会是一个坏主意。您可以创建一个相对有效的内联TVF并加入其中,例如假设你有一个Numbers表:
CREATE FUNCTION dbo.SplitStrings_Numbers
(
@List NVARCHAR(MAX),
@Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
SELECT Item = SUBSTRING(@List, Number,
CHARINDEX(@Delimiter, @List + @Delimiter, Number) - Number)
FROM dbo.Numbers
WHERE Number <= CONVERT(INT, LEN(@List))
AND SUBSTRING(@Delimiter + @List, Number, LEN(@Delimiter)) = @Delimiter
);
GO
(对于绝对最有效的方法,例如CLR,请参阅http://www.sqlperformance.com/2012/07/t-sql-queries/split-strings)
现在你可以说:
SELECT t.rpt_title
FROM dbo.table_name AS t
INNER JOIN dbo.SplitStrings_Numbers(@Input, '|') AS x
ON t.rpt_title LIKE '%' + x.Item + '%'
GROUP BY t.rpt_title;
另一个想法是首先不使用像foo|bar|splunge
这样的列表,而是使用TVP。首先创建一个表类型:
CREATE TYPE dbo.TitleMatches
(
Pattern NVARCHAR(64)
);
GO
然后使用它的存储过程:
CREATE PROCEDURE dbo.Whatever
@matches dbo.TitleMatches READONLY
AS
BEGIN
SET NOCOUNT ON;
SELECT t.rpt_title
FROM dbo.table_name AS t
INNER JOIN @matches AS m
ON t.rpt_title LIKE '%' + m.Pattern + '%'
GROUP BY t.rpt_title;
END
GO
然后你只需要传入你的DataTable或任何包含潜在匹配列表的结构,例如:在C#中,这可能看起来像:
DataTable DataTableName = new DataTable();
// ... populate data table here
SqlCommand cmd = new SqlCommand("dbo.Whatever", conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter tvp = cmd.Parameters.AddWithValue("@matches", DataTableName);
tvp.SqlDbType = SqlDbType.Structured;
// ... execute, consume results, etc.
(http://www.sqlperformance.com/2012/08/t-sql-queries/splitting-strings-now-with-less-t-sql)
的详细信息答案 1 :(得分:0)
嗯,这有点脏,但它有效。原谅我,我有一段时间没有做过TSQL。
DECLARE @delim CHAR, @search NVARCHAR(100)
set @delim = '|'
set @search = 'world|food'
DECLARE @tblSearch TABLE(
string NVARCHAR(100)
)
DECLARE @tmpStr NVARCHAR(100)
WHILE (@search <> '')
BEGIN
IF (CHARINDEX(@delim, @search) = 0)
BEGIN
INSERT INTO @tblSearch(string) VALUES('%' + @search + '%')
SET @search = ''
END
ELSE
BEGIN
INSERT INTO @tblSearch(string)
SELECT '%' + SUBSTRING(@search, 0, CHARINDEX(@delim, @search)) + '%'
SET @search = SUBSTRING(@search, CHARINDEX(@delim, @search) + 1, LEN(@search))
END
END
SELECT ...
FROM ... as m
INNER JOIN @tblSearch as s ON m.rpt_title like s.string
答案 2 :(得分:0)
听起来你需要使用动态tsql,如下所示。不幸的是,我多次读过它使用起来效率不高,因为它无法由服务器预编译,并且每次运行时都必须在运行时进行优化,这比你的方式要慢。但是,我认为这应该完成你所要求的。 要运行它,请调用它 proc_DelimitedQuery'aaa | bbb | etc'
create PROCEDURE [dbo].[proc_DelimitedQuery]
@delimitedString varchar(100)
as
DECLARE @delimiter int
declare @pos int
DECLARE @item varchar(500)
Declare @criteria varchar(5000)
set @criteria = '(rpt_title like ''%'
set @pos=1
--find the first delimiter
SET @delimiter = CHARINDEX('|', @delimitedString, @pos)
WHILE @delimiter<>0
BEGIN
set @item=substring(@delimitedString, @pos, @delimiter-@pos)
set @criteria = @criteria + @item + '%'' or rpt_title like ''%'
--Find the next delimiter
set @pos=@delimiter+1
if charindex('|', @delimitedString, @pos)=0
begin
set @pos=@delimiter+1
set @item=substring(@delimitedString, @pos, datalength(@delimitedString)-@delimiter)
set @criteria = @criteria + @item + '%'')'
set @delimiter=0
end
else
begin
set @delimiter=charindex('|', @delimitedString, @pos)
end
END
declare @sql varchar(500)
set @sql='select * from MyTable where 1=1 and ' + @criteria
--change the next line to select @sql if you want to examine the sql statement
EXECUTE(@sql)
GO