【问题标题】:T-SQL PATINDEX Pattern of Seven Characters with at Least One Letter and One Number至少一个字母和一个数字的七个字符的 T-SQL PATINDEX 模式
【发布时间】:2019-02-12 03:06:39
【问题描述】:

我想在任意长度的文本中识别一个由七个字符组成的块:

  • 以字母开头
  • 至少包含一个数字(任何地方)
  • 所有字母均为大写

我将如何用PATINDEX() 表示这种类型的模式? PATINDEX('%[A-Z]%',text) 满足第一个要求,但不满足其他要求。我将如何制作这个变量,以便七个字符空间内的数字和字母可以以任何方式混杂(在第一个字符之后)?

我用它来打印块:SUBSTRING(MESSAGE_SUBJECT,PATINDEX('%[A-Z]%',MESSAGE_SUBJECT),7)

如果没有 CLR,这似乎是不可能的。为了更简单,是否有可能找到以字母开头并包含一个数字的七个字符分组?

【问题讨论】:

  • 区分大小写取决于您的排序规则
  • 没关系,我可以在最后添加排序规则 - 我的重点是前两个项目符号
  • where column like '[A-Z]%[0-9]%'
  • @scsimon 所以字符串不一定以模式开头。它只是任何以字母开头并包含至少一个数字的七个字符块。您的模式看起来只返回文本的前 7 个字符,而不管数字是否包含

标签: sql-server tsql patindex


【解决方案1】:

根据我上面的 cmets...

declare @table table (a varchar(64))
insert into @table
values
('aaaaaA123A')
,('123A')
,('A123a')
,('A123')
,('A123ADD')
,('A1DD23A')
,('aAAA1DD23A')
,('aAAAAAAA')
,('hello there AA11BB2')


select a, 1 
from @table
where 
patindex('%[A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]%',a collate Latin1_General_CS_AS) > 0
and substring(a,patindex('%[A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]%',a collate Latin1_General_CS_AS),7) collate Latin1_General_CS_AS = upper(substring(a,patindex('%[A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]%',a collate Latin1_General_CS_AS),7))
and patindex('%[0-9]%',substring(a,patindex('%[A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]%',a collate Latin1_General_CS_AS),7)) > 0

或者你可以用CASE标记它

select
    a
    ,MeetsPattern = case 
                        when patindex('%[A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]%',a collate Latin1_General_CS_AS) > 0
                        and substring(a,patindex('%[A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]%',a collate Latin1_General_CS_AS),7) collate Latin1_General_CS_AS = upper(substring(a,patindex('%[A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]%',a collate Latin1_General_CS_AS),7))
                        and patindex('%[0-9]%',substring(a,patindex('%[A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]%',a collate Latin1_General_CS_AS),7)) > 0
                        then 1
                        else 0
                    end
from @table

或者提取它

select
    a
    ,substring(a,patindex('%[A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]%',a collate Latin1_General_CS_AS),7)
from @table
where
patindex('%[A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]%',a collate Latin1_General_CS_AS) > 0
and substring(a,patindex('%[A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]%',a collate Latin1_General_CS_AS),7) collate Latin1_General_CS_AS = upper(substring(a,patindex('%[A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]%',a collate Latin1_General_CS_AS),7))
and patindex('%[0-9]%',substring(a,patindex('%[A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]%',a collate Latin1_General_CS_AS),7)) > 0

【讨论】:

  • 谢谢您-也请查看我的评论。文本可以是任意长度,我正在寻找一个七个字符的块。
  • 谢谢 - 看起来很接近 - 但是,它不能保证包含数字。因此,例如“hello there AA11BB2”返回“hello t”
  • good catch @CameronTaylor 又添加了一行来检查这一点
  • 谢谢 - 看起来它正在检查已经返回的文本。例如“hello there AA11BB2”返回“hello t”,然后您添加的新片段会检查“hello t”中的数字。相反,最好让数字检查与初始检查一起使用,以便首先返回“AA11BB2”。
  • 除了匹配之外,我没有返回任何东西。如果你想让我提取文本,那也是可能的
【解决方案2】:

我不相信 PATINDEX() 会给你你需要的东西。 PATINDEX() 函数返回与您的字符串匹配的第一个匹配项的位置。我想你会更高兴使用 LIKE() 函数。

