T-SQL PATINDEX Шаблон из семи символов, по крайней мере, из одной буквы и одного числа - PullRequest
0 голосов
/ 06 сентября 2018

Я хочу идентифицировать фрагмент из семи символов в тексте любой длины:

  • Начинается с буквы
  • Включает по крайней мере один номер (в любом месте)
  • Все буквы в верхнем регистре

Как бы я представлял этот тип паттерна с PATINDEX()? PATINDEX('%[A-Z]%',text) выполняет первое требование, но не выполняет другое требование. Как бы я сделал эту переменную так, чтобы цифры и буквы в семизначном пространстве могли быть перемешаны любым способом (после первого символа)?

Я использую это, чтобы распечатать чанк: SUBSTRING(MESSAGE_SUBJECT,PATINDEX('%[A-Z]%',MESSAGE_SUBJECT),7)

Не похоже, что это возможно без CLR. Чтобы сделать это еще проще, можно ли найти группу из семи символов, которая начинается с буквы и включает одно число?

Ответы [ 3 ]

0 голосов
/ 06 сентября 2018

Я не верю, что PATINDEX () даст вам то, что вам нужно. Функция PATINDEX () возвращает позицию первого вхождения, которая соответствует вашей строке. Я думаю, что вы были бы счастливее, используя функцию LIKE ().

0 голосов
/ 07 сентября 2018

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

Для идентификации фрагмента букв (AKA подстрока или, в контексте N-грамм , 7-грамм ), которые (1) начинаться с буквы, включает как минимум одно число и не содержит строчных букв, которые вы могли бы использовать 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
);
0 голосов
/ 06 сентября 2018

За мои комментарии выше ...

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
...