Usando RegEx no SQL Server

92

Estou procurando como substituir / codificar texto usando RegEx com base nas configurações / parâmetros RegEx abaixo:

RegEx.IgnoreCase = True     
RegEx.Global = True     
RegEx.Pattern = "[^a-z\d\s.]+"   

Eu vi alguns exemplos no RegEx, mas não sei como aplicá-lo da mesma forma no SQL Server. Quaisquer sugestões seriam úteis. Obrigado.

Control Freak
fonte
1
Olá, dê uma olhada neste artigo: codeproject.com/Articles/42764/…
Mohsen
Há também uma excelente solução TSQL + Windows API na Robyn Page e Phil Factor's que depende da classe VBScript.RegExp , que, acredito, é enviada em todas as versões do Windows desde o Windows 2000.
Julio Nobre
Se você absolutamente positivamente precisa RegEx via TSQL, uma opção para SQL Server 2016 e acima é para serviços de uso R .
Dave Mason,

Respostas:

103

Você não precisa interagir com o código gerenciado, pois pode usar LIKE :

CREATE TABLE #Sample(Field varchar(50), Result varchar(50))
GO
INSERT INTO #Sample (Field, Result) VALUES ('ABC123 ', 'Do not match')
INSERT INTO #Sample (Field, Result) VALUES ('ABC123.', 'Do not match')
INSERT INTO #Sample (Field, Result) VALUES ('ABC123&', 'Match')
SELECT * FROM #Sample WHERE Field LIKE '%[^a-z0-9 .]%'
GO
DROP TABLE #Sample

Como sua expressão termina com +você pode ir com'%[^a-z0-9 .][^a-z0-9 .]%'

EDIT : para deixar claro: o SQL Server não oferece suporte a expressões regulares sem código gerenciado. Dependendo da situação, o LIKEoperador pode ser uma opção, mas carece da flexibilidade que as expressões regulares fornecem.

Rubens Farias
fonte
8
@MikeYoung, você está certo. Esta resposta aborda incorretamente o +quantificador como {1,2}quando deveria considerá-lo {1, }. Surpreendentemente, isso funcionou para o OP.
Rubens Farias
2
Isso não funciona no servidor sql, pois não suporta regex.
VVN
10
@VVN, LIKEnão é regex (é uma sintaxe de correspondência de padrões mais limitada), portanto, a falta de suporte a regex não significa que isso não funcionará.
Charles Duffy de
@RubensFarias não seria bom atualizar a resposta à luz dos comentários de @mike-young?
Sudhanshu Mishra
8

Versão ligeiramente modificada da resposta de Julio.

-- MS SQL using VBScript Regex
-- select dbo.RegexReplace('aa bb cc','($1) ($2) ($3)','([^\s]*)\s*([^\s]*)\s*([^\s]*)')
-- $$ dollar sign, $1 - $9 back references, $& whole match

CREATE FUNCTION [dbo].[RegexReplace]
(   -- these match exactly the parameters of RegExp
    @searchstring varchar(4000),
    @replacestring varchar(4000),
    @pattern varchar(4000)
)
RETURNS varchar(4000)
AS
BEGIN
    declare @objRegexExp int, 
        @objErrorObj int,
        @strErrorMessage varchar(255),
        @res int,
        @result varchar(4000)

    if( @searchstring is null or len(ltrim(rtrim(@searchstring))) = 0) return null
    set @result=''
    exec @res=sp_OACreate 'VBScript.RegExp', @objRegexExp out
    if( @res <> 0) return '..VBScript did not initialize'
    exec @res=sp_OASetProperty @objRegexExp, 'Pattern', @pattern
    if( @res <> 0) return '..Pattern property set failed'
    exec @res=sp_OASetProperty @objRegexExp, 'IgnoreCase', 0
    if( @res <> 0) return '..IgnoreCase option failed'
    exec @res=sp_OAMethod @objRegexExp, 'Replace', @result OUT,
         @searchstring, @replacestring
    if( @res <> 0) return '..Bad search string'
    exec @res=sp_OADestroy @objRegexExp
    return @result
END

Você precisará do Ole Automation Procedures ativado no SQL:

exec sp_configure 'show advanced options',1; 
go
reconfigure; 
go
sp_configure 'Ole Automation Procedures', 1; 
go
reconfigure; 
go
sp_configure 'show advanced options',0; 
go
reconfigure;
go
Zachary Scott
fonte
2
BTW, é muito mais rápido destruir e recriar o objeto regex do que armazená-lo em cache e reutilizá-lo. Executamos 10.000 comparações com números significativamente maiores reutilizando o objeto.
Zachary Scott
8

Você terá que construir um procedimento CLR que fornece funcionalidade regex, como este artigo ilustra.

Sua função de exemplo usa VB.NET:

Imports System
Imports System.Data.Sql
Imports Microsoft.SqlServer.Server
Imports System.Data.SqlTypes
Imports System.Runtime.InteropServices
Imports System.Text.RegularExpressions
Imports System.Collections 'the IEnumerable interface is here  


Namespace SimpleTalk.Phil.Factor
    Public Class RegularExpressionFunctions
        'RegExIsMatch function
        <SqlFunction(IsDeterministic:=True, IsPrecise:=True)> _
        Public Shared Function RegExIsMatch( _
                                            ByVal pattern As SqlString, _
                                            ByVal input As SqlString, _
                                            ByVal Options As SqlInt32) As SqlBoolean
            If (input.IsNull OrElse pattern.IsNull) Then
                Return SqlBoolean.False
            End If
            Dim RegExOption As New System.Text.RegularExpressions.RegExOptions
            RegExOption = Options
            Return RegEx.IsMatch(input.Value, pattern.Value, RegExOption)
        End Function
    End Class      ' 