【讨论】:

  • 谢谢 - 我编辑了我的问题以包含我用来根据 patindex 打印出值的子字符串函数
  • 但是@The_Flin 没有足够的声誉来发表评论。然后将其标记为 Not an answer
  • 这是一个答案。答案说使用 LIKE() 函数。如果您觉得这不正确或没用,请投反对票。
  • 不,scsimon 发布的是一个答案。这是一条评论,绝不会解决 OP 的问题。对此类问题的正确答案应包括代码 sn-p 或 OP 可用于解决其问题并附有解释的示例。此外,在这种情况下,LIKEPATINDEX 相比没有任何优势,WHERE PATINDEX(<pattern>,<string>) > 0WHERE <string> LIKE <pattern> 将返回相同的行。
【解决方案3】:

这样的事情不需要 CLR 或正则表达式。像这样的问题正是NGrams8K 旨在解决的问题。首先是关于 NGrams8K 的速成课程。

这个:

DECLARE @string VARCHAR(100) = 'ABC123XYZ'

SELECT ng.position, ng.token 
FROM   dbo.NGrams8k(@string, 7) AS ng;

返回:

position  token
--------- -----------
1         ABC123X
2         BC123XY
3         C123XYZ

识别 (1) 以字母开头的一组字母(也称为 子字符串,或者,在 N-Grams 的上下文中,一个 7-gram) , 至少包含一个数字并且不包含小写字母,您可以像这样使用 NGrams8K:

DECLARE @string VARCHAR(100) = 'x96AE0E33CFD5';

SELECT       ng.position, ng.token
FROM         dbo.ngrams8k(@string,7)                       AS ng
CROSS APPLY (VALUES(ng.token COLLATE latin1_general_bin2)) AS token(cs)
WHERE        token.cs LIKE '[A-Z]%[0-9]%' 
AND          token.cs NOT LIKE '%[a-z]%'; 

返回:

position   token
---------- ---------------
4          AE0E33C
5          E0E33CF
7          E33CFD5

如您所见,我们提取了符合您要求的每个 7 个字符的子字符串。或者,这会更有效:

SELECT ng.position, ng.token
FROM   dbo.ngrams8k(@string,7) AS ng
WHERE (ASCII(LEFT(ng.token,1)) - 65) & 0x7FFF < 26
AND    PATINDEX('%[a-z]%',ng.token COLLATE latin1_general_bin2) = 0;

为了更好地理解发生了什么,请考虑以下查询:

DECLARE @string VARCHAR(100) = 'x96AE0E33CFD5';

SELECT       ng.position, 
             ng.token, 
             isMatch = CASE WHEN token.cs LIKE '[A-Z]%[0-9]%' 
                             AND token.cs NOT LIKE '%[a-z]%' THEN 1 ELSE 0 END
FROM         dbo.ngrams8k(@string,7)                       AS ng
CROSS APPLY (VALUES(ng.token COLLATE latin1_general_bin2)) AS token(cs);

返回:

position   token      isMatch
---------- ---------- ---------
1          x96AE0E    0
2          96AE0E3    0
3          6AE0E33    0
4          AE0E33C    1
5          E0E33CF    1
6          0E33CFD    0
7          E33CFD5    1

这是一个针对您只想返回符合您的条件的行的表的示例:

DECLARE @table TABLE (someId INT IDENTITY, string VARCHAR(100));
INSERT @table(string) VALUES ('!!!!AB1234567'),('c555'),('!!ABC1234ggg')

SELECT t.someId, t.string
FROM   @table AS t
WHERE EXISTS
(
  SELECT  1
  FROM    dbo.ngrams8k(t.string,7) AS ng
  WHERE  (ASCII(LEFT(ng.token,1)) - 65) & 0x7FFF < 26
  AND     PATINDEX('%[a-z]%',ng.token COLLATE latin1_general_bin2) = 0
);

【讨论】:

  • 这很有趣 - 有什么方法可以只拾取 7 个两边都有空格的字符块 - 即单词级 ngram?此方法目前正在使用(来自“HELLO THERE 12345”)“e 12345”。我想忽略这些情况,只选择单词本身符合标准的情况。所以从“你好,AA11BB2”,我想要“AA11BB2”。
  • 我看到博客系列的第 5 部分应该讨论单词级别,但我似乎找不到它
猜你喜欢
  • 1970-01-01
  • 2015-05-24
  • 2021-01-26
  • 1970-01-01
  • 2013-05-27
  • 1970-01-01
  • 2016-06-04
  • 1970-01-01
  • 2011-08-26
相关资源
最近更新 更多