¡Hola, chicos! ¿Cómo estás?
Hoy el tema será uno de los más interesantes: ¡Expresiones regulares!

Introducción

Cualquiera que me haya seguido durante mucho tiempo sabe que siempre insistí en que la compatibilidad con expresiones regulares en SQL Server era una gran brecha. En 2018, escribí el artículo. SQL Server: cómo utilizar expresiones regulares (RegExp) en su base de datos, que detalla cómo sortear esta limitación usando LIKE desde DONDE (funciona, pero es limitado), SQLCLR, creando una DLL en C# e incrustándola dentro de la base de datos para tener el poder de expresión regular o usando Automatización OLE.

Funcionó durante años, pero siempre con esa “sobrecarga” de contexto entre el motor SQL y el CLR, además de restricciones de seguridad (PERMISSION_SET) que muchas veces impedían la implementación en entornos más restringidos, como Azure SQL Database.

Con la llegada de Servidor SQL 2025 y las actualizaciones de mayo de 2024 sobre Base de datos SQL de Azure, Microsoft finalmente escuchó a la comunidad y brindó soporte nativo para expresiones regulares. Hablamos de funciones integradas directamente en el motor, optimizadas y listas para usar sin necesidad de configuraciones externas.

En esta publicación, demostraré estas nuevas funciones, comprenderé la sintaxis y, por supuesto, las compararé con nuestro antiguo método a través de SQLCLR para ver qué cambia realmente en la vida del DBA y del desarrollador.

Vídeo sobre expresiones regulares:

Las nuevas funciones de expresión regular

Diferente al viejo COMO (que es extremadamente limitado), las nuevas funciones utilizan el estándar RE2, desarrollado por Google, que permite búsquedas complejas, extracciones y reemplazos de cadenas de una manera mucho más eficaz.

Las funciones introducidas, como podemos ver en documentación oficial, ellos son:

Funciones escalares:

  • REGEXP_LIKE: Valida si la cadena coincide con el patrón (devuelve BIT).
  • REGEXP_COUNT: Cuenta las apariciones del patrón.
  • REGEXP_INSTR: Encuentra la posición de un patrón.
  • REGEXP_REPLACE: Reemplaza patrones con nuevos textos.
  • REGEXP_SUBSTR: Extrae una parte de la cadena.

Funciones de tabla (conjuntos de filas):

  • REGEXP_MATCHES: Extrae todas las apariciones de un patrón y las devuelve como filas de una tabla, incluida la separación de los grupos de captura.
  • REGEXP_SPLIT_TO_TABLE: Divide una cadena en varias líneas usando un patrón Regex como delimitador (el sueño STRING_SPLIT).

Tabla de comparación de características

A continuación preparé una tabla comparativa para que sea más fácil ver cómo se comporta cada función en relación con parámetros comunes:

Función Tipo Objetivo principal Regreso principal
REGEXP_LIKE Trepar Validar si un texto sigue un patrón (Email, CPF, etc.) Booleano (1 o 0)
REGEXP_COUNT Trepar Contar cuántas veces aparece un patrón en un texto. Entero
REGEXP_INSTR Trepar Encuentra la posición inicial/final de un patrón Entero (Posición)
REGEXP_REPLACE Trepar Reemplazar apariciones de un patrón con texto nuevo VARCHAR/NVARCHAR
REGEXP_SUBSTR Trepar Extraiga una parte específica (subcadena) según el patrón VARCHAR/NVARCHAR
REGEXP_MATCHES Tabular Extraiga todas las ocurrencias y sus grupos de captura. Tabla (conjunto de filas)
REGEXP_SPLIT_TO_TABLE Tabular Divida una cadena en varias líneas usando Regex como delimitador Tabla (conjunto de filas)
Observación: Las funciones tabulares (REGEXP_MATCHES y REGEXP_SPLIT_TO_TABLE) se procesan a través de APLICACIÓN CRUZADA. Esto permite a SQL Server usar el paralelismo de manera mucho más eficiente que las antiguas funciones de división basadas en bucles o recursividad usando T-SQL puro, como la que compartí en el artículo. SQL Server: dividir cadenas en subcadenas usando una cadena dividida.
IMPORTANTE: La compatibilidad nativa requiere que el nivel de compatibilidad de la base de datos esté establecido en 160 (SQL Server 2022) o superior. Específicamente para utilizar la función REGEXP_LIKE, el nivel de compatibilidad debe ser 170 (SQL Server 2025). ¡Compruébelo siempre antes de realizar pruebas en producción!

Para comprobar el nivel de compatibilidad de sus bases de datos, puede utilizar el siguiente script T-SQL:

-- CONSULTA DE COMPATIBILIDADE DAS BASES DE DADOS
SELECT
    [A].[name]                AS [Nm_Banco_Dados],
    [A].[compatibility_level] AS [Nr_Nivel_Compatibilidade],
    CASE [A].[compatibility_level]
        WHEN 100 THEN 'SQL Server 2008 / 2008 R2'
        WHEN 110 THEN 'SQL Server 2012'
        WHEN 120 THEN 'SQL Server 2014'
        WHEN 130 THEN 'SQL Server 2016'
        WHEN 140 THEN 'SQL Server 2017'
        WHEN 150 THEN 'SQL Server 2019'
        WHEN 160 THEN 'SQL Server 2022'
        WHEN 170 THEN 'SQL Server 2025'
        ELSE 'Versão Desconhecida / Legacy'
    END                       AS [Ds_Versao_Correspondente]
FROM
    [sys].[databases] AS [A]
ORDER BY
    [A].[compatibility_level] DESC,
    [A].[name] ASC;

Si desea cambiar el nivel de compatibilidad a 170 (SQL Server 2025) y puede usar funciones de expresión regular, use el siguiente comando:

ALTER DATABASE [SeuBanco] SET COMPATIBILITY_LEVEL = 170

ATENCIÓN: Las funciones de expresión regular todavía tienen limitaciones con respecto a los tipos VARCHAR(MAX) y NVARCHAR(MAX). Si intenta utilizarlos, puede recibir un mensaje de error como este:
Mensaje 19304, Nivel 16, Estado 5, Línea 35
Actualmente, la función 'REGEXP_MATCHES' no admite entradas NVARCHAR(max)/VARCHAR(max).

¿Por qué RE2 y no PCRE o POSIX puros?

La mayoría de las bibliotecas Regex (como PCRE usada en PHP/Python o la propia biblioteca .NET que usamos en SQLCLR) usan un Retroceder.

Esto es poderoso pero peligroso para una base de datos. Si un desarrollador escribe una expresión regular mal formateada, puede causar lo que llamamos ReDoS (Denegación de servicio de expresión regular). El motor de búsqueda entra en un bucle exponencial de reintentos (retroceso catastrófico), bloquea un núcleo de CPU al 100% y puede reducir el rendimiento de toda la instancia.