End Namespace

... e é instalado no SQL Server usando o seguinte SQL (substituindo '%' - variáveis ​​delimitadas por seus equivalentes reais:

sp_configure 'clr enabled', 1
RECONFIGURE WITH OVERRIDE

IF EXISTS ( SELECT   1
            FROM     sys.objects
            WHERE    object_id = OBJECT_ID(N'dbo.RegExIsMatch') ) 
   DROP FUNCTION dbo.RegExIsMatch
go

IF EXISTS ( SELECT   1
            FROM     sys.assemblies asms
            WHERE    asms.name = N'RegExFunction ' ) 
   DROP ASSEMBLY [RegExFunction]

CREATE ASSEMBLY RegExFunction 
           FROM '%FILE%'
GO

CREATE FUNCTION RegExIsMatch
   (
    @Pattern NVARCHAR(4000),
    @Input NVARCHAR(MAX),
    @Options int
   )
RETURNS BIT
AS EXTERNAL NAME 
   RegExFunction.[SimpleTalk.Phil.Factor.RegularExpressionFunctions].RegExIsMatch
GO

--a few tests
---Is this card a valid credit card?
SELECT dbo.RegExIsMatch ('^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$','4241825283987487',1)
--is there a number in this string
SELECT dbo.RegExIsMatch( '\d','there is 1 thing I hate',1)
--Verifies number Returns 1
DECLARE @pattern VARCHAR(255)
SELECT @pattern ='[a-zA-Z0-9]\d{2}[a-zA-Z0-9](-\d{3}){2}[A-Za-z0-9]'
SELECT  dbo.RegExIsMatch (@pattern, '1298-673-4192',1),
        dbo.RegExIsMatch (@pattern,'A08Z-931-468A',1),
        dbo.RegExIsMatch (@pattern,'[A90-123-129X',1),
        dbo.RegExIsMatch (@pattern,'12345-KKA-1230',1),
        dbo.RegExIsMatch (@pattern,'0919-2893-1256',1)
mwigdahl
fonte
Isso está no Classic ASP, é compatível? Acho que CLR é apenas para funções .NET, certo?
Control Freak
4
Os procedimentos CLR são instalados no ambiente do SQL Server e podem ser chamados como qualquer outro procedimento armazenado ou função definida pelo usuário, portanto, se o Classic ASP pode chamar um procedimento armazenado ou função definida pelo usuário, ele pode chamar um procedimento CLR.
mwigdahl
1
Embora este link possa responder à pergunta, é melhor incluir as partes essenciais da resposta aqui e fornecer o link para referência. As respostas somente com link podem se tornar inválidas se a página vinculada mudar. - Da avaliação
Federico klez Culloca
Obrigado @FedericoklezCulloca. Essa era uma resposta antiga e eu a atualizei de acordo.
mwigdahl
@mwigdahl obrigado por isso. Vejo que é antigo, mas apareceu em uma fila de revisão :)
Federico klez Culloca
8

Expressões regulares no uso de implementação de bancos de dados SQL Server

Expressão regular - descrição
. Corresponde a qualquer caractere
* Corresponde a qualquer caractere
+ Corresponde a pelo menos uma instância da expressão antes de
^ Comece no início da linha
$ Pesquisa no final da linha
< Corresponde apenas se a palavra começar neste ponto
> Corresponde apenas se a palavra parar neste ponto
\ n Corresponde a uma quebra de linha
[] Corresponde a qualquer caractere entre colchetes
[^ ...] Corresponde a qualquer caractere não listado após ^
[ABQ]% A string deve começar com as letras A, B ou Q e pode ter qualquer comprimento
[AB] [CD]% A string deve ter um comprimento de dois ou mais e deve começar com A ou B e ter C ou D como o segundo caractere
[AZ]% A string pode ter qualquer comprimento e deve começar com qualquer letra de A a Z
[A -Z0-9]% A string pode ter qualquer comprimento e deve começar com qualquer letra de A a Z ou numeral de 0 a 9
[^ AC]% A string pode ter qualquer comprimento, mas não pode começar com as letras de A a C
% [AZ] A string pode ter qualquer comprimento e deve terminar com qualquer uma das letras de A a Z
% [% $ # @]% A string pode ter qualquer comprimento e deve conter pelo menos um dos caracteres especiais entre o suporte

Ravi Makwana
fonte
5
SELECT * from SOME_TABLE where NAME like '%[^A-Z]%'

Ou alguma outra expressão em vez de AZ

Kalyan Vasanth
fonte
1

Uma abordagem semelhante à resposta de @mwigdahl, você também pode implementar um .NET CLR em C #, com código como;

using System.Data.SqlTypes;
using RX = System.Text.RegularExpressions;

public partial class UserDefinedFunctions
{
 [Microsoft.SqlServer.Server.SqlFunction]
 public static SqlString Regex(string input, string regex)
 {
  var match = RX.Regex.Match(input, regex).Groups[1].Value;
  return new SqlString (match);
 }
}

As instruções de instalação podem ser encontradas aqui

Fiach Reid
fonte