t-sql“LIKE”和模式匹配

时间:2011-08-23 21:46:57

标签: tsql pattern-matching sql-like

我发现了一个小小的烦恼,我想知道如何绕过...

在一个简化的例子中,说我需要返回“TEST B-19”和“TEST B-20”

我有一个where子句,如下所示:

where [Name] LIKE 'TEST B-[12][90]'

并且它有效...除非有一个我不想要的“TEST B-10”或“TEST-B29”值。

我宁愿不采取这两种情况,因为在更复杂的情况下会变得过高。

我试过了:

where [Name] LIKE 'TEST B-[19-20]'

但当然这不起作用,因为它正在寻找单个字符......

思考?同样,这是一个非常简单的例子,我正在寻找方法来获取从16到32或234到459的范围,而不会抓住可能创建的所有额外值。

已编辑以包含测试示例......

您可能会在该字段中看到“TEXAS 22”或“THX 99-20-110-B6”或“E-19”或“SOUTHERN B”或“122 FLOWERS”。数字的存在是常见的,但不是一个坚定的规则,并且绝对没有关于超大,数字,字符,顺序等的一般模式。

4 个答案:

答案 0 :(得分:2)

我会将Name列分成文本部分和数字部分,并将数字部分转换为整数,然后检查是否在值之间。类似的东西:

where cast(substring([Name], 7, 2) as integer) between 19 and 20

当然,如果[Name]的可能结构要复杂得多,你必须计算7和2的值,而不是硬编码....

编辑:如果您想先滤除不符合图案的图案,请执行以下操作:

where [Name] LIKE '%TEST B-__%'
and cast(substring([Name], CHARINDEX('TEST B-', [Name]) + LEN('TEST B-'), 2) as integer) between 19 and 20

使用CHARINDEX替代最顶行第二行中的LIKE可能会更快,特别是如果你在计算值上放一个索引,但是......那只是优化......:)

编辑:测试过程。鉴于以下数据:

jajajajajajajTEST B-100
jajajajajajajTEST B-85
jajajajjTEST B-100
jajjajajTEST B-100
jajajajajajajTEST B-00
jajajajaTEST B-100
jajajajajajajEST B-99
jajajajajajajTEST B-100
jajajajajajajTEST B-19
jajajajjTEST B-100
jajjajajTEST B-120
jajajajajajajTEST B-00
jajajajaTEST B-150
jajajajajajajEST B-20
TEST B-20asdfh asdfkh

查询返回以下行:

jajajajajajajTEST B-19
TEST B-20asdfh asdfkh

答案 1 :(得分:1)

在没有测试数据的情况下,我自己生成了测试数据。我刚刚删除了Test B-前缀,转换为int并执行了Between

With Numerals As
(
    Select top 100 row_number() over (order by name)  TestNumeral
    from sys.columns
),
TestNumbers AS
(
    Select 'TEST B-' + Convert (VarChar, TestNumeral) TestNumber
    From Numerals
)
Select * 
From TestNumbers
Where Cast (Replace (TestNumber, 'TEST B-', '') as Integer) between 1 and 16

这给了我

TestNumber
-------------------------------------
TEST B-1
TEST B-2
TEST B-3
TEST B-4
TEST B-5
TEST B-6
TEST B-7
TEST B-8
TEST B-9
TEST B-10
TEST B-11
TEST B-12
TEST B-13
TEST B-14
TEST B-15
TEST B-16

但是,这意味着如果您有不同的命名测试策略,则必须删除所有不同类型的前缀。

现在,另一方面,如果您的测试编号是TEST-Space-TestType-Hyphen-TestNumber格式,您可以使用PatIndex和SubString

With Numerals As
(
    Select top 100 row_number() over (order by name)  TestNumeral
    from sys.columns
),
TestNumbers AS
(
    Select 'TEST B-' + Convert (VarChar, TestNumeral) TestNumber
    From Numerals
    Where TestNumeral Between 10 and 19
    UNION
    Select 'TEST A-' + Convert (VarChar, TestNumeral) TestNumber
    From Numerals
    Where TestNumeral Between 20 and 29
)
Select *
From TestNumbers
Where Cast (SubString (TestNumber, PATINDEX ('%-%', TestNumber)+1, Len (TestNumber) - PATINDEX ('%-%', TestNumber)) as Integer) between 16 and 26

这应该产生以下

TestNumber
-------------------------------------
TEST A-20
TEST A-21
TEST A-22
TEST A-23
TEST A-24
TEST A-25
TEST A-26
TEST B-16
TEST B-17
TEST B-18
TEST B-19

您的所有示例似乎都有最终的测试编号。因此,如果您可以创建模式表,然后使用LIKE语句加入JOIN,那么您可以使其工作。这是一个例子:

;
With TestNumbers As
(
      select 'E-1' TestNumber
union select 'E-2'
union select 'E-3'
union select 'E-4'
union select 'E-5'
union select 'E-6'
union select 'E-7'
union select 'SOUTHERN B1'
union select 'SOUTHERN B2'
union select 'SOUTHERN B3'
union select 'SOUTHERN B4'
union select 'SOUTHERN B5'
union select 'SOUTHERN B6'
union select 'SOUTHERN B7'
union select 'Southern CC'
union select 'Southern DD'
union select 'Southern EE'
union select 'TEST B-1'
union select 'TEST B-2'
union select 'TEST B-3'
union select 'TEST B-4'
union select 'TEST B-5'
union select 'TEST B-6'
union select 'TEST B-7'
union select 'TEXAS 1'
union select 'TEXAS 2'
union select 'TEXAS 3'
union select 'TEXAS 4'
union select 'TEXAS 5'
union select 'TEXAS 6'
union select 'TEXAS 7'
union select 'THX 99-20-110-B1'
union select 'THX 99-20-110-B2'
union select 'THX 99-20-110-B3'
union select 'THX 99-20-110-B4'
union select 'THX 99-20-110-B5'
union select 'THX 99-20-110-B6'
union select 'THX 99-20-110-B7'
union select 'Southern AA'
union select 'Southern CC'
union select 'Southern DD'
union select 'Southern EE'
),
Prefixes as
(
    Select 'TEXAS ' TestPrefix
    Union Select 'THX 99-20-110-B'
    Union Select 'E-'
    Union Select 'SOUTHERN B'
    Union Select 'TEST B-'
)
Select TN.TestNumber
From TestNumbers TN, Prefixes P
Where 1=1 
And TN.TestNumber Like '%' + P.TestPrefix + '%'
And Cast (REPLACE (Tn.TestNumber, p.TestPrefix, '') AS INTEGER) between 4 and 6

这会给你

TestNumber
----------------
E-4
E-5
E-6
SOUTHERN B4
SOUTHERN B5
SOUTHERN B6
TEST B-4
TEST B-5
TEST B-6
TEXAS 4
TEXAS 5
TEXAS 6
THX 99-20-110-B4
THX 99-20-110-B5
THX 99-20-110-B6

(15 row(s) affected)

答案 2 :(得分:1)

通配符或否,每次要更改范围定义时,仍然需要编辑查询。如果您总是处理范围(并且它并不总是相同的范围),您可以使用参数。例如:

注意:出于某种原因(这也发生在很多其他帖子中),当我尝试发布以'declare'开头的代码时,SO挂起并超时。我已经在meta上报告了它,但没有人可以重现它(包括我)。在这里它又一次发生了,所以我把'D'关了,现在它起作用了。明天我会回来的,它会让我把'D'重新打开。

DECLARE @min varchar(5)
DECLARE @max varchar(5)

SET @min = 'B-19'
SET @max = 'B-20'

SELECT
   ...
WHERE NAME BETWEEN @min AND @max

您应该避免像其他人建议的那样格式化[NAME](使用其上的功能) - 这样,您的搜索就可以从中获得索引。

无论如何 - 您可能会重新考虑您的表格结构。听起来'TEST B-19'是类别('TEST')+子类别('B')+实例('19')的复合(非标准化)值。将它放在一个包含4列(id为第一列)的查找表中,然后在任何需要输出复合值的查询中通过id连接它。这将使搜索和索引更容易,更快。

答案 3 :(得分:0)

这是否可以接受:

WHERE [Name] IN ( 'TEST B-19', 'TEST B-20' )

值列表可以来自子查询,例如:

WHERE [Name] IN ( SELECT [Name] FROM Elsewhere WHERE ... )