EL RE2 fue diseñado para ser seguro:

  • Tiempo lineal: RE2 garantiza que el tiempo de ejecución es lineal con respecto al tamaño de la cadena de entrada ($O(n)$).
  • Previsibilidad: No permite retroceder, lo que significa que nunca "bloqueará" su servidor, sin importar cuán compleja sea la expresión.
  • Seguridad de la memoria: El consumo de memoria es controlado y finito, ideal para quienes gestionan Grupo de almacenamiento intermedio y Empleados de la memoria.

Cuando miramos el Tipos de espera, el uso de RE2 nativo tiende a centrar el esfuerzo exclusivamente en SOS_SCHEDULER_YIELD (si la consulta es demasiado larga) en lugar de bloquear recursos externos o generar eventos WAIT desde CLR_AUTO_EVENT.

¿Quieres más información?

Sintaxis básica:

Metacarácter Descripción
. Coincide con cualquier carácter (muchas aplicaciones excluyen los saltos de línea, y exactamente qué caracteres se consideran saltos de línea es específico del "sabor" de la expresión regular, la codificación de caracteres y la plataforma, pero es seguro asumir que el carácter de salto de línea está incluido). Dentro de las expresiones entre corchetes POSIX, el carácter de punto corresponde a un punto literal. Por ejemplo, ANTES DE CRISTO corresponde a “abc”, etc., pero [ANTES DE CRISTO] coincide sólo con “a”, “.” o “c”.
[ ] Una expresión entre corchetes. Coincide con un solo carácter contenido entre corchetes. Por ejemplo, [ABECEDARIO] corresponde a “a”, “b” o “c”, y [Arizona] especifica un rango que coincide con cualquier letra minúscula de “a” a “z”. Estas formas se pueden mezclar: [abcx-z] corresponde a “a”, “b”, “c”, “x”, “y” o “z”, así como [a-cx-z].

el personaje se trata como un carácter literal si es el último o el primer carácter entre corchetes: [ABECEDARIO-], [-ABECEDARIO]. el personaje ] se puede incluir en una expresión entre corchetes si es el primer carácter: []ABECEDARIO]. La expresión entre corchetes también puede contener clases de caracteres, clases de equivalencia y caracteres de clasificación.

[^] Coincide con un solo carácter que No está contenido entre corchetes. Por ejemplo, [^abc] coincide con cualquier carácter que no sea “a”, “b” o “c”, y [^az] coincide con cualquier carácter que no sea una letra minúscula de “a” a “z”. Estas formas se pueden mezclar: [^abcx-z] coincide con cualquier carácter excepto “a”, “b”, “c”, “x”, “y” o “z”.

el personaje se trata como un carácter literal si es el último carácter o el primer carácter después del ^: [^abc-], [^-abc]. el personaje ] se trata como un carácter literal si es el primer carácter después del ^: [^]abc]. La expresión también puede contener clases de caracteres, clases de equivalencia y caracteres de agrupación.

^ Coincide con la posición inicial dentro de la cadena, si es el primer carácter de la expresión regular.
$ Coincide con la posición final de la cadena si es el último carácter de la expresión regular.
* Coincide con el elemento anterior cero o más veces. Por ejemplo, abecedario coincide con “ac”, “abc”, “abbbc”, etc. [xyz]* coincide con “”, “x”, “y”, “z”, “zx”, “zyx”, “xyzzy”, etc.

Ejemplos:

  • .at coincide con cualquier cadena de tres caracteres que termine en "at", incluidos "hat", "cat" y "bat".
  • [hc]en Corresponde a “sombrero” y “gato”.
  • [^b]en coincide con todas las cadenas identificadas por .at, excepto “murciélago”.
  • ^[hc]en coincide con "sombrero" y "gato", pero sólo al principio de la cadena o línea.
  • [hc]en$ coincide con "sombrero" y "gato", pero solo al final de la cadena o línea.
  • \[.\] coincide con cualquier carácter rodeado por “[” y “]” ya que los corchetes tienen caracteres de escape; por ejemplo: “[a]” y “[b]”.

Clases POSIX:

clase POSIX Similar a Significado
[:superior:] [ARIZONA] Letras mayúsculas
[:más bajo:] [Arizona] Letras minúsculas
[:alfa:] [A-Za-z] Letras mayúsculas y minúsculas
[:dígito:] [0-9] Dígitos
[:xdígito:] [0-9A-Fa-f] Dígitos hexadecimales
[:alnum:] [A-Za-z0-9] Dígitos, letras mayúsculas y minúsculas
[:punto:] Puntuación (todos los caracteres gráficos excepto letras y dígitos)
[:blanco:] [\t] Espacio y pestaña
[:espacio:] [ \t\n\r\f\v] Caracteres de espacios en blanco (vacíos)
[:control:] personajes de control
[:gráfico:] Caracteres gráficos (excluye espacios)
[:imprimir:] [[:gráfico:] ] Caracteres imprimibles (incluye espacios)
ALERTA DE LIMITACIÓN TÉCNICA: Como RE2 prioriza la seguridad y el rendimiento lineal, NO SOPORTA algunas características comunes en otros motores, como Referencias anteriores (referencias anteriores como \1) y Mirar a tu alrededor (mirar hacia adelante/mirar hacia atrás). Si su script SQLCLR dependía de esto, deberá reconsiderar la lógica al migrar a nativo.

Ejemplos prácticos de cada función para expresión regular.

Para facilitar la comprensión, demostraré algunos ejemplos de cada función para comprender algunas aplicaciones útiles en la vida cotidiana.

REGEXP_LIKE

Valida si la cadena coincide con el patrón (devuelve BIT).

La sintaxis es REGEXP_LIKE (expresión_cadena, expresión_patrón [, banderas]), donde los valores de las banderas son:

  • i: No distingue entre mayúsculas y minúsculas (falso predeterminado)
  • m: Modo multilínea: “^” y “$” coinciden con la línea inicial/final además del texto inicial/final (falso predeterminado)
  • s: Permitir "." coincide con “\n” (falso predeterminado)
  • c: distingue entre mayúsculas y minúsculas (predeterminado verdadero)

Documentación oficial: REGEXP_LIKE

Ejemplo 1: Validar máscaras de códigos postales

SELECT
    -- Caso 1: Formato padrão 00000-000
    CASE 
        WHEN REGEXP_LIKE('29090-270', '^[0-9]{5}-[0-9]{3}$') THEN 1 
        ELSE 0 
    END AS Fl_Cep_Valido_1,

    -- Caso 2: Formato pontuado 00.000-000
    -- Nota: O caractere '.' precisa de escape (\\ ou \) pois em Regex ele significa "qualquer caractere"
    CASE 
        WHEN REGEXP_LIKE('29.090-270', '^[0-9]{2}\.[0-9]{3}-[0-9]{3}$') THEN 1 
        ELSE 0 
    END AS Fl_Cep_Valido_2,

    -- Caso 3: Formato apenas números 00000000
    CASE 
        WHEN REGEXP_LIKE('29090270', '^[0-9]{8}$') THEN 1 
        ELSE 0 
    END AS Fl_Cep_Valido_3

Ejemplo 2: validar si la máscara del código postal coincide con el valor probado

