Hola, chicos,
¡Buen día!
En esta publicación, que será la número 100 del blog, me gustaría hablar sobre algo que me gusta particularmente de SQL Server, que es la creación de rutinas .NET dentro de la base de datos de SQL Server. Sí, estamos hablando del CLR (Common Language Runtime).
Introducción
Haga clic aquí para verEn otras palabras, el CLR permite crear rutinas (procedimientos almacenados, funciones, disparadores, etc.) escritas en C#, F# y VB.NET, compilarlas y ejecutarlas en la base de datos de forma nativa, ampliando las capacidades del DBMS, ya que es posible crear multitud de cosas que no serían posibles usando solo Transact-SQL, como manipulación de archivos, carga y descarga de archivos vía FTP, funciones de agregación, integración con Webservices y mucho más.
Ventajas y desventajas
Haga clic aquí para verVentajas de SQL CLR
- Posibilidad de crear nuevos recursos, lo que no sería posible usando solo T-SQL
- Posibilidad de trabajar con expresiones regulares (RegExp) en la base de datos.
- Optimización del rendimiento: Una misma función escrita en C# en el CLR generalmente se ejecuta mucho más rápido que una función T-SQL, especialmente en casos de bucles y cálculos, ya que el compilador .NET está especializado para este tipo de operaciones, mientras que el compilador T-SQL es para trabajar con conjuntos. En mi trabajo, he visto varios casos en los que la misma función CLR se realizó 5 veces, 10 veces y algunos incluso 60 veces más rápido que la función T-SQL.
- Integración con Webservices vía base de datos.
- Seguridad: Una cosa interesante que me gusta de CLR es que podemos definir un usuario fijo para conectarse a la base de datos. De esta forma podremos liberar acceso para que ejecute SP’s y consulte vistas y tablas del sistema y cree funciones y SP’s en el CLR para ello. Cuando un analista necesita utilizar estos SP del sistema, simplemente libere el acceso al SP/Vista/Función del CLR, que tendrá acceso indirecto al objeto del sistema, sin necesidad de liberar el acceso al mismo en el objeto fuente o crear objetos en los bancos del sistema.
- Herramientas de desarrollo: La herramienta utilizada para desarrollar rutinas CLR es Visual Studio. Management Studio es un IDE muy bueno para crear rutinas Transact-SQL, especialmente con SQL Prompt instalado, pero no se compara con el poderoso Visual Studio, especialmente con ReSharper. La programación es mucho más rápida y práctica.
- Versionado del código fuente: Debido a que utilizamos Visual Studio, el código fuente puede ser fácilmente controlado y administrado por TFS (Team Foundation Server), dando control total sobre los códigos creados, a diferencia de los Procedimientos Almacenados en la base de datos, que no tienen controles como Merge, Diff, etc.
- Reemplazo de xp_cmdshell: A pesar de estar deshabilitado por defecto, muchas personas y empresas terminan habilitando cmdshell en sus instancias, incluso en producción, ya que algunas operaciones no se pueden realizar usando solo Transact-SQL, como la manipulación de archivos, por ejemplo. Esta característica es un gran peligro ya que simplemente ejecuta cualquier comando que se le envía sin ningún filtro ni restricción. Para ello, recomiendo desactivar esta función y utilizar procedimientos CLR diseñados exclusivamente para cada propósito, ya sea copiar archivos o incluso cargar una instancia.
- Reemplazo de OLE Automation: Una característica que todavía se usa ampliamente y también está deshabilitada de manera predeterminada, los procedimientos de OLE Automation son bibliotecas de C++ que le permiten usar las API de Windows para realizar diversas operaciones, como manipulación de archivos, etc. El gran problema es que al habilitar esta característica, cualquier usuario puede crear cualquier cosa con ella, combinado con el hecho de que los comandos no se administran y se ejecutan dentro del proceso de SQL Server. En caso de falla, la instancia se cierra, ya que el sistema operativo cierra automáticamente el proceso de SQL Server (!!!!)
- Automatización: Con los procedimientos CLR puedes automatizar multitud de procesos del día a día, que antes solo se podían automatizar usando Integration Services, que es una gran herramienta, pero termina quedando un poco limitado en el mundo de las posibilidades de CLR, ya que en MSIS solo tienes los recursos que las herramientas ponen a tu disposición, mientras que en CLR puedes crear cualquier cosa que la plataforma .NET te permita. Además, el resultado del CLR son objetos de la base de datos, ya sean Productos Almacenados, Funciones, Triggers, etc., que pueden usarse libremente en otros SP's, Jobs y cualquier otro objeto de la base de datos, mientras que los Paquetes solo pueden ser ejecutados por la herramienta o por los Jobs. (Nota: CLR y MSIS son herramientas con objetivos diferentes, solo las comparé porque algunas tareas de Integration Services pueden ser reemplazadas fácilmente por CLR)
- Conectividad: Posibilidad de utilizar conectores .NET Framework y acceder a otros DBMS y otras instancias con un enlace directo, sin necesidad de un LinkedServer, que ejecuta el comando de forma remota
Desventajas de SQL CLR
- Necesidad de conocer SQL y lenguaje de programación (C#, F# o VB.net)
- Poca documentación y gente con conocimientos sobre el tema.
- Al publicar una nueva versión, los objetos se eliminan y se vuelven a crear, perdiendo permisos y haciendo que los objetos no estén disponibles durante la publicación.
- Si está mal desarrollado e implementado, puede presentar riesgos para el DBMS.
- Algunas funciones pueden requerir un alto volumen de CPU para su procesamiento
- No hay parámetros opcionales para los procedimientos. Todo debe estar completo.
Habilitando el CLR en su instancia de SQL Server
Haga clic aquí para versp_configure 'clr enabled'
GO
sp_configure 'clr enabled', 1
GO
RECONFIGURE
GO
sp_configure 'clr enabled'
GO
Y tendremos el siguiente resultado:

De lo contrario, encontrará este mensaje de error al intentar utilizar CLR:
Mensaje 6263, Nivel 16, Estado 1, Línea 2
La ejecución de código de usuario en .NET Framework está deshabilitada. Habilite la opción de configuración “clr habilitado”.
Creando su primer proyecto SQL CLR en Visual Studio
Haga clic aquí para verDespués de eso, abra Visual Studio y acceda al menú Archivo > Nuevo > Proyecto. Seleccione el tipo de proyecto SQL Server > Proyecto de base de datos de SQL Server
Y inicialmente debería verse así:

Ahora agreguemos un nuevo proyecto de biblioteca de clases, que contendrá nuestros códigos C#. Para hacer esto, haga clic derecho en la Solución y seleccione la opción Agregar > Nuevo proyecto. Seleccione la categoría Visual C# > Windows > Biblioteca de clases

Una vez creado, generalmente creo directorios por tipo de objeto en la Biblioteca de clases para organizar mejor el código. Esto es recomendado pero opcional. Es posible que desee organizar su código por tema o según sus necesidades.
Es posible que se eliminen algunas referencias ya que no se utilizarán en los ejemplos. Haga clic derecho en "Referencias" en el proyecto CLR y seleccione la opción "Agregar referencia ...". En la pantalla que se abrirá, seleccione la categoría Proyectos > Soluciones y marque la casilla de verificación del proyecto Biblioteca de clases:
No olvide configurar el permiso del proyecto que se importó, como se muestra en la siguiente figura:

El Explorador de soluciones debería verse similar a lo siguiente:

Creando un procedimiento almacenado sin retorno
- Haga clic derecho en el directorio “Procedimientos” del proyecto Bibliotecas y seleccione la opción Agregar > Clase…. En la pantalla que se abre, escriba el nombre del archivo que se creará. Normalmente pongo el mismo nombre que el objeto que se creará en la base de datos. En este ejemplo, crearé el archivo stpCopia_Arquivo.cs
- Copie y pegue el siguiente código:
using System.IO; public partial class StoredProcedures { [Microsoft.SqlServer.Server.SqlProcedure] public static void stpCopia_Arquivo(string origem, string destino, bool sobrescrever) { File.Copy(origem, destino, sobrescrever); } }
Crear un procedimiento almacenado con retorno de una selección
- Haga clic derecho en el directorio “Procedimientos” del proyecto Bibliotecas y seleccione la opción Agregar > Clase…. En la pantalla que se abre, escriba el nombre del archivo que se creará. Normalmente pongo el mismo nombre que el objeto que se creará en la base de datos. En este ejemplo, crearé el archivo stpImporta_Txt.cs
- Copie y pegue el siguiente código:
using System.Data; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; using System.IO; public partial class StoredProcedures { [Microsoft.SqlServer.Server.SqlProcedure] public static void stpImporta_Txt(SqlString caminho) { if (caminho.IsNull) return; using (var sr = new StreamReader(caminho.Value)) { // Define um novo objeto de metadados com 2 colunas var colunas = new SqlMetaData[2]; // Define as colunas de retorno colunas[0] = new SqlMetaData("Nr_Linha", SqlDbType.Int); colunas[1] = new SqlMetaData("Ds_Linha", SqlDbType.NVarChar, 1024); var rec = new SqlDataRecord(colunas); // inicia o envio dos dados SqlContext.Pipe.SendResultsStart(rec); var contador = 1; while (sr.Peek() >= 0) { // define as colunas rec.SetInt32(0, contador); rec.SetString(1, sr.ReadLine()); // Envia o registro para o banco de dados SqlContext.Pipe.SendResultsRow(rec); contador++; } // finaliza o envio dos dados SqlContext.Pipe.SendResultsEnd(); } } }
Creando una función escalar
- Haga clic derecho en el directorio “Funciones” > “Función escalar” del proyecto Bibliotecas (Biblioteca de clases) y seleccione la opción Agregar > Clase…. En la pantalla que se abre, escriba el nombre del archivo que se creará. Normalmente pongo el mismo nombre que el objeto que se creará en la base de datos. En este ejemplo crearé el archivo fncArquivo_Existe.cs
- Copie y pegue el siguiente código:
using System.Data.SqlTypes; using System.IO; public partial class UserDefinedFunctions { [Microsoft.SqlServer.Server.SqlFunction] public static SqlBoolean fncArquivo_Existe(SqlString Ds_Arquivo) { return (File.Exists(Ds_Arquivo.ToString())); } } - Tenga en cuenta que, a diferencia de los procedimientos que siempre devuelven void, las funciones deben devolver datos. En el caso del ejemplo, devuelven datos de tipo SqlBoolean (bit = true/false)
Crear una función con valores de tabla
- Haga clic derecho en el directorio “Procedimientos” del proyecto Bibliotecas y seleccione la opción Agregar > Clase…. En la pantalla que se abre, escriba el nombre del archivo que se creará. Normalmente pongo el mismo nombre que el objeto que se creará en la base de datos. En este ejemplo, crearé el archivo fncArquivo_Ler.cs
- Copie y pegue el siguiente código:
using System.IO; using System.Collections; using System.Data.SqlTypes; public partial class UserDefinedFunctions { // Classe que irá receber os dados processados através do construtor da Classe private class ArquivoLer { public SqlInt32 Nr_Linha; public SqlString Ds_Texto; public ArquivoLer(SqlInt32 nrLinha, SqlString dsTexto) { Nr_Linha = nrLinha; Ds_Texto = dsTexto; } } // Definição da função, contendo o método de preenchimento dos registros (FillRow) // e o retorno para o banco de dados [Microsoft.SqlServer.Server.SqlFunction( FillRowMethodName = "FillRow_Arquivo_Ler", TableDefinition = "Nr_Linha INT, Ds_Texto NVARCHAR(MAX)" /*, DataAccess = DataAccessKind.Read // Caso a função realize consultas no banco */ )] public static IEnumerable fncArquivo_Ler(string Ds_Caminho) { var ArquivoLerCollection = new ArrayList(); if (string.IsNullOrEmpty(Ds_Caminho)) return ArquivoLerCollection; var contador = 1; using (var sr = new StreamReader(Ds_Caminho)) { while (sr.Peek() >= 0) { ArquivoLerCollection.Add(new ArquivoLer( contador, sr.ReadLine() )); contador++; } sr.Close(); } return ArquivoLerCollection; } /* Método de preenchimento dos dados. O parâmetro de entrada é sempre o objeto da classe criada no início do código e as variáveis que serão retornadas, todas como OUTPUT, pois seus valores originais serão alterados para o método IEnumerable que controla a função. */ public static void FillRow_Arquivo_Ler(object objArquivoLer, out SqlInt32 nrLinha, out SqlString dsTexto) { var ArquivoLer = (ArquivoLer)objArquivoLer; nrLinha = ArquivoLer.Nr_Linha; dsTexto = ArquivoLer.Ds_Texto; } } - Este tipo de objeto es probablemente el más difícil de crear en el CLR, ya que su resultado es una tabla y para llenar esta tabla, necesitamos crear una clase con conjuntos/obtenciones con los datos que serán devueltos, definir la salida de los datos a la base de datos y la programación para poblar/calcular los datos.
cadena o SqlString?
Haga clic aquí para verEjemplos:
El tipo SqlString tiene el método de verificación IsNull para determinar si se pasó un valor NULL como parámetro. Esta verificación es más rápida que usar string.IsNullOrEmpty. Sin embargo, utilizando una variable de cadena como parámetro, si la función o SP se llama con un valor NULL, se ejecutará normalmente, sea o no tratado en el futuro en su código fuente. Si ingresas un valor NULL para una variable de tipo SqlString y no has realizado el tratamiento usando el método variavel.IsNull, al recuperar el valor ingresado usando el atributo variavel.Value o el método variavel.ToString(), se generará una excepción ERROR en tu rutina.
Otro buen ejemplo para ilustrar la diferencia es entre DateTime y SqlDateTime. Si pasa una fecha NULL a DateTime, se generará una excepción ERROR en su rutina. Este tipo de datos no acepta NULL. Si necesita usar algo como esto, debe usar el método MinValue (1/1/0001 12:00:00 a.m.) o MaxValue (31/12/9999 23:59:59). El tipo de datos SqlDateTime acepta valores nulos y tiene el método variavel.Null para definir valores nulos.
Mi recomendación es utilizar siempre los tipos de datos de la base de datos (SqlString, SqlInt32, etc.) y recordar SIEMPRE realizar los tratamientos necesarios (principalmente la variable.IsNull).
¿Qué versión de .NET Framework debo usar?
Haga clic aquí para ver– SQL Server 2005: sólo puede utilizar .NET Framework 1.0 y 2.0
– SQL Server 2008: admite hasta .NET Framework 3.5
– SQL Server 2012 y 2014: admite hasta .NET Framework 4.6.1
Puede configurar la versión de .NET Framework al crear el proyecto o haciendo clic derecho en el proyecto de la Biblioteca de clases y seleccionando la opción "Propiedades". También debes realizar el mismo procedimiento para el proyecto CLR, que aún te brinda la posibilidad de definir la versión de la base de datos:
Compilando su proyecto y publicándolo en la base de datos.
Haga clic aquí para verUna vez creada la base de datos, haga clic derecho en el proyecto CLR y seleccione la opción “Publicar…”. Los botones “Build” y “Rebuild” se utilizan para simplemente compilar el código fuente y validar si hay errores de sintaxis en el código fuente, mientras que el botón “Clean” elimina los archivos generados y el caché de Visual Studio.
En la pantalla que se abrirá deberás hacer clic en el botón “Editar…” para ingresar al servidor y cómo conectarte a la base de datos.

Puede crear/cargar perfiles para facilitar la publicación en más de un entorno. El botón "Generar scripts" generará un script SQL, que debe ser ejecutado por SQLCMD para publicar el CLR y el botón "Publicar" generará el script y lo ejecutará en la base de datos.

Nivel de permiso de montaje
En SQLCLR, existen 3 niveles de permisos para los ensamblados creados:
- SEGURO: Los métodos ensambladores pueden hacer más que los métodos Transact-SQL con la misma lógica y se ejecutan con las credenciales del usuario que llama.
- ACCESO EXTERNO: Los métodos pueden realizar manipulación de archivos y operaciones de E/S a través de la red. Los métodos se ejecutan utilizando la cuenta de servicio de SQL Server, heredando sus privilegios de Active Directory.
- INSEGURO / SIN RESTRICCIONES: amplía los privilegios de ACCESO EXTERNO, lo que permite que CLR ejecute comandos sin restricciones
Si el ensamblado necesita utilizar los niveles de permiso EXTERNAL ACCESS o UNRESTRICTED, será necesario definir la base de datos en la que será publicado como TRUSTWORTY:
ALTER DATABASE [Nome_do_Database] SET TRUSTWORTHY ON
Permisos necesarios para compilar el CLR
Para que un usuario tenga permiso para publicar un CLR en la base de datos, deberá cumplir uno de los siguientes requisitos:
- Miembro del rol de administrador de sistemas
- Si el nivel de permiso de ensamblaje es SEGURO, el usuario necesitará el permiso CREATE ASSEMBLY y DROP ASSEMBLY.
- Si el nivel de permiso de ensamblaje es ACCESO EXTERNO, el usuario necesitará los permisos CREATE ASSEMBLY, DROP ASSEMBLY y EXTERNAL ACCESS ASSEMBLY.
- Si el nivel de permiso de ensamblaje es SIN RESTRICCIONES, el usuario necesitará los permisos CREATE ASSEMBLY, DROP ASSEMBLY y UNSAFE ASSEMBLY.
Resultado final:
Restricciones en ensamblados: DLL admitidas y no admitidas
Haga clic aquí para verAtributos personalizados no permitidos
Los ensamblajes no se pueden anotar con los siguientes atributos personalizados:
– System.ContextStaticAttribute
– System.MTAThreadAttribute
– System.Runtime.CompilerServices.MethodImplAttribute
– System.Runtime.CompilerServices.CompilationRelaxationsAttribute
– System.Runtime.Remoting.Contexts.ContextAttribute
– System.Runtime.Remoting.Contexts.SynchronizationAttribute
– System.Runtime.InteropServices.DllImportAttribute
– Sistema.Seguridad.Permisos.CodeAccessSecurityAttribute
– System.STAThreadAttribute
– System.ThreadStaticAttribute
Además, los ensamblados SAFE y EXTERNAL_ACCESS no se pueden anotar con los siguientes atributos personalizados:
– System.Security.SuppressUnmanagedCodeSecurityAttribute
– System.Security.UnverifiableCodeAttribute
API de .NET Framework no permitidas
Cualquier API de Microsoft .NET Framework anotada con una de las prohibiciones de HostProtectionAttributes no se puede llamar desde los ensamblados SAFE y EXTERNAL_ACCESS.
– eSelfAffectingProcessMgmt
– eSelfAffectingThreading
– eSincronización
– eSharedState
– eGestión de procesos externos
– eThreading externo
– Infraestructura de seguridad electrónica
– eMayLeakOnAbort
– IIU
Ensamblados de .NET Framework compatibles
Cualquier ensamblado al que haga referencia su ensamblado personalizado debe cargarse en SQL Server mediante CREATE ASSEMBLY. Los siguientes ensamblados de .NET Framework ya están cargados en SQL Server y, por lo tanto, se pueden consultar ensamblados personalizados sin tener que usar CREATE ASSEMBLY.
– CustomMarshalers.dll
– Microsoft.VisualBasic.dll
– Microsoft.VisualC.dll
– mscorlib.dll
– Sistema.dll
– Configuración.del.sistema
– System.Core.dll (compatible con SQL Server 2008)
– System.Data.dll
– Sistema.Datos.OracleClient
– System.Data.SqlXml.dll
– Implementación.del.sistema
– System.Security.dll
– Sistema.Transacciones
– System.Web.Services.dll
– System.Xml.dll
– System.Xml.Linq.dll (compatible con SQL Server 2008)
Vistas y SP’s del Catálogo
Haga clic aquí para ver-- SYS.ASSEMBLIES
-- Name, Assembly ID, security and “is_visible” flag
SELECT * FROM sys.assemblies
-- SYS.ASSEMBLY_FILES
-- Assembly ID, name of each file & assembly contents
SELECT * FROM sys.assembly_files
-- SYS.ASSEMBLY_MODULES
-- Sql ObjectID, Assembly ID, name & assembly method
SELECT * FROM sys.assembly_modules
-- SYS.ASSEMBLY_REFERENCES
-- Links between assemblies on Assembly ID
SELECT * FROM sys.assembly_references
-- SYS.MODULE_ASSEMBLY_USAGES
-- Partial duplicate of SYS.ASSEMBLY_MODULES
-- Links SQL Object ID to an Assembly ID
SELECT * FROM sys.module_assembly_usages
-- CLR STORED PROCEDURES
SELECT
SCHEMA_NAME(sp.schema_id) + '.' + sp.[name] AS [Name],
sp.create_date,
sp.modify_date,
sa.permission_set_desc AS [Access],
sp.is_auto_executed
FROM
sys.procedures AS sp
INNER JOIN sys.module_assembly_usages AS sau ON sp.object_id = sau.object_id
INNER JOIN sys.assemblies AS sa ON sau.assembly_id = sa.assembly_id
WHERE
sp.type_desc = N'CLR_STORED_PROCEDURE';
-- CLR TRIGGERS
SELECT
SCHEMA_NAME(so.schema_id) + '.' + tr.[name] AS [Name],
SCHEMA_NAME(so.schema_id) + '.' + OBJECT_NAME(tr.parent_id) AS [Parent],
te.type_desc AS [Fired On],
te.is_first,
te.is_last,
tr.create_date,
tr.modify_date,
sa.permission_set_desc AS [Access],
tr.is_disabled,
tr.is_not_for_replication,
tr.is_instead_of_trigger
FROM
sys.triggers AS tr
INNER JOIN sys.objects AS so ON tr.[object_id] = so.[object_id]
INNER JOIN sys.trigger_events AS te ON tr.[object_id] = te.[object_id]
INNER JOIN sys.module_assembly_usages AS mau ON tr.object_id = mau.object_id
INNER JOIN sys.assemblies AS sa ON mau.assembly_id = sa.assembly_id
WHERE
tr.type_desc = N'CLR_TRIGGER'
-- CLR Scalar Functions
SELECT
SCHEMA_NAME(so.schema_id) + N'.' + so.[name] AS [Name],
so.create_date,
so.modify_date,
sa.permission_set_desc AS [Access]
FROM
sys.objects AS so
INNER JOIN sys.module_assembly_usages AS sau ON so.object_id = sau.object_id
INNER JOIN sys.assemblies AS sa ON sau.assembly_id = sa.assembly_id
WHERE
so.type_desc = N'CLR_SCALAR_FUNCTION'
-- CLR Table-valued Functions
SELECT
SCHEMA_NAME(so.schema_id) + N'.' + so.[name] AS [Name],
so.create_date,
so.modify_date,
sa.permission_set_desc AS [Access]
FROM
sys.objects AS so
INNER JOIN sys.module_assembly_usages AS sau ON so.object_id = sau.object_id
INNER JOIN sys.assemblies AS sa ON sau.assembly_id = sa.assembly_id
WHERE
so.type_desc = N'CLR_TABLE_VALUED_FUNCTION'
-- CLR Aggregate Function
SELECT
SCHEMA_NAME(so.schema_id) + N'.' + so.[name] AS [Name],
so.create_date,
so.modify_date,
sa.permission_set_desc AS [Access]
FROM
sys.objects AS so
INNER JOIN sys.module_assembly_usages AS mau ON so.object_id = mau.object_id
INNER JOIN sys.assemblies AS sa ON mau.assembly_id = sa.assembly_id
WHERE
so.type_desc = N'AGGREGATE_FUNCTION'
EXEC sys.sp_assemblies_rowset N'<AssemblyName>'
EXEC sys.sp_assembly_dependencies_rowset <AssemblyID>
Vídeo: Introducción a SQLCLR
¿Quieres hablarnos sobre SQLCLR?
– Telegrama: https://t.me/sqlclr
– Whatsapp: https://chat.whatsapp.com/71JS49CiD12Ct6pR3fjfLu
¡Y eso es todo, amigos!
¡Hasta el próximo post!
CLR SQL Server SQLCLR cómo activar habilitar cómo usar cómo activar habilitar cómo crear sp cómo crear un procedimiento cómo programar cómo comenzar introducción comenzar por dónde empezar cómo codificar procedimientos de programación funciones con valores de tabla escalar c# programación csharp base de datos programación de bases de datos
CLR SQL Server SQLCLR cómo activar habilitar cómo usar cómo activar habilitar cómo crear sp cómo crear un procedimiento cómo programar cómo comenzar introducción comenzar por dónde empezar cómo codificar procedimientos de programación funciones con valores de tabla escalar c# programación csharp base de datos programación de bases de datos
Dirceu Resende
Arquitecto de Bases de Datos y BI · Microsoft MVP · MCSE, MCSA, MCT, MTA, MCP.








Comentários (0)
Carregando comentários…