SQL SELECT 表中的行(如果给定条件是列数据的子字符串,子字符串之间';'分隔符)



如果列[to]有邮件abc@mail,我试图从表中获取行。

简单的解是Select * from Mails where [To] = 'abc@mail'

但事情是[To]列的数据如123@mail;abc@mail;aabc@mail等用分号分隔其中To为发送的多封邮件

我知道我可以做一些像Select * from Mails where [To] like '%abc@mail%',但这不会解决问题,如果给定的邮件是另一个邮件的子字符串。我想到了一个拆分字符串的解决方案

我有一个这样的split_string函数,

CREATE FUNCTION [dbo].[split_string]
(
@string_value NVARCHAR(MAX),
@delimiter_character CHAR(1)
)
RETURNS @result_set TABLE(splited_data NVARCHAR(MAX)
)
BEGIN
DECLARE @start_position INT,
@ending_position INT
SELECT @start_position = 1,
@ending_position = CHARINDEX(@delimiter_character, @string_value)
WHILE @start_position < LEN(@string_value) + 1
BEGIN
IF @ending_position = 0 
SET @ending_position = LEN(@string_value) + 1
INSERT INTO @result_set (splited_data) 
VALUES(SUBSTRING(@string_value, @start_position, @ending_position - @start_position))
SET @start_position = @ending_position + 1
SET @ending_position = CHARINDEX(@delimiter_character, @string_value, @start_position)
END
RETURN
END

返回列中单个数据的分割字符串,函数工作正常。

我试着执行查询

Select * 
from Mails 
where 'abc@mail' in (
Select * 
from dbo.split_string((SELECT [To] FROM Mails) , ';')
)

抛出错误:

子查询返回多个值。这是不允许的子查询在=,!=,<, <=,>,>=或子查询用作一个表达式。

从这里开始,我需要帮助。我使用的是Microsoft SQL Server 2014。

TL;DR;这是您想要的查询

SELECT *
FROM dbo.Mails AS m
WHERE EXISTS (
SELECT *
FROM dbo.split_string(m.[To], ';') s
WHERE s.splited_data = 'abc@mail'
)

我推荐拆分方法。任何字符查找都必须考虑分号的可变性,而拆分分号将处理分号所在位置的模糊性,然后可以进行直接的相等性检查。如果你想更进一步,寻找额外的[to]地址,你可以像这样添加一个IN子句,SQL Server不需要做更多的工作,你得到相同的结果。

SELECT *
FROM dbo.Mails AS m
WHERE EXISTS (
SELECT *
FROM dbo.split_string(m.[To], ';') s
WHERE s.splited_data IN ('abc@mail', 'def@mail')
)

我的答案与@Kitta的答案非常相似,因为我们将数据分离出来,@Kitta关于in子句是正确的,但是当他们的答案有效时,它需要你将数据分组回一起以获得一个单一的答案。使用EXISTS子句将为您绕过所有这些问题,只提供原始表中的数据。话虽如此,如果他们的答案对你同样有效,请标记@Kitta作为答案。

下面是我使用的测试设置
DROP TABLE Mails
GO
CREATE TABLE Mails
([To] VARCHAR(3000))
INSERT INTO dbo.Mails
(
[To]
)
VALUES
('123@mail;abc@mail;aabc@mail')
,('nottheone@mail.com')
,('nottheone@mail.com;Overhere@mail.com')
,('aabc@mail;ewrkljwe@mail')
,('ewrkljwe@mail')
GO
DROP FUNCTION [split_string]
GO
CREATE FUNCTION [dbo].[split_string]
(
@string_value NVARCHAR(MAX),
@delimiter_character CHAR(1)
)
RETURNS @result_set TABLE(splited_data NVARCHAR(MAX)
)
BEGIN
DECLARE @start_position INT,
@ending_position INT
SELECT @start_position = 1,
@ending_position = CHARINDEX(@delimiter_character, @string_value)
WHILE @start_position < LEN(@string_value) + 1
BEGIN
IF @ending_position = 0 
SET @ending_position = LEN(@string_value) + 1
INSERT INTO @result_set (splited_data) 
VALUES(SUBSTRING(@string_value, @start_position, @ending_position - @start_position))
SET @start_position = @ending_position + 1
SET @ending_position = CHARINDEX(@delimiter_character, @string_value, @start_position)
END
RETURN
END
GO

SELECT *
FROM dbo.Mails AS m
WHERE EXISTS (
SELECT *
FROM dbo.split_string(m.[To], ';') s
WHERE s.splited_data = 'abc@mail'
)

,它返回正确的'123@mail;abc@mail;aabc@mail'行

您不能将多行子查询作为参数传递给dbo.split_string函数。尝试将表函数连接到Mails表:

SELECT DISTINCT ms.*
FROM Mails AS ms
CROSS APPLY dbo.split_string(ms.[To], ';') AS s
WHERE s.splited_data LIKE 'abc@mail'

如果你可以升级你的SQL Server到2016 (13.x),你可以使用内置的STRING_SPLIT表函数代替自定义的dbo.split_string

或者,您可以使用蛮力实现您的目标,并将比较分解为简单的术语,如以下所示:

SELECT * 
FROM Mails 
WHERE 
[To] LIKE 'abc@mail'
OR [To] LIKE '%;abc@mail;%'
OR [To] LIKE 'abc@mail;%' 
OR [To] LIKE '%;abc@mail'

这可能不是最好的方法,但它非常简单,并且不需要split函数。

可以使用CHARINDEX()函数

select * from Mails where CHARINDEX('abc@mail.com', To) > 0

CHARINDEX函数检查给定字符串中的子字符串,如果找到则返回大于零的值。这里你要搜索的字符串(子字符串)将是'abc@mail.com',而要搜索的主字符串将是"To"列。

关于CHARINDEX()函数的更多信息可以在下面的链接中找到https://www.techonthenet.com/sql_server/functions/charindex.php: ~:文本3 = % 20 SQL服务器% % 20 charindex % 20函数% 20 description。% 20 201% % 20 SQL,使用% 20 % 20 charindex % 20函数% 20 % 20 SQL服务器% % 20 28 transact - SQL % 29 20%。

最新更新