-- Objetivo: Validar máscara de CEP no formato 99999-999.

SELECT
    -- Caso 1: Válido (Atende exatamente ao padrão)
    CASE 
        WHEN REGEXP_LIKE('29090-270', '^[0-9]{5}-[0-9]{3}$') THEN 1 
        ELSE 0 
    END AS Fl_Cep_Valido1,

    -- Caso 2: Inválido (Possui ponto extra)
    CASE 
        WHEN REGEXP_LIKE('29.090-270', '^[0-9]{5}-[0-9]{3}$') THEN 1 
        ELSE 0 
    END AS Fl_Cep_Valido2,

    -- Caso 3: Inválido (Apenas números, sem o traço)
    CASE 
        WHEN REGEXP_LIKE('29090270', '^[0-9]{5}-[0-9]{3}$') THEN 1 
        ELSE 0 
    END AS Fl_Cep_Valido3;

-- Dica: Se você não estiver no SQL Server 2022, a alternativa nativa é o LIKE:
-- WHERE coluna LIKE '[0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9]'

Ejemplo 3: Validación de CPF y CNPJ

-- 1. VALIDAÇÃO DE CPF (999.999.999-99)
SELECT
    -- Exemplo 1: Formato Válido
    CASE 
        WHEN REGEXP_LIKE('123.456.789-09', '^\d{3}\.\d{3}\.\d{3}\-\d{2}$') THEN 1 
        ELSE 0 
    END AS CPF_Valido,

    -- Exemplo 2: Inválido (Letra X presente)
    CASE 
        WHEN REGEXP_LIKE('12X.456.789-09', '^\d{3}\.\d{3}\.\d{3}\-\d{2}$') THEN 1 
        ELSE 0 
    END AS CPF_Invalido_Caractere,

    -- Exemplo 3: Inválido (Sem máscara/apenas números)
    CASE 
        WHEN REGEXP_LIKE('12345678909', '^\d{3}\.\d{3}\.\d{3}\-\d{2}$') THEN 1 
        ELSE 0 
    END AS CPF_Invalido_Formato;

-- 2. VALIDAÇÃO DE CNPJ (99.999.999/9999-99)
SELECT
    -- Exemplo 1: Formato Válido
    CASE 
        WHEN REGEXP_LIKE('12.345.678/1234-09', '^\d{2}\.\d{3}\.\d{3}\/\d{4}\-\d{2}$') THEN 1 
        ELSE 0 
    END AS CNPJ_Valido,

    -- Exemplo 2: Inválido (Letra X presente)
    CASE 
        WHEN REGEXP_LIKE('12.3X5.678/1234-09', '^\d{2}\.\d{3}\.\d{3}\/\d{4}\-\d{2}$') THEN 1 
        ELSE 0 
    END AS CNPJ_Invalido_Caractere,

    -- Exemplo 3: Inválido (Sem máscara/apenas números)
    CASE 
        WHEN REGEXP_LIKE('12345678123409', '^\d{2}\.\d{3}\.\d{3}\/\d{4}\-\d{2}$') THEN 1 
        ELSE 0 
    END AS CNPJ_Invalido_Formato;

Ejemplo 4: búsqueda de palabras repetidas
Aquí nos encontramos con una barrera del dialecto RE2, que es la ausencia de Backtracking. Con la función SQLCLR, que usa el dialecto .NET, podría usar expresiones regulares para buscar palabras repetidas usando la expresión \1, como en el siguiente ejemplo:

-- Identificando palavras repetidas
SELECT 
    CLR.dbo.fncRegex_Match('Essa frase frase contém palavras repetidas', '\b(\w+)\s+\1\b'),
    CLR.dbo.fncRegex_Match('Essa frase NÃO contém palavras repetidas', '\b(\w+)\s+\1\b')

Si intentamos adaptar este código a una expresión regular nativa, se vería así:

-- Identificando palavras repetidas
SELECT 
    CASE WHEN REGEXP_LIKE('Essa frase frase contém palavras repetidas', '\b(\w+)\s+\1\b') THEN 1 ELSE 0 END,
    CASE WHEN REGEXP_LIKE('Essa frase NÃO contém palavras repetidas', '\b(\w+)\s+\1\b') THEN 1 ELSE 0 END

Y al intentar hacer esto generará un mensaje de error como este:

Mensaje 19300, Nivel 16, Estado 1, Línea 210
Se proporcionó un patrón no válido '\b(\w+)\s+\1\b'. Se produjo el error "secuencia de escape no válida: \1" durante la evaluación del patrón.

Usando el dialecto RE2, no es posible hacer lo mismo usando solo una expresión regular. En este caso, encontramos una limitación técnica del motor de procesamiento de expresiones regulares de SQL Server 2025 y Azure SQL Database.

Una alternativa para resolver este problema es usar STRING_SPLIT:

DECLARE @exemplo2 VARCHAR(2000) = 'nesse exemplo exemplo eu gostaria de eu demonstrar como identificar palavras palavras repetidas repetidas eu';

WITH Palavras AS (
    -- O parâmetro '1' habilita a coluna [value] e [ordinal] (posição)
    SELECT 
        value AS Palavra, 
        ordinal AS Posicao
    FROM
        STRING_SPLIT(@exemplo2, ' ', 1)
)
SELECT 
    p1.Palavra
FROM
    Palavras p1
    JOIN Palavras p2 ON p1.Posicao = p2.Posicao - 1 -- Compara com a próxima palavra
WHERE
    p1.Palavra = p2.Palavra
    AND p1.Palavra <> ''; -- Evita espaços vazios se houver espaços duplos

Ejemplo 5: busca nombres que contengan al menos un espacio entre caracteres alfabéticos

SELECT 
    City, 
    StateProvince
FROM
    SalesLT.Address
WHERE
    REGEXP_LIKE(City, '[[:alpha:]]+[[:space:]]+[[:alpha:]]+')
ORDER BY
    City;

Ejemplo 6: Ciudades que empiezan o terminan con vocales

SELECT DISTINCT 
    City
FROM
    SalesLT.Address
WHERE
    REGEXP_LIKE(City, '^[AEIOU].*[aeiou]$');

Ejemplo 7: Personas cuyo nombre tiene acentos o caracteres especiales

SELECT
    FirstName, 
    LastName
FROM
    SalesLT.Customer
WHERE
    REGEXP_LIKE(FirstName, '[^a-zA-Z ]')

Ejemplo 8: distingue entre mayúsculas y minúsculas y no distingue entre mayúsculas y minúsculas
De forma predeterminada, las expresiones regulares siempre distinguen entre mayúsculas y minúsculas, pero puede controlar este comportamiento mediante banderas.

Fuerce la consulta distinguiendo entre mayúsculas y minúsculas y busque “montaña”:

SELECT 
    Name, 
    ProductNumber
FROM
    SalesLT.Product
WHERE
    REGEXP_LIKE(Name, 'mountain', 'c'); -- 'c' força Case Sensitivity

