我很习惯使用MySQL,但对SQL Server并不是特别熟悉。运气好,我在这里处理的数据库是在SQL Server 2014上。
我有一个包含列的表,其值都是带有前导,分隔和尾随分号的整数,就像这三个虚构的行一样:
;905;1493;384;13387;29;933;467;28732;
;905;138;3084;1387;290;9353;4767;2732;
;9085;14493;3864;130387;289;933;4767;28732;
我现在要做的是从此列中选择从数字列表中取出多个数字的所有行。所以例如,给定上面的三行,如果我有组905,467,4767
,我试图弄清楚如何构造的语句应该返回前两行:第一行包含905和467;第二行包含905和4767.第三行仅包含4767,因此不应返回该行。
As far as I can tell,SQL Server实际上并不直接支持正则表达式(我甚至不知道托管代码是什么),这没有用。即使使用正则表达式,我也不知道从哪里开始。 Oracle似乎有a function that would be very useful,但那是Oracle。
这里大多数类似的问题都涉及找到同一个角色的多个实例(通常是单数)并通过replacing the string to match with nothing and counting the difference in length来解决问题。我认为这在技术上也可以在这里工作,但是如果给出一个由15个数字组成的“过滤器”组,那么SELECT
语句会变得荒谬冗长而且错综复杂,完全不可读。另外,我只想匹配整个数字(因此,如果要匹配的数字之一是29,则第29行将匹配第一行,但第二行中的值290不匹配) ,这意味着我必须在REPLACE
子句中包含分号,然后在计算长度时对它们进行折扣。一团糟。
我理想的做法是这样的:
SELECT * FROM table WHERE REGEXP_COUNT(column, ';(905|467|4767);') > 1
- 但由于各种原因(最明显的一个原因是Oracle外部REGEXP_COUNT
不存在),这显然不会起作用。
是否有一些理智,可管理的方式?
答案 0 :(得分:2)
你可以做到
SELECT *
FROM Mess
CROSS APPLY (SELECT COUNT(*)
FROM (VALUES (905),
(467),
(4767)) V(Num)
WHERE Col LIKE CONCAT('%;', Num, ';%')) ca(count)
WHERE count > 1
或者
WITH Nums
AS (SELECT Num
FROM (VALUES (905),
(467),
(4767)) V(Num))
SELECT Mess.*
FROM Mess
CROSS APPLY (VALUES(CAST(CONCAT('<x>', REPLACE(Col, ';', '</x><x>'), '</x>') AS XML))) x(x)
CROSS APPLY (SELECT COUNT(*)
FROM (SELECT n.value('.', 'int')
FROM x.x.nodes('/x') n(n)
WHERE n.value('.', 'varchar') <> ''
INTERSECT
SELECT Num
FROM Nums) T(count)
HAVING COUNT(*) > 1) ca2(count)
答案 1 :(得分:1)
你能把你的参数放到一个表中(可能使用一个表值函数接受一个字符串(逗号分隔的整数)作为参数)并使用这样的东西吗?
DECLARE @T table (String varchar(255))
INSERT INTO @T
VALUES
(';905;1493;384;13387;29;933;467;28732;')
, (';905;138;3084;1387;290;9353;4767;2732;')
, (';9085;14493;3864;130387;289;933;4767;28732;')
DECLARE @Arguments table (Arg int)
INSERT INTO @Arguments
VALUES
(905)
, (467)
, (4767)
SELECT String
FROM
@T
CROSS JOIN @Arguments
GROUP BY String
HAVING SUM(CASE WHEN PATINDEX('%;' + CAST(Arg AS varchar) + ';%', String) > 0 THEN 1 ELSE 0 END) > 1
使用此函数生成参数的示例:
CREATE FUNCTION GenerateArguments (@Integers varchar(255))
RETURNS @Arguments table (Arg int)
AS
BEGIN
WITH cte
AS
(
SELECT
PATINDEX('%,%', @Integers) p
, LEFT(@Integers, PATINDEX('%,%', @Integers) - 1) n
UNION ALL
SELECT
CASE WHEN PATINDEX('%,%', SUBSTRING(@Integers, p + 1, LEN(@Integers))) + p = p THEN 0 ELSE PATINDEX('%,%', SUBSTRING(@Integers, p + 1, LEN(@Integers))) + p END
, CASE WHEN PATINDEX('%,%', SUBSTRING(@Integers, p + 1, LEN(@Integers))) = 0 THEN RIGHT(@Integers, PATINDEX('%,%', REVERSE(@Integers)) - 1) ELSE LEFT(SUBSTRING(@Integers, p + 1, LEN(@Integers)), PATINDEX('%,%', SUBSTRING(@Integers, p + 1, LEN(@Integers))) - 1) END
FROM cte
WHERE p <> 0
)
INSERT INTO @Arguments (Arg)
SELECT n
FROM cte
RETURN
END
GO
DECLARE @T table (String varchar(255))
INSERT INTO @T
VALUES
(';905;1493;384;13387;29;933;467;28732;')
, (';905;138;3084;1387;290;9353;4767;2732;')
, (';9085;14493;3864;130387;289;933;4767;28732;')
;
SELECT String
FROM
@T
CROSS JOIN GenerateArguments('905,467,4767')
GROUP BY String
HAVING SUM(CASE WHEN PATINDEX('%;' + CAST(Arg AS varchar) + ';%', String) > 0 THEN 1 ELSE 0 END) > 1
答案 2 :(得分:0)
你可以使用regex和row_number的like函数来确定匹配的数量。
这里我们声明用于测试的列值:
DECLARE @tbl TABLE (
string NVARCHAR(MAX)
)
INSERT @tbl VALUES
(';905;1493;384;13387;29;933;467;28732;'),
(';905;138;3084;1387;290;9353;4767;2732;'),
(';9085;14493;3864;130387;289;933;4767;28732;')
然后我们将您的搜索参数传递给要加入的表变量:
DECLARE @search_tbl TABLE (
search_value INT
)
INSERT @search_tbl VALUES
(905),
(467),
(4767)
最后,我们将表与列一起搜索到搜索表。我们应用row_number函数来确定它匹配的次数。我们从这个子查询中选择row_number = 2,这意味着它至少加入了两次。
SELECT
string
FROM (
SELECT
tbl.string,
ROW_NUMBER() OVER (PARTITION BY tbl.string ORDER BY tbl.string) AS rn
FROM @tbl tbl
JOIN @search_tbl search_tbl ON
tbl.string LIKE '%;' + CAST(search_tbl.search_value AS NVARCHAR(MAX)) + ';%'
) tbl
WHERE rn = 2
答案 3 :(得分:0)
您可以构建一个这样的where子句:
WHERE
case when column like '%;905;%' then 1 else 0 end +
case when column like '%;467;%' then 1 else 0 end +
case when column like '%;4767;%' then 1 else 0 end >= 2
优点是您不需要帮助程序表。我不知道你是如何构建查询的,但是以下内容也可以工作,如果数字在tsql变量中,则非常有用。
case when column like ('%;' + @n + ';%') then 1 else 0 end