SQL 服务器:使用字符串的所有单词作为单独的 LIKE 参数(并且所有单词都应该匹配)
SQL Server: use all the words of a string as separate LIKE parameters (and all words should match)
我有一个包含一定数量单词的字符串(它可能从 1 到很多不等),我需要找到 table 的记录,其中包含所有这些单词,顺序不限。
例如,假设我的输入字符串是 'yellow blue red' 并且我有一个包含以下记录的 table:
1 yellow brown white
2 red blue yellow
3 black blue red
查询应该return记录2.
我知道基本的方法应该是这样的:
select * from mytable where colors like '%yellow%' and colors like '%blue%' and colors like '%red%'
但是我无法弄清楚如何将字符串的单词变成单独的类似参数。
我有这段代码将字符串的单词拆分为 table,但现在我卡住了:
DECLARE @mystring varchar(max) = 'yellow blue red';
DECLARE @terms TABLE (term varchar(max));
INSERT INTO @terms
SELECT Split.a.value('.', 'NVARCHAR(MAX)') term FROM (SELECT CAST('<X>'+REPLACE(@mystring, ' ', '</X><X>')+'</X>' AS XML) AS String) AS A CROSS APPLY String.nodes('/X') AS Split(a)
SELECT * FROM @terms
有什么想法吗?
首先,将 XML 垃圾放入函数中:
CREATE FUNCTION dbo.SplitThem
(
@List NVARCHAR(MAX),
@Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN ( SELECT Item = y.i.value(N'(./text())[1]', N'nvarchar(4000)')
FROM ( SELECT x = CONVERT(XML, '<i>'
+ REPLACE(@List, @Delimiter, '</i><i>')
+ '</i>').query('.')
) AS a CROSS APPLY x.nodes('i') AS y(i));
现在您可以提取 table 中的单词,将它们连接到输入字符串中的单词,并丢弃任何不具有相同计数的单词:
DECLARE @mystring varchar(max) = 'red yellow blue';
;WITH src AS
(
SELECT t.id, t.colors, fc = f.c, tc = COUNT(t.id)
FROM dbo.mytable AS t
CROSS APPLY dbo.SplitThem(t.colors, ' ') AS s
INNER JOIN (SELECT Item, c = COUNT(*) OVER()
FROM dbo.SplitThem(@mystring, ' ')) AS f
ON s.Item = f.Item
GROUP BY t.id, t.colors, f.c
)
SELECT * FROM src
WHERE fc = tc;
输出:
id
colors
fc
tc
2
red blue yellow
3
3
这忽略了任何一方重复的可能性,并忽略了更大的首要问题,即这是存储事物集的最不理想的方式。你有一个关系数据库,使用它!你肯定不认为这个问题上的标签作为文字字符串存储在某处
string sql-server-2012 sql-like
当然不是,这些 question
:tag
关系存储在关系 table 中。拆弦是为了鸟儿和那些有各种CPU和空闲时间的人。
如果您将分隔列表存储在单个列中,那么您确实需要将其规范化为单独的 table。
但假设您实际上只想进行多次 free-form LIKE
比较,您可以针对值列表进行比较:
select *
from mytable t
where not exists (select 1
from (values
('%yellow%'),
('%blue%'),
('%red%')
) v(search)
where t.colors not like v.search
);
理想情况下,您应该将这些值作为 Table 值参数传递,然后将其放入查询中
select *
from mytable t
where not exists (select 1
from @tmp v
where t.colors not like v.search
);
如果您想模拟 OR
语义而不是 AND
,请将 not exists
更改为 exists
并将 not like
更改为 like
。
我有一个包含一定数量单词的字符串(它可能从 1 到很多不等),我需要找到 table 的记录,其中包含所有这些单词,顺序不限。
例如,假设我的输入字符串是 'yellow blue red' 并且我有一个包含以下记录的 table:
1 yellow brown white
2 red blue yellow
3 black blue red
查询应该return记录2.
我知道基本的方法应该是这样的:
select * from mytable where colors like '%yellow%' and colors like '%blue%' and colors like '%red%'
但是我无法弄清楚如何将字符串的单词变成单独的类似参数。
我有这段代码将字符串的单词拆分为 table,但现在我卡住了:
DECLARE @mystring varchar(max) = 'yellow blue red';
DECLARE @terms TABLE (term varchar(max));
INSERT INTO @terms
SELECT Split.a.value('.', 'NVARCHAR(MAX)') term FROM (SELECT CAST('<X>'+REPLACE(@mystring, ' ', '</X><X>')+'</X>' AS XML) AS String) AS A CROSS APPLY String.nodes('/X') AS Split(a)
SELECT * FROM @terms
有什么想法吗?
首先,将 XML 垃圾放入函数中:
CREATE FUNCTION dbo.SplitThem
(
@List NVARCHAR(MAX),
@Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN ( SELECT Item = y.i.value(N'(./text())[1]', N'nvarchar(4000)')
FROM ( SELECT x = CONVERT(XML, '<i>'
+ REPLACE(@List, @Delimiter, '</i><i>')
+ '</i>').query('.')
) AS a CROSS APPLY x.nodes('i') AS y(i));
现在您可以提取 table 中的单词,将它们连接到输入字符串中的单词,并丢弃任何不具有相同计数的单词:
DECLARE @mystring varchar(max) = 'red yellow blue';
;WITH src AS
(
SELECT t.id, t.colors, fc = f.c, tc = COUNT(t.id)
FROM dbo.mytable AS t
CROSS APPLY dbo.SplitThem(t.colors, ' ') AS s
INNER JOIN (SELECT Item, c = COUNT(*) OVER()
FROM dbo.SplitThem(@mystring, ' ')) AS f
ON s.Item = f.Item
GROUP BY t.id, t.colors, f.c
)
SELECT * FROM src
WHERE fc = tc;
输出:
id | colors | fc | tc |
---|---|---|---|
2 | red blue yellow | 3 | 3 |
这忽略了任何一方重复的可能性,并忽略了更大的首要问题,即这是存储事物集的最不理想的方式。你有一个关系数据库,使用它!你肯定不认为这个问题上的标签作为文字字符串存储在某处
string sql-server-2012 sql-like
当然不是,这些 question
:tag
关系存储在关系 table 中。拆弦是为了鸟儿和那些有各种CPU和空闲时间的人。
如果您将分隔列表存储在单个列中,那么您确实需要将其规范化为单独的 table。
但假设您实际上只想进行多次 free-form LIKE
比较,您可以针对值列表进行比较:
select *
from mytable t
where not exists (select 1
from (values
('%yellow%'),
('%blue%'),
('%red%')
) v(search)
where t.colors not like v.search
);
理想情况下,您应该将这些值作为 Table 值参数传递,然后将其放入查询中
select *
from mytable t
where not exists (select 1
from @tmp v
where t.colors not like v.search
);
如果您想模拟 OR
语义而不是 AND
,请将 not exists
更改为 exists
并将 not like
更改为 like
。