Forzar consulta sin distinguir entre mayúsculas y minúsculas y buscar “montaña”:

SELECT 
    Name, 
    ProductNumber
FROM
    SalesLT.Product
WHERE
    REGEXP_LIKE(Name, 'mountain', 'i'); -- 'i' força Case IN-Sensitivity

Vale la pena recordar que REGEXP_LIKE busca cualquier parte de la cadena, no sólo el principio:

REGEXP_INSTR

Devuelve la posición inicial o final de la subcadena correspondiente, según el valor del argumento return_option.

La sintaxis es REGEXP_INSTR (expresión_cadena, expresión_patrón [, inicio [, aparición [, opción_retorno [, banderas [, grupo]]]]]]].

Documentación oficial: REGEXP_INSTR

Ejemplo 1: Recuperar pasajes donde aparece la palabra “Dirceu” en el texto

DECLARE @exemplo1 VARCHAR(2000) = 'Olá pessoal! Meu nome é Dirceu Resende e quero demonstrar como o Regexp é útil nesse artigo do Dirceu blog Resende do Wordpress';

WITH Trechos AS (
    -- Localiza a primeira ocorrência
    SELECT 
        REGEXP_INSTR(@exemplo1, 'Dirceu', 1, 1) AS Posicao,
        1 AS Ocorrencia
    UNION ALL
    -- Busca as próximas ocorrências recursivamente
    SELECT 
        REGEXP_INSTR(@exemplo1, 'Dirceu', Posicao + 1, 1),
        Ocorrencia + 1
    FROM Trechos
    WHERE REGEXP_INSTR(@exemplo1, 'Dirceu', Posicao + 1, 1) > 0
)
SELECT 
    -- Como não temos SUBSTR nativo com Regex, usamos SUBSTRING tradicional
    -- Pegando "Dirceu" + 25 caracteres para simular o contexto dos próximos termos
    '...' + SUBSTRING(@exemplo1, Posicao, 30) + '...' AS Ds_Texto
FROM Trechos;

Ejemplo 2: busque el número al final de la cadena que consta de 2 dígitos (Ej.: “42”, “58”, “70”)

-- O CHARINDEX não consegue achar "a posição do primeiro número que tenha exatamente 2 dígitos".
-- Ele acharia o '2' em '2005' ou o '1' em 'Road-150'.
SELECT 
    [Name],
    REGEXP_INSTR([Name], '\b[0-9]{2}\b') AS Pos_Dimensao_Exata
FROM
    SalesLT.[Product]
WHERE
    REGEXP_INSTR([Name], '\b[0-9]{2}\b') > 0
ORDER BY 
    NEWID()

Ejemplo 3: Posición de múltiples sufijos en direcciones

-- Queremos a posição de 'Drive', 'Court' ou 'Avenue', mas apenas se forem a última palavra.
-- Fazer isso com CHARINDEX exige inverter a string ou cálculos complexos de LEN.
SELECT 
    AddressLine1,
    REGEXP_INSTR(AddressLine1, '\b(Drive|Court|Avenue|St|St\.)\b$') AS Pos_Sufixo_Final
FROM
    SalesLT.Address
WHERE
    REGEXP_INSTR(AddressLine1, '\b(Drive|Court|Avenue|St|St\.)\b$') > 0;

Ejemplo 4: recuperar el nombre de la calle e ignorar el número

-- Localiza a posição da primeira LETRA que aparece após uma sequência de NÚMEROS.
-- Essencial para separar '1234 Main St' em '1234' e 'Main St'.
SELECT 
    AddressLine1,
    REGEXP_INSTR(AddressLine1, '[a-zA-Z]', 1, 1) AS Pos_Inicio_Nome_Rua,
    SUBSTRING(AddressLine1, REGEXP_INSTR(AddressLine1, '[a-zA-Z]', 1, 1), LEN(AddressLine1)) AS NomeRua
FROM
    SalesLT.Address
WHERE
    REGEXP_INSTR(AddressLine1, '^[0-9]+') > 0;

Ejemplo 5: Identificación de apartamentos o unidades

