SQL Server: Comandos de Administración más utilizados (Permisos, Seguridad y Monitoreo)

Si trabajas con SQL Server en entornos productivos, dominar la administración de permisos y el monitoreo es clave para mantener la seguridad, estabilidad y rendimiento del sistema.

En este artículo encontrarás desde comandos básicos hasta ejemplos más avanzados utilizados en escenarios reales.


🔐 Gestión de permisos (Nivel básico)

Dar permiso CONNECT en la base de datos

USE NombreBaseDatos;
GO
GRANT CONNECT TO NombreUsuario;

Permitir ver los Stored Procedures

GRANT VIEW DEFINITION ON DATABASE::TuBaseDeDatos TO TuUsuario;

Permitir ver el código de los SPs

GRANT VIEW DEFINITION TO TuUsuario;

Dar permiso EXECUTE sobre el esquema dbo o base de datos

GRANT EXECUTE ON SCHEMA::dbo TO NombreUsuario;
GRANT EXECUTE ON DATABASE::TuBaseDeDatos TO NombreUsuario;

Permisos sobre tablas o vistas específicas

GRANT SELECT ON dbo.MiTabla TO NombreUsuario;
GRANT INSERT, UPDATE, DELETE ON dbo.MiTabla TO NombreUsuario;
GRANT SELECT ON dbo.MiVista TO NombreUsuario;

Acceso completo sobre un esquema

GRANT SELECT ON SCHEMA::dbo TO NombreUsuario;
GRANT INSERT, UPDATE, DELETE ON SCHEMA::dbo TO NombreUsuario;

🔐 Gestión avanzada de seguridad

Crear Login (nivel servidor)

CREATE LOGIN MiLogin WITH PASSWORD = 'PasswordSeguro123!';

Crear Usuario en la base de datos

USE NombreBaseDatos;
CREATE USER MiUsuario FOR LOGIN MiLogin;

Asignar roles predefinidos

ALTER ROLE db_datareader ADD MEMBER MiUsuario;
ALTER ROLE db_datawriter ADD MEMBER MiUsuario;

Crear un rol personalizado

CREATE ROLE RolLecturaEscritura;

GRANT SELECT, INSERT, UPDATE, DELETE ON SCHEMA::dbo TO RolLecturaEscritura;

ALTER ROLE RolLecturaEscritura ADD MEMBER MiUsuario;

Revocar permisos (REVOKE vs DENY)

-- Revoca el permiso (lo quita si existe)
REVOKE SELECT ON dbo.MiTabla FROM MiUsuario;

-- Deniega explícitamente (tiene mayor prioridad)
DENY SELECT ON dbo.MiTabla TO MiUsuario;

💡 Tip importante: DENY siempre tiene prioridad sobre GRANT.

Ejecutar como otro usuario (testing de seguridad)

EXECUTE AS USER = 'MiUsuario';

SELECT * FROM dbo.MiTabla;

REVERT;

📊 Monitoreo y diagnóstico

Que privilegios tiene un usuario

SELECT 
    dp.name AS Usuario,
    dp2.name AS Objeto,
    perm.permission_name,
    perm.state_desc
FROM sys.database_permissions perm
JOIN sys.database_principals dp 
    ON perm.grantee_principal_id = dp.principal_id
LEFT JOIN sys.objects dp2
    ON perm.major_id = dp2.object_id
WHERE dp.name = 'TuUsuario';

A que roles pertenece un usuario

SELECT 
    member.name AS Usuario,
    role.name AS Rol
FROM sys.database_role_members drm
JOIN sys.database_principals role 
    ON drm.role_principal_id = role.principal_id
JOIN sys.database_principals member 
    ON drm.member_principal_id = member.principal_id
WHERE member.name = 'TuUsuario';

Uso de índices y tablas

SELECT
    DB_NAME(database_id) AS BaseDeDatos,
    OBJECT_NAME(i.object_id) AS Objeto,
    last_user_seek,
    last_user_scan,
    last_user_lookup,
    last_user_update
FROM sys.dm_db_index_usage_stats s
JOIN sys.indexes i 
    ON s.object_id = i.object_id AND s.index_id = i.index_id
WHERE database_id = DB_ID('TuBaseDeDatos')
ORDER BY 
    COALESCE(last_user_seek, last_user_scan, last_user_lookup, last_user_update) DESC;

Consultas más costosas (performance tuning)

SELECT TOP 10
    qs.total_elapsed_time / qs.execution_count AS AvgElapsedTime,
    qs.execution_count,
    qs.total_logical_reads,
    qt.text
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
ORDER BY AvgElapsedTime DESC;

Bloqueos activos

SELECT
    blocking_session_id,
    session_id,
    wait_type,
    wait_time,
    wait_resource
FROM sys.dm_exec_requests
WHERE blocking_session_id <> 0;

Sesiones activas

SELECT
    session_id,
    login_name,
    status,
    host_name,
    program_name
FROM sys.dm_exec_sessions
WHERE is_user_process = 1;

Actividad con Default Trace

SELECT *
FROM fn_trace_gettable(
    (SELECT TOP 1 path FROM sys.traces WHERE is_default = 1),
    DEFAULT
)
WHERE DatabaseName = 'TuBaseDeDatos'
ORDER BY StartTime DESC;

🧠 Buenas prácticas

  • Usa roles en lugar de permisos directos a usuarios
  • Evita usar dbo para todo, segmenta por esquemas
  • Aplica el principio de mínimos privilegios
  • Audita accesos periódicamente
  • Documenta cambios de seguridad

🚀 Conclusión

Estos comandos te permiten:

  • Administrar accesos de forma segura
  • Diagnosticar problemas de rendimiento
  • Identificar bloqueos y uso de recursos
  • Prepararte para escenarios reales en producción

Dominar estos scripts es clave para cualquier desarrollador backend o líder técnico que trabaje con SQL Server.


📌 Si te fue útil, considera guardar este artículo o compartirlo con tu equipo.



Levantar un contenedor de SQL Server en un NAS Synology

Después de varios intentos fallidos, por fin logre levantar un contenedor de SQL Server basado en Docker en mi NAS Synology.

Aquí les comento el paso a paso y los problemas que tuve al momento de levantar la instancia.

Primeramente, hay que verificar que nuestro NAS cumpla con los requerimientos mínimos, si es así, procedemos con la creación el contenedor dentro del Container Manager.

En la sección Contenedor, dar clic en el botón Crear.

En el campo Imagen, selecciona la opción Agregar Imagen.

En la configuración tendrás que agregar el registro de contenedores de Microsoft https://mcr.microsoft.com

Posteriormente, seleccionarlo como el contenedor de registro a utilizar

Después, buscar la imagen del contenedor de SQL Server mediante el buscador, ingresando el texto “mssql/server” en el buscador, y seleccionar la versión que se requiera.

Después, asignar un nombre y configurar los recursos a asignar a nuestro contenedor.

Para esto recomiendo utilizar prioridad de CPU media, ni alta ni baja ya que puede consumir todo nuestro CPU o puede no ser suficiente con configuración baja. Para el Limite de memoria asignar por lo menos un 30% mas de lo recomendado en los requerimientos mínimos, y muy importante, no habilitar el reinicio automático, ya que en caso de alguna falla puede entrar en un bucle de falla-reinicio infinito.

En la siguiente sección de configuración hay que asignar el puerto para nuestro contenedor, podría ser el puerto por defecto de SQL Server (1433) o algún otro.

Es importante configurar algún volumen físico de nuestro NAS a la ruta “/var/opt/mssql” del contenedor, para almacenar nuestras bases de datos fuera del contenedor y no se pierdan al apagar o reiniciar nuestro NAS.

Hay que agregar las siguientes variables de ambiente mínimas requeridas y asignar privilegios de ejecución al contenedor

Del resto de la configuración, pueden dejar las opciones por defecto y dar clic en Finalizar.

Con esto ya estaría listo nuestro contenedor de SQL Server, ya solo restaría crear alguna base de datos de ejemplo como la de AdventureWorks

Espero les sea de utilidad.

Referencias:

Prerequisites

Inicio rápido: Ejecución de imágenes de contenedor de SQL Server para Linux con Docker

Variables de ejecución del contenedor

Deploy and connect to SQL Server Linux containers

Montaje de un directorio host como volumen de datos

Restauración de una base de datos de SQL Server en un contenedor para Linux

AdventureWorks sample databases

Verificar la existencia de objetos en SQL Server

En esta ocasión les comparto un script para verificar la existencia de objetos en SQL Server, es muy útil para validar procesos de despliegue entre ambientes.

Solo necesitan agregar en la primera seccion los objetos a validar.

-----(Script to verify deploy DB objects...)-----

declare @TblResultset table 
(schema_name sysname null, 
ItemName sysname null, 
ParentName sysname null,	--apply for indexes and columns
ItemType int not null,		--1 for tables, 2 for udtt, 3 for stored procedures, 4 for functions, 5 for indexes & 6 for columns.
create_date datetime, 
modify_date datetime, 
StatusMsg nvarchar(256))

set nocount on 

--/* update here with the objects to search.... !!!! --*/
insert @TblResultset (schema_name, ItemName,ItemType,ParentName)
--tables (schema, TableName, 1, null)
select 'dbo', 'MyTable1' ,1,null union all
select 'dbo', 'MyTable2',1,null union all
--user defined table types (schema, UdtName, 2, null)
select 'dbo', 'udt_1',2,null union all
select 'dbo', 'udt_2',2,null union all
--stored procedures (schema, SpName, 3,null)
select 'dbo', 'MyProcedure1' ,3,null union all 
select 'dbo', 'MyProcedure2' ,3,null union all 
--functions (schema, FunctionName, 4,null)
select 'dbo', 'fn_MyFuntion1' ,4,null union all
--indexes (schema, idxName, 5, ParentTableName)
select 'MySchema', 'MyIndexName' ,5,'MyParentTable' union all
--columns  (schema, ColumnName, 6, ParentTableName)
select 'MySchema','MyColumnName',6,'MyParentTable'

update RS
set RS.create_date = ST.create_date,
	RS.modify_date = ST.modify_date, 
	RS.StatusMsg = 
		case
			when ST.name is not null then 'Exists in database.'
			else 'Do not exists in database.'
		end
from @TblResultset RS 
	left outer join sys.tables ST 
		on RS.ItemName = ST.name
where RS.ItemType = 1 --tables

update RS
set RS.create_date = AO.create_date,
	RS.modify_date = null,
	RS.StatusMsg = 
	case
		when ST.name is not null then 'Exists in database.'
		else 'Do not exists in database.'
	end
from @TblResultset RS 
	left outer join  sys.table_types ST 
		on RS.ItemName = ST.name
	left outer join sys.all_objects AO 
		on ST.type_table_object_id = AO.object_id
where RS.ItemType = 2 --user defined table type


update RS
set RS.create_date = ST.created,
	RS.modify_date = ST.LAST_ALTERED,
	RS.StatusMsg = 
	case
		when ST.SPECIFIC_NAME is not null then 'Exists in database.'
		else 'Do not exists in database.'
	end
from @TblResultset RS 
	left outer join INFORMATION_SCHEMA.ROUTINES ST 
		on RS.ItemName = ST.SPECIFIC_NAME
where RS.ItemType in(3,4) --stored procedures & functions

update RS
set RS.create_date = AO.create_date,
	RS.modify_date = AO.modify_date,
	RS.StatusMsg = 
	case
		when ST.name is not null then 'Exists in database.'
		else 'Do not exists in database.'
	end
from @TblResultset RS 
	left outer join  sysindexes ST 
		on RS.ItemName = ST.name
	left outer join sys.all_objects AO 
		on ST.id = AO.object_id
where RS.ItemType = 5 --index

update RS
set RS.create_date =null,
	RS.modify_date = null,
	RS.StatusMsg = 
	case
		when ST.name is not null then 'Exists in database.'
		else 'Do not exists in database.'
	end
from @TblResultset RS 
	left outer join  syscolumns ST 
		on RS.ItemName = ST.name and object_name(ST.id) = RS.ParentName
where RS.ItemType = 6 --columns

-----(((Output resultset)))-----
select schema_name, ItemName as ObjectName, 
case
	when ItemType = 1 then 'Table'
	when ItemType = 2 then 'UDTT'
	when ItemType = 3 then 'Stored Procedure'
	when ItemType = 4 then 'Function'
	when ItemType = 5 then 'Index'
	when ItemType = 6 then 'Column'
	else 'UFO'
end as ObjectType,
isnull (cast(create_date as varchar),'NA') as CreateDate, 
isnull(cast(modify_date as varchar),'NA') as ModifyDate,
StatusMsg
from @TblResultset

set nocount off
go

La salida del script seria asi:

Espero les sea de utilidad…