-- Localiza a posição de qualquer marcador de unidade (Apt, Unit, #, Ste)
-- O CHARINDEX precisaria de 4 buscas separadas com OR.
SELECT
    AddressLine1,
    REGEXP_INSTR(AddressLine1, '\b(Apt|Unit|#|Ste|Suite)\b', 1, 1, 0, 'i') AS Pos_Marcador_Unidade
FROM
    SalesLT.Address
WHERE
    REGEXP_INSTR(AddressLine1, '\b(Apt|Unit|#|Ste|Suite)\b', 1, 1, 0, 'i') > 0;

REGEXP_COUNT

Cuenta el número de veces que un patrón de expresión regular coincide en una cadena.

La sintaxis es REGEXP_COUNT (expresión_cadena, expresión_patrón [, inicio [, banderas]]), donde los valores de las banderas son:

  • i: No distingue entre mayúsculas y minúsculas (falso predeterminado)
  • m: Modo multilínea: “^” y “$” coinciden con la línea inicial/final además del texto inicial/final (falso predeterminado)
  • s: Permitir "." coincide con “\n” (falso predeterminado)
  • c: distingue entre mayúsculas y minúsculas (predeterminado verdadero)

Documentación oficial: https://learn.microsoft.com/pt-br/sql/t-sql/functions/regexp-count-transact-sql

Ejemplo 1: Cuente cuántas veces aparece la letra a en el nombre de cada producto.

SELECT 
    Name,
    REGEXP_COUNT( Name, 'a' ) AS A_COUNT
FROM
    SalesLT.Product
WHERE
    REGEXP_COUNT( Name, 'a' ) > 1;

Ejemplo 2: Devuelve productos que terminan en “tire” o “ube”, ignorando las letras mayúsculas y minúsculas.

SELECT 
    Name
FROM
    SalesLT.Product
WHERE
    REGEXP_COUNT(Name, 'tire|ube$', 1, 'i') > 0;

Ejemplo 3: contar el número de palabras y nombres con iniciales

SELECT
    FirstName + ' ' + LastName                              AS NomeCompleto,
    REGEXP_COUNT( FirstName + ' ' + LastName, '\w+' )       AS Qtd_Palavras,
    -- Conta quantas iniciais seguidas de ponto existem (Ex: "J. R. R. Tolkien")
    REGEXP_COUNT( FirstName + ' ' + LastName, '\b[A-Z]\.' ) AS Qtd_Iniciais
FROM
    SalesLT.Customer
WHERE
    REGEXP_COUNT( FirstName + ' ' + LastName, '\w+' ) > 2

REGEXP_REPLACE

Devuelve una cadena modificada, en la que se encontró la aparición del patrón de expresión regular. Si no se encuentra ninguna coincidencia, la función devuelve la cadena original.

La sintaxis es REGEXP_REPLACE( expresión_cadena, expresión_patrón [ , reemplazo_cadena [ , inicio [ , aparición [ , banderas ] ] ] ] ), donde los valores de las banderas son:

  • i: No distingue entre mayúsculas y minúsculas (falso predeterminado)
  • m: Modo multilínea: “^” y “$” coinciden con la línea inicial/final además del texto inicial/final (falso predeterminado)
  • s: Permitir "." coincide con “\n” (falso predeterminado)
  • c: distingue entre mayúsculas y minúsculas (predeterminado verdadero)

Documentación oficial: https://learn.microsoft.com/pt-br/sql/t-sql/functions/regexp-replace-transact-sql

Ejemplo 1: enmascarar teléfonos y correos electrónicos

SELECT
    Phone,
    
    -- Mascarar exatamente os primeiros 4 caracteres
    -- ^.{4} -> O início da string (^) seguido de qualquer caractere (.) repedido 4 vezes {4}
    REGEXP_REPLACE(Phone, '^.{4}', '****') AS Mascara_Telefone_Inicio,
    
    -- Mascarar exatamente os primeiros 4 NÚMEROS, mantendo formatação
    REGEXP_REPLACE(
        REGEXP_REPLACE(
            REGEXP_REPLACE(
                REGEXP_REPLACE(Phone, '\d', '*', 1, 1), -- Mascara o 1º número
            '\d', '*', 1, 1),                           -- Mascara o novo 1º número
        '\d', '*', 1, 1),                               -- E assim por diante...
    '\d', '*', 1, 1) AS Telefone_Mascarado_Inicio_Somente_Numero,

    REGEXP_REPLACE(Phone, '\d{4}$', '****') AS Mascara_Telefone_Final,

    EmailAddress,
    REGEXP_REPLACE(EmailAddress, '^[^@]+', 'XXXX') AS Email_Mascarado
FROM
    SalesLT.Customer

Ejemplo 2: devolver solo números de una cadena

-- Remove parênteses, traços e espaços de uma só vez para deixar apenas os dígitos.
-- Tente fazer isso com REPLACE comum e veja o tamanho do código!
SELECT 
    Phone,
    REGEXP_REPLACE(Phone, '[^0-9]', '') AS Telefone_Apenas_Digitos
FROM
    SalesLT.Customer

Ejemplo 3: reemplazar varias expresiones para normalizar un nombre

-- Exemplo 2: Substituindo múltiplos "ruídos" por um nome de categoria único
DECLARE @Descricao VARCHAR(100) = 'Este item é uma Bicycle, também chamada de Cycle ou Velo';

SELECT 
    @Descricao AS Original,
    -- Substitui qualquer uma das palavras no grupo por 'Bike'
    REGEXP_REPLACE(@Descricao, '\b(Bicycle|Cycle|Velo)\b', 'Bike', 1, 0, 'i') AS Descricao_Limpa;

Ejemplo 4: transformaciones diversas

-- 1. REGEXP_REPLACE substituirá tudo que NÃO é letra ([^A-Za-z]) por nada ('')
SELECT REGEXP_REPLACE('Protocolo #12345-ABC', '[^A-Za-z ]', '') AS Somente_Letras
   
-- 2. Usando classes POSIX para maior elegância e suporte a acentos (se necessário)
SELECT REGEXP_REPLACE('Protocolo #12345-ABC', '[^[:alpha:] ]', '') AS Somente_Letras_POSIX

-- 3. Retornando apenas números
SELECT REGEXP_REPLACE('Protocolo #12345-ABC', '[^0-9]', '') AS Somente_Numeros

-- 4. Retornando apenas os caracteres especiais
SELECT REGEXP_REPLACE('Protocolo #12345-ABC', '[[:alnum:]]', '') AS Somente_Caracteres_Especiais

Ejemplo 5: devolver texto sin etiquetas HTML

SELECT 
    REGEXP_REPLACE('<div><p>Olá</p> <span>Mundo</span></div>', '<[^>]+>', '') AS Texto_Sem_Html

REGEXP_SUBSTR

Devuelve una aparición de una cadena que coincide con el patrón de expresión regular. Si no se encuentra ninguna coincidencia, devuelve NULL.

La sintaxis es REGEXP_SUBSTR( expresión_cadena, expresión_patrón [ , reemplazo_cadena [ , inicio [ , aparición [ , banderas ] ] ] ] ), donde los valores de las banderas son:

  • i: No distingue entre mayúsculas y minúsculas (falso predeterminado)
  • m: Modo multilínea: “^” y “$” coinciden con la línea inicial/final además del texto inicial/final (falso predeterminado)
  • s: Permitir "." coincide con “\n” (falso predeterminado)
  • c: distingue entre mayúsculas y minúsculas (predeterminado verdadero)

Documentación oficial: https://learn.microsoft.com/pt-br/sql/t-sql/functions/regexp-substr-transact-sql

Ejemplo 1: recuperar partes de una cadena

-- 1. Extraindo o domínio de um e-mail (Tudo após o @)
DECLARE @email VARCHAR(100) = 'contato.diretoria@empresa.com.br'
SELECT REGEXP_SUBSTR(@email, '@[a-z0-9.-]+') AS Dominio

-- 2. Extraindo apenas os números de uma string suja
SELECT REGEXP_SUBSTR('Protocolo #12345-ABC', '[0-9]+') AS Somente_Numeros

-- 3. Extraindo a primeira palavra de uma frase
SELECT REGEXP_SUBSTR('Otimização de Performance SQL', '^\w+') AS Primeira_Palavra

-- 4. Extraindo a última palavra de uma frase
SELECT REGEXP_SUBSTR('SQL Server 2022 Release', '\w+$') AS Ultima_Palavra

-- 5. Extraindo versão de software (Padrão X.X.X)
DECLARE @log VARCHAR(100) = 'Build version 14.0.1000 patch applied'
SELECT REGEXP_SUBSTR(@log, '[0-9]+\.[0-9]+\.[0-9]+') AS Versao

Resultado:

Ejemplo 2: devolver un número compuesto por 2 dígitos en la cadena

SELECT
    Name,
    REGEXP_SUBSTR(Name, '\b[0-9]{2}\b') AS Tamanho_Extraido
FROM
    SalesLT.Product
WHERE
    REGEXP_LIKE(Name, '\b[0-9]{2}\b')

Ejemplo 3: extraer solo el nombre de usuario del correo electrónico

-- Usamos o 6º parâmetro para retornar apenas o que está dentro do primeiro parêntese
SELECT 
    EmailAddress,
    REGEXP_SUBSTR(EmailAddress, '^([^@]+)', 1, 1, 'i', 1) AS Usuario_Email
FROM
    SalesLT.Customer;

Mi recomendación al usar REGEXP_SUBSTR es: validar siempre NULL antes de procesar.

A diferencia del SUBSTRING tradicional, que puede devolver una cadena vacía o un error si los índices son incorrectos, REGEXP_SUBSTR devolverá NULL si no se encuentra el patrón. Utilice esto a su favor en sus consultas de limpieza: DONDE REGEXP_SUBSTR(columna, patrón) NO ES NULO

Esto garantiza que su extracción sea limpia y que no intente realizar operaciones matemáticas o JOIN en valores inexistentes.

REGEXP_MATCHES

Devuelve una tabla de subcadenas capturadas que coinciden con un patrón de expresión regular con una cadena. Si no se encuentra ninguna coincidencia, la función no devuelve filas.

La sintaxis es REGEXP_MATCHES (expresión_cadena, expresión_patrón [, banderas]), donde los valores de las banderas son:

  • i: No distingue entre mayúsculas y minúsculas (falso predeterminado)
  • m: Modo multilínea: “^” y “$” coinciden con la línea inicial/final además del texto inicial/final (falso predeterminado)
  • s: Permitir "." coincide con “\n” (falso predeterminado)
  • c: distingue entre mayúsculas y minúsculas (predeterminado verdadero)

Documentación oficial: https://learn.microsoft.com/pt-br/sql/t-sql/functions/regexp-matches-transact-sql

Ejemplo 1: Devolver una tabla con los hashtags de una oración.

-- Opção 1: Especificando manualmente o conjunto de caracteres
SELECT * FROM REGEXP_MATCHES ('Learning #AzureSQL #AzureSQLDB #SQL #SQLServer', '#([A-Za-z0-9_]+)');

-- Opção 2: Utilizando o padrão \w+ para capturar a palavra após o #
SELECT * FROM REGEXP_MATCHES('Learning #AzureSQL #AzureSQLDB #SQL #SQLServer', '#(\w+)');

Ejemplo 2: devolver una tabla de protocolos en la cadena

-- 2. Localizando todos os protocolos (Formato: 3 letras - 4 números)
-- Ex: ABC-1234, XYZ-9999
SELECT * FROM REGEXP_MATCHES('Protocolos: ABC-1234 e XYZ-9999', '[A-Z]{3}-[0-9]{4}');

Ejemplo 3: recuperar todos los correos electrónicos de una cadena

SELECT 
    m.match_value AS Email_Encontrado
FROM (VALUES ('Contatos: maria@corp.com; suporte@adv.br. Email do Dirceu: dirceu@dirceuresende.com')) AS t(Texto)
CROSS APPLY REGEXP_MATCHES(t.Texto, '[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}', 'i') m;

Ejemplo 4: consultas variadas

-- 1. IDENTIFICANDO TODOS OS VALORES MONETÁRIOS (R$, $)
SELECT
    m.match_value AS Preco_Detectado
FROM (VALUES ('Item A: R$ 150,00 | Item B: $ 20.00')) AS t(Texto)
CROSS APPLY REGEXP_MATCHES(t.Texto, '(R\$|\$)\s?[0-9,.]+') m;

-- 2. EXTRAINDO TODAS AS TAGS HTML (Nome da Tag)
-- Usamos grupos de captura para pegar apenas o que está dentro de < >
SELECT 
    m.match_value AS Tag_Nome
FROM (VALUES ('<div><p>Texto</p><span>Item</span></div>')) AS t(Html)
CROSS APPLY REGEXP_MATCHES(t.Html, '<([a-z1-6]+)>') m;

-- 3. LOCALIZANDO TODAS AS DATAS EM FORMATOS VARIADOS
SELECT 
    m.match_value AS Data_Encontrada
FROM (VALUES ('Início: 29/12/2025 - Fim: 2026-01-15')) AS t(Texto)
CROSS APPLY REGEXP_MATCHES(t.Texto, '\b(\d{2}/\d{2}/\d{4}|\d{4}-\d{2}-\d{2})\b') m;

-- 4. EXTRAINDO MENÇÕES DE USUÁRIOS (@usuario)
SELECT 
    m.match_value AS Usuario
FROM (VALUES ('Relatório enviado por @dirceu e revisado por @resende')) AS t(Msg)
CROSS APPLY REGEXP_MATCHES(t.Msg, '@(\w+)') m;

-- 5. ISOLANDO ENDEREÇOS IP DE UM LOG ESCALAR
DECLARE @log VARCHAR(100) = 'Erro de rede nos IPs 192.168.0.1 e 10.0.0.254';
SELECT match_value FROM REGEXP_MATCHES(@log, '\b(?:\d{1,3}\.){3}\d{1,3}\b');

Ejemplo 5: extraer colores de una cadena

SELECT 
    p.ProductID,
    p.Name AS NomeOriginal,
    m.match_value AS CorExtraida
FROM
    SalesLT.Product p
    CROSS APPLY REGEXP_MATCHES(p.Name, '\b(Black|Silver|Red|Blue|Yellow|White|Grey|Multi)\b', 'i') m;

REGEXP_SPLIT_TO_TABLE

Devuelve una tabla dividida de cadenas, delimitada por el patrón de expresiones regulares. Si no hay ninguna coincidencia con el patrón, la función devuelve la cadena.

La sintaxis es REGEXP_SPLIT_TO_TABLE( expresión_cadena, expresión_patrón [, banderas]), donde los valores de las banderas son:

  • i: No distingue entre mayúsculas y minúsculas (falso predeterminado)
  • m: Modo multilínea: “^” y “$” coinciden con la línea inicial/final además del texto inicial/final (falso predeterminado)
  • s: Permitir "." coincide con “\n” (falso predeterminado)
  • c: distingue entre mayúsculas y minúsculas (predeterminado verdadero)

Documentación oficial: https://learn.microsoft.com/pt-br/sql/t-sql/functions/regexp-split-to-table-transact-sql

Ejemplo 1: devolver una tabla con cada palabra del texto como una línea

SELECT *
FROM REGEXP_SPLIT_TO_TABLE ('Este artigo sobre expressões regulares ficou sensacional', '\s+');

Ejemplo 2: dividir el texto en varias líneas con varios delimitadores

SELECT 
    p.ProductID,
    p.Name AS NomeOriginal,
    s.value AS Atributo,
    s.ordinal AS Posicao
FROM
    SalesLT.Product p
    CROSS APPLY REGEXP_SPLIT_TO_TABLE(p.Name, '[- ,/]+') s;

Esta función es muy similar a la STRING_SPLIT:

Sin embargo, tiene una sutil diferencia, que es el hecho de que la función STRING_SPLIT acepta solo 1 carácter separador, mientras que REGEXP_SPLIT_TO_TABLE no tiene este límite.

Puedo separar mi cadena usando 3 caracteres como separador (-=-):

SELECT *
FROM REGEXP_SPLIT_TO_TABLE ('Este-=-artigo-=-sobre-=-expressões-=-regulares-=-ficou-=-sensacional', '-=-');

Pero al intentar hacer lo mismo usando STRING_SPLIT, nos encontramos con este mensaje de error:

SELECT *
FROM STRING_SPLIT('Este-=-artigo-=-sobre-=-expressões-=-regulares-=-ficou-=-sensacional', '-=-', 1)

Mensaje 214, Nivel 16, Estado 11, Línea 1
El procedimiento espera el parámetro 'separador' de tipo 'nchar(1)/nvarchar(1)'.

Comparación: nativo vs SQLCLR

Ahora llegamos al punto que todos querían saber: ¿Vale la pena migrar lo que ya tenemos en SQLCLR a funciones nativas?

Sintaxis y mantenimiento

En SQLCLR, cada función debía asignarse a un método de C#. Si necesitaba un nuevo comportamiento, necesitaba volver a compilar la DLL e implementarla en el servidor (a menudo dependiendo de los privilegios del usuario). administrador de sistemas) y espero que no haya problemas con la versión de .NET Framework. Con funciones nativas, la sintaxis es T-SQL estándar. Cualquier desarrollador de SQL puede leer y comprender lo que sucede sin tener que abrir un proyecto en Visual Studio.

Tipos de espera de recursos y rendimiento

Cuando usamos SQLCLR, SQL Server necesita administrar el Dominio de aplicación y realizar una “clasificación” de datos entre el motor relacional y el CLR. Esto genera esperas de procesamiento que a menudo se traducen en CLR_AUTO_EVENT ou CLR_MANUAL_EVENT.

Con funciones nativas:

  • UPC: El procesamiento se produce dentro del propio motor de ejecución de SQL, lo que permite un mejor paralelismo.
  • Memoria: No es necesario reservar memoria adicional para CLR Garbage Collector.
  • IOPS: Aunque el impacto directo en IO es menor, la eficiencia en la lectura de columnas LOB (VARCHAR(MAX)) con expresiones regulares nativas es superior ya que no requiere copiar los datos al entorno administrado.

Un punto de atención: Tanto SQLCLR como Native Regex no son SARGABLES. Es decir, si pones un DONDE REGEXP_LIKE(…) en una columna, SQL Server probablemente hará un Escaneo de índice ou Escaneo de mesa. La expresión regular se procesa línea por línea.

Además, al utilizar REGEXP_MATCHES ou REGEXP_SPLIT_TO_TABLE, SQL Server no necesita realizar "Marshaling" (copiar datos) al entorno SQLCLR .NET y esto mejora significativamente el rendimiento de la función nativa.

Comparación de rendimiento entre SQLCLR y funciones nativas:

Si desea crear las funciones en su entorno, simplemente ejecute el siguiente script:

CREATE ASSEMBLY [Regexp]
FROM 0x4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C0103008C57115B0000000000000000E00022200B013000000E00000006000000000000BE2C00000020000000400000000000100020000000020000040000000000000006000000000000000080000000020000000000000300608500001000001000000000100000100000000000001000000000000000000000006C2C00004F00000000400000A002000000000000000000000000000000000000006000000C000000342B00001C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E74657874000000C40C000000200000000E000000020000000000000000000000000000200000602E72737263000000A0020000004000000004000000100000000000000000000000000000400000402E72656C6F6300000C0000000060000000020000001400000000000000000000000000004000004200000000000000000000000000000000A02C00000000000048000000020005004C220000E808000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001B3002006A00000001000011000F00280500000A2D090F01280500000A2B01170A062C087E0600000A0B2B4800000F00280700000A0F01280700000A280800000A0C086F0900000A2D077E0600000A2B16086F0A00000A166F0B00000A6F0C00000A280D00000A0BDE0A0D007E0600000A0BDE00072A000001100000000021003D5E000A090000011B3003005900000002000011000F00280500000A2D120F01280500000A2D090F02280500000A2B01170A062C087E0600000A0B2B2E00000F00280700000A0F01280700000A0F02280700000A280E00000A280D00000A0BDE0A0C007E0600000A0BDE00072A0000000110000000002A00234D000A090000011B3003008D0000000300001100730F00000A0A02281000000A2D0803281000000A2B01170D092C050613042B69170B020316281100000A0C00086F1200000A13052B2D11056F1300000A1306000607281400000A11066F1500000A280D00000A73060000066F1600000A260717580B0011056F1700000A2DCADE1611057510000001130711072C0811076F1800000A00DC0613042B0011042A00000001100000020035003A6F001600000000133002002100000004000011000274030000020A03067B01000004811100000104067B0200000481070000012A2202281900000A002A5E02281900000A000002037D0100000402047D020000042A000042534A4201000100000000000C00000076342E302E33303331390000000005006C000000C8020000237E0000340300002403000023537472696E6773000000005806000004000000235553005C0600001000000023475549440000006C0600007C02000023426C6F620000000000000002000001571502000902000000FA01330016000001000000170000000300000002000000060000000C00000019000000060000000400000001000000030000000100000000005A010100000000000600F00024020600100124020600C70011020F00440200000600DB0279010A00DB00C8010A0038015B020E00540170020600A00179010A007F00C8010A007900C80106009F008F020600FA028F020E00800170020600F1018F020600AB0079010A0001005B020E000D0370020E00BB0170020E00900170020E00B70070020600440179010E00B7027002000000000A00000000000100010001001000A2020000150001000100030010006E000000150001000600060013008D000600AA011E0050200000000096004B0191000100D8200000000096008E009A00030050210000000096005C00A5000600FC210000000094004800AC00080029220000000086180B0206000B0032220000000086180B02B7000B0000000100AA0100000200240000000100AA0100000200240000000300E30100000100AA01000002002400000001006B00020002001C0002000300B301000001001C0000000200B30109000B02010011000B02060019000B020A0031000B020600390065011A0039006B011E0039002E0122009100540126009900CF021A004100C4022D00A10070013200A9002E0122003900E202380091009700460069000B020600B10013035D009100530262007100FD016B007900EE0270008900E2027400290042012200690044007A00790004031A008100BF00060029000B02060020002300F0002E000B00BF002E001300C8002E001B00E70040002300F00060002300F50010003E004D007F00048000000000000000000000000000000000C101000004000000000000000000000084003B000000000004000000000000000000000084002F000000000004000000000000000000000084007901000000000300020000000053716C496E743332003C4D6F64756C653E004E725F4C696E6861006E724C696E68610044735F4D6173636172610053797374656D2E44617461006D73636F726C6962004164640046696C6C526F775F5265676578705F46696E6400666E635265676578705F46696E64006F626A52656765787046696E640053797374656D446174614163636573734B696E6400666E6352656765785F5265706C6163650049456E756D657261626C650049446973706F7361626C65004361707475726500446973706F73650044656275676761626C654174747269627574650053716C46756E6374696F6E41747472696275746500436F6D70696C6174696F6E52656C61786174696F6E734174747269627574650052756E74696D65436F6D7061746962696C697479417474726962757465006765745F56616C75650053716C537472696E6700546F537472696E6700666E6352656765785F4D61746368005265676578702E646C6C006765745F49734E756C6C006765745F4974656D0053797374656D004D61746368436F6C6C656374696F6E0047726F7570436F6C6C656374696F6E00457863657074696F6E0044735F546578746F006473546578746F0047726F757000526567657870004D6963726F736F66742E53716C5365727665722E5365727665720044735F537562737469747569720049456E756D657261746F7200476574456E756D657261746F72002E63746F720053797374656D2E446961676E6F73746963730053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300446562756767696E674D6F646573004D6174636865730053797374656D2E446174612E53716C54797065730053797374656D2E546578742E526567756C617245787072657373696F6E730053797374656D2E436F6C6C656374696F6E730055736572446566696E656446756E6374696F6E730052656765784F7074696F6E73006765745F47726F757073006765745F53756363657373004F626A656374006F705F496D706C69636974006765745F43757272656E740041727261794C697374004D6F76654E6578740052656765780049734E756C6C4F72456D70747900000000000000000C14EAF7F46A2245B903BE327055CEBE0004200101080320000105200101111109070402111D12211225032000020306111D0320000E06000212210E0E0420001251052001124D08050001111D0E07070302111D12250600030E0E0E0E0F07081235081239021231123D1C1241040001020E08000312390E0E115D042000123D0320001C050001114508042001081C040701120C08B77A5C561934E08903061145080002111D111D111D0A0003111D111D111D111D06000212310E0E0A0003011C10114510111D072002011145111D0801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F7773010801000701000000000401000000818501000400540E1146696C6C526F774D6574686F644E616D651346696C6C526F775F5265676578705F46696E64540E0F5461626C65446566696E6974696F6E244E725F4C696E686120494E542C2044735F546578746F204E56415243484152284D4158295455794D6963726F736F66742E53716C5365727665722E5365727665722E446174614163636573734B696E642C2053797374656D2E446174612C2056657273696F6E3D342E302E302E302C2043756C747572653D6E65757472616C2C205075626C69634B6579546F6B656E3D623737613563353631393334653038390A446174614163636573730100000054557F4D6963726F736F66742E53716C5365727665722E5365727665722E53797374656D446174614163636573734B696E642C2053797374656D2E446174612C2056657273696F6E3D342E302E302E302C2043756C747572653D6E65757472616C2C205075626C69634B6579546F6B656E3D623737613563353631393334653038391053797374656D4461746141636365737301000000000000008C57115B00000000020000001C010000502B0000500D000052534453A6F053CD17E4E94A923D821619E3D25F01000000433A5C55736572735C646966696C5C446F63756D656E74735C56697375616C2053747564696F20323031375C50726F6A656374735C5265676578705C5265676578705C6F626A5C44656275675C5265676578702E7064620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000942C00000000000000000000AE2C0000002000000000000000000000000000000000000000000000A02C0000000000000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF25002000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100100000001800008000000000000000000000000000000100010000003000008000000000000000000000000000000100000000004800000058400000440200000000000000000000440234000000560053005F00560045005200530049004F004E005F0049004E0046004F0000000000BD04EFFE00000100000000000000000000000000000000003F000000000000000400000002000000000000000000000000000000440000000100560061007200460069006C00650049006E0066006F00000000002400040000005400720061006E0073006C006100740069006F006E00000000000000B004A4010000010053007400720069006E006700460069006C00650049006E0066006F0000008001000001003000300030003000300034006200300000002C0002000100460069006C0065004400650073006300720069007000740069006F006E000000000020000000300008000100460069006C006500560065007200730069006F006E000000000030002E0030002E0030002E003000000036000B00010049006E007400650072006E0061006C004E0061006D00650000005200650067006500780070002E0064006C006C00000000002800020001004C006500670061006C0043006F0070007900720069006700680074000000200000003E000B0001004F0072006900670069006E0061006C00460069006C0065006E0061006D00650000005200650067006500780070002E0064006C006C0000000000340008000100500072006F006400750063007400560065007200730069006F006E00000030002E0030002E0030002E003000000038000800010041007300730065006D0062006C0079002000560065007200730069006F006E00000030002E0030002E0030002E00300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000C000000C03C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
WITH PERMISSION_SET = SAFE
GO

CREATE FUNCTION [dbo].[fncRegex_Match] (@Ds_Texto [nvarchar](MAX), @Ds_Mascara [nvarchar](MAX))
RETURNS [nvarchar](MAX)
AS EXTERNAL NAME [Regexp].[UserDefinedFunctions].[fncRegex_Match];
GO

CREATE FUNCTION [dbo].[fncRegex_Replace] (@Ds_Texto [nvarchar](MAX), @Ds_Mascara [nvarchar](MAX), @Ds_Substituir [nvarchar](MAX))
RETURNS [nvarchar](MAX)
AS EXTERNAL NAME [Regexp].[UserDefinedFunctions].[fncRegex_Replace];
GO

CREATE FUNCTION [dbo].[fncRegexp_Find](@Ds_Texto [nvarchar](max), @Ds_Mascara [nvarchar](max))
RETURNS  TABLE (
    [Nr_Linha] [int] NULL,
    [Ds_Texto] [nvarchar](max) NULL
) WITH EXECUTE AS CALLER
AS 
EXTERNAL NAME [Regexp].[UserDefinedFunctions].[fncRegexp_Find]
GO

Para conocer más sobre estas funciones y su sintaxis accede al post SQL Server: cómo utilizar expresiones regulares (RegExp) en su base de datos.
CONSEJO DE DBA: Para ganar rendimiento, utilice Regex junto con filtros simples.

Por ejemplo: DONDE [Columna] COMO '192%' Y REGEXP_LIKE([Columna], 'patrón_complejo'). Simple LIKE realiza el primer filtro usando el índice y Regex solo procesa lo que queda.

Para demostrar que LIKE es más rápido que REGEXP, incluso usando LIKE '%texto%', podemos usar el siguiente script:

SELECT 
    Name, 
    ProductNumber
FROM
    SalesLT.Product
WHERE
    REGEXP_LIKE(Name, 'mountain', 'i'); -- 'c' força Case In-Sensitivity
    
SELECT 
    Name, 
    ProductNumber
FROM
    SalesLT.Product
WHERE
    Name LIKE '%mountain%' collate SQL_Latin1_General_CP1_CI_AI -- Forçar ser Case In-Sensitivity

Resultado:

Seguridad

Esta es la mayor ganancia. Muchas instancias de Instancia administrada de Azure SQL o entornos locales extremadamente bloqueados no le permiten ejecutar clr habilitado. Con las funciones nativas, esta barrera desaparece. Tienes el poder de Regex manteniendo la instancia en CONFIGURACIÓN DE SUPERFICIE seguro.

CONSEJO DE RENDIMIENTO: Aunque es nativo, Regex es costoso para la CPU. Evite el uso de funciones Regex en cláusulas DÓNDE de tablas con millones de filas sin filtros previos que utilicen índices. ¡Native Regex no hace que tu búsqueda sea “Sargable” mágicamente! El índice sigue siendo tu mejor amigo.

Conclusión

La introducción de funciones Regex nativas en SQL Server 2025 es un hito de madurez para la plataforma. Eliminan la necesidad de “soluciones alternativas” técnicas con SQLCLR para las tareas diarias de manipulación de cadenas y proporcionan un rendimiento mucho más predecible e integrado. Si está iniciando un nuevo proyecto o planea actualizar a SQL 2025, utilizar estas funciones es la ruta oficial y recomendada.

Espero que te haya gustado este tip, un fuerte abrazo y ¡hasta la próxima!