Script para listar permisos

Esto no es algo nuevo, en realidad tengo mucho tiempo de tener este codigo. Y otras veces me fue super util. Hoy de hecho me funciono en el trabajo cuando necesitaba replicar permisos de una base de datos que tuvimos que cambiar de un servidor a otros.

SELECT 
		[Permisos] = state_desc 
			+ ' ' + permission_name 
			+ ' on [' + c.name
			+ '].[' + b.name 
			+ '] to [' + ar.name 
			+ ']' COLLATE latin1_general_ci_as
FROM 
	sys.database_permissions AS a
	INNER JOIN sys.objects AS b 
		ON a.major_id = b.object_id
	INNER JOIN sys.schemas AS c 
		ON b.schema_id = c.schema_id
	INNER JOIN sys.database_principals AS ar 
		ON a.grantee_principal_id = ar.principal_id
UNION
SELECT 
		[Permisos] = state_desc 
			+ ' ' + permission_name 
			+ ' on Schema::['+ c.name 
			+ '] to [' + ar.name 
			+ ']' COLLATE latin1_general_ci_as
FROM 
	sys.database_permissions AS a
	INNER JOIN sys.schemas AS c 
		ON a.major_id = c.schema_id AND a.class_desc = 'Schema'
	INNER JOIN sys.database_principals AS ar 
		ON a.grantee_principal_id = ar.principal_id
		

Cual job se esta ejecutando???

Hoy ocupaba una consulta rápida para una validación, y la verdad buscando en red me encontré un código rápido y bastante útil para encontrar los jobs o tareas programadas en ejecución, el original esta en esta pagina (http://www.sanssql.com/2013/08/t-sql-query-to-find-currently-running.html), la verdad lo único que modifique fueron las etiquetas. No es postear por postear, la verdad si me fue bastante útil en lo que ocupaba, tal ves lo sea para alguien mas

SELECT
    J.name AS 'Nombre del Job',
    ja.start_execution_date AS 'Inicio a las:',
    DATEDIFF(ss, ja.start_execution_date, GETDATE()) AS 'Ha corrido por: (Segundos)'
FROM msdb.dbo.sysjobactivity ja
     INNER JOIN msdb.dbo.sysjobs J ON J.job_id=ja.job_id
WHERE job_history_id IS NULL AND ja.start_execution_date IS NOT NULL
ORDER BY start_execution_date;

Este otro da un poco mas de info

SELECT
	J.name AS 'Nombre del Job',
	ja.start_execution_date AS 'Inicio a las:',
	DATEDIFF(ss, ja.start_execution_date, GETDATE()) AS 'Ha corrido por: (Segundos)'
FROM msdb.dbo.sysjobactivity ja
	 INNER JOIN msdb.dbo.sysjobs J ON J.job_id=ja.job_id
WHERE job_history_id IS NULL AND ja.start_execution_date IS NOT NULL
ORDER BY start_execution_date;Otra version un poco mas de detalle


SELECT 
    [sj].[job_id] AS [ID]
    , [sj].[name] AS [Nombre]
    , CASE 
        WHEN [jh].[run_date] IS NULL OR [jh].[run_time] IS NULL THEN NULL
        ELSE CAST(
                CAST([jh].[run_date] AS CHAR(8))
                + ' ' 
                + STUFF(
                    STUFF(RIGHT('000000' + CAST([jh].[run_time] AS VARCHAR(6)),  6), 3, 0, ':'), 6, 0, ':')
                AS DATETIME)
      END AS [UltimaFechaEjecucion]
    , CASE [jh].[run_status]
        WHEN 0 THEN 'Fallido'
        WHEN 1 THEN 'Completado'
        WHEN 2 THEN 'Reintento'
        WHEN 3 THEN 'Cancelado'
        WHEN 4 THEN 'Ejecutando' -- In Progress
      END AS [EstadoUltimaEjecucion]
    , STUFF(STUFF(RIGHT('000000' + CAST([jh].[run_duration] AS VARCHAR(6)),  6), 3, 0, ':'), 6, 0, ':') 
        AS [DuracionUltimaEjecucion(HH:MM:SS)]
    , [jh].[message] AS [EstadoDeLaUltimaEjecucion]
    , [jh].[last_executed_step_id] [IDUltimoPasoEjecutado]
    , [jh].[last_executed_step] [UltimoPasoEjecutado]
	, CASE [sJOBSCH].[NextRunDate]
        WHEN 0 THEN NULL
        ELSE CAST(
                CAST([sJOBSCH].[NextRunDate] AS CHAR(8))
                + ' ' 
                + STUFF(STUFF(RIGHT('000000' + CAST([sJOBSCH].[NextRunTime] AS VARCHAR(6)),  6), 3, 0, ':'), 6, 0, ':') AS DATETIME)
      END AS [FechaProximaEjecucion]
FROM 
    [msdb].[dbo].[sysjobs] AS [sj]
    LEFT JOIN (
                SELECT
                    [job_id]
                    , MIN([next_run_date]) AS [NextRunDate]
                    , MIN([next_run_time]) AS [NextRunTime]
                FROM [msdb].[dbo].[sysjobschedules]
                GROUP BY [job_id]
            ) AS [sJOBSCH]
        ON [sj].[job_id] = [sJOBSCH].[job_id]
    LEFT JOIN (
                SELECT 
                    [sjh].[job_id]
                    , [sjh].[run_date]
                    , [sjh].[run_time]
                    , [sjh].[run_status]
                    , [sjh].[run_duration]
                    , [sjh].[message]
                    , ROW_NUMBER() OVER (
                                            PARTITION BY sjh.[job_id] 
                                            ORDER BY [sjh].[run_date] DESC, [sjh].[run_time] DESC
                      ) AS RowNumber
					, [sja].[last_executed_step_id]
					, [sjs].[step_name] [last_executed_step]
					, [sja].[last_executed_step_date]
                FROM [msdb].[dbo].[sysjobhistory] sjh
					LEFT OUTER JOIN [msdb].[dbo].[sysjobactivity] sja ON sja.job_id = sjh.job_id
					LEFT OUTER JOIN [msdb].[dbo].[sysjobsteps] sjs ON sjs.job_id = sja.job_id AND sjs.step_id = sja.last_executed_step_id
                WHERE sjh.[step_id] = 0
            ) AS [jh]
        ON [sj].[job_id] = [jh].[job_id]
        AND [jh].[RowNumber] = 1
ORDER BY [Nombre]

Como medir el progreso del rollback de una transaccion

Muchas veces nos encontramos con casos de consultas (queries) que estan modificando datos por largos periodos de tiempo y honestamente lo ultimo que quiero hacer en esos momentos es terminar el proceso; si se da el caso que el Log de la base de datos se lleno es preferible crear un log temporal en otra unidad y esperar a que la transacción termine por si misma. Pero si se da el caso que no podemos esperar y hay que terminar o matar la transacción ya tenemos a nuestro jefe o cliente presionando por ello, siempre es util saber cual es el % del progreso del rollback, para ello utilizamos el mismo comando KILL <SPID> WITH STATUSONLY, este comando nos dara un estimado del porcentaje.

Para reproducir, generar un ciclo de inserción en cualquier tabla, en este ejemplo insertamos texto infinitamente:


CREATE TABLE tprueba
(
id    INT NOT NULL IDENTITY(1, 1),
texto VARCHAR(15),
CONSTRAINT pk_tprueba PRIMARY KEY (id)
)

WHILE EXISTS(SELECT 1
FROM   sys.tables
WHERE  name = 'Tprueba')
BEGIN
INSERT INTO tprueba
VALUES      (' Es Una Prueba ')
END

Una vez que tengamos el código en ejecución procedamos a matar o terminar la transacción que recien iniciamos


KILL <SPID>

Una vez en estado rollback ejecutamos la misma instrucción de KILL con el parámetro [statusonly]

KILL <spid> WITH statusonly


Errores comunes al comprimir archivo de una base de datos MSSQL

Personalmente, creo que como DBA el hecho de borrar datos es algo de por si malo, pero en algunas ocasiones hay que borrar información de manera masiva ya que son parte de los históricos de una base de datos muy grandes. En mi experiencia el borrado de datos se asocia a bases de datos que ocupan varios TB de disco duro.
Comprimir mi base de datos va a generar ciertos problemas como fragmentación de mis índices; con el tiempo he encontrado dos errores frecuentes cuando trato de comprimir archivos de base de datos utilizando el DBCC SHRINKFILE:

  1. Could not adjust the space allocation for file ‘<file name>’. Este error esta bien documentado en muchos foros, la solucion mas sencilla es modificar el tamaño del archivo que estamos comprimiendo, se le asignan unos cuantos mega bytes mas de su tamaño actual y listo.
  2. [Cannot use the PAGE granularity hint on the table «<table name>» because locking at the specified granularity is inhibited.], en este caso tenemos que analizar la tabla que nos esta generando el problema, habitualmente esta asociado a tablas sin un primary key o sin un indice clustered. En dado caso tenemos dos opciones:
  • Defragmentar los indices de la tabla cuando existe un clustered.
  • Crear un primary key para la tabla que nos genera el problema.

Espero les sea util

Contador Page life Expectancy

Ultimamente hemos observado uno que otro problema de memoria y hemos utilizado ese contador de MSSQL, que en castellano podria ser algo asi posible duracion de la pagina en memoria. Algunas veces ni siquiera traduzco los terminos tecnicos y creo que la mayoria lo hacemos de igual manera. Ahora bien este contador es mas menos eso; me indica que tanto va a estar alojada una pagina en memoria sin referencia alguna.

Microsoft hace referencia a que un PLE de 300 es un buen numero, el problema de este dato es que ese calculo nunca se actualizo cuando fue mejorando el hardware; por lo que ese numero puede ser bueno en equipos pequeños pero en máquinas poderosas puede estar muy lejos de la realidad.

Mi recomendación es mirar este contador con lupa y ver si de alguna manera podría tener problemas de memoria sin darse cuenta.

La siguiente consulta lista los numeros del PLE

SELECT  @@servername AS Instancia ,
        [object_name] Objeto ,
        [counter_name] Contador ,
        [cntr_value] AS PLE_SEGUNDOS ,
        [cntr_value] / 60 AS PLE_MINUTOS ,
        [cntr_value] / 3600 AS PLE_HORAS
FROM    sys.dm_os_performance_counters
WHERE   [object_name] LIKE '%Manager%'
        AND [counter_name] = 'Page life expectancy' 

Leyendo varios foros encontré una formula que utilizan muchos de los MVPs de SQL Server para determinar un PLE apropiado para cada equipo:

PLE Recomendado = (Tamaño en GB del Datos en Cache/4GB *300) en ingles por aquello de la traduccion: (DataCacheSizeInGB/4GB*300), como lo dice don Jonathan Kehayias

Este query trae un valor bastante cercano a lo que deberíamos esperar o buscar en realidad:

DECLARE @PLE DECIMAL

SELECT @PLE = ( ( ( bpool_committed * 8 ) / ( 1024 * 1024 ) ) / 4 ) * 300
FROM   sys . dm_os_sys_info

SELECT 'Recommended PLE' = CASE
WHEN @PLE &lt; 300 THEN 300
ELSE @PLE
END

Como encontrar todos los triggers en mi base de datos SQL Server

Siguiendo con el post anterior, a continuacion se hace uso de las vistas de sistema para encontrar los triggers en una base de datos:\

SELECT 
    Object_name(so .parent_object_id) Parent_Name,
    so .name ObjectName,
    so .type_desc [Type Description],
    so .create_date [Create Date],
    sm.definition [Text]
FROM   sys .objects so
INNER JOIN sys. sql_modules sm
ON so.object_id = sm.object_id
WHERE  so .type = 'TR'  

A partir de mi proximos posts empezare una revision de las vistas de sistema, y que cambio despues de 2005 y posteriores, que deberiamos sacar de los nuevos queries que se desarrollen tomando en cuenta que muchas vistas estan por desaparecer en futuras versions de MSSQL

Como encontrar corrupcion en mi base de datos?

Muchas veces hacemos uso frecuente del CHECKDB como herrarmienta de verificacion de la base de datos cuando lo que tratamos de encontrar es corrupcion.

A partir de SQL Server 2005 microsoft puso a nuestra disposicion una tabla en la MSBD que nos indica que paginas estan en un estado «sospechoso» (la traduccion del ingles suspect). El motor de base de datos inserta datos en esta tabla cuando encuentra errores al leer una pagina de datos en particular.

  • Error 823: una prueba cíclica de redundancia (CRC) generada por el sistema operativo, por ejemplo un error de disco (ciertos errores de hardware)
  • Error 824: generalmente indica errores logicos

La informacion que se registra en esta tabla se guarda durante las siguientes operaciones:

  • Lectura rutinaria durante cualquier query (consulta)
  • Respaldo de base de datos (backup restore)
  • Operaciones de reparacion de base de datos (CHECKDB)

Es importante saber cuales son los tipos de eventos listados (event_type) dentro de la tabla:

Valor event_type Descripción del error
1 Error 823 error de CRC del sistema operativo, o error 824 que no sea una suma de comprobación (checksum) no válida o una página con informacion incierta (por ejemplo, un Id. de página no válido)
2 Suma de comprobación errónea (checksum en Ingles)
3 Página rasgada (informacion incierta o Torn page en ingles)
4 Restaurada (la página se restauró después de marcarse como errónea)
5 Reparada (DBCC reparó la página)
7 Desasignada por DBCC

Para mi la autoridad en estos temas es Paul Randall, considero que los videos disponibles en este vinculo son de la informacion mas informativa disponible en linea.

Una consulta útil para buscar información en msdb..suspect_pages

SELECT 
    DB_NAME(database_id),
    [file_id],
    page_id,
    event_type,
    CASE event_type
        WHEN 1 THEN '823 or 824 or Torn Page'
        WHEN 2 THEN 'Bad Checksum'
        WHEN 3 THEN 'Torn Page'
        WHEN 4 THEN 'Restored'
        WHEN 5 THEN 'Repaired (DBCC)'
        WHEN 7 THEN 'Deallocated (DBCC)'
    END,
    error_count,
    last_update_date
FROM msdb..suspect_pages 

Una nota importante es que la información en esta pagina se actualiza; los eventos 4, 5 y 7 son de eventos que han sido actualizados y corregidos.

Esta tabla no se limpia automáticamente por lo que una vez solucionado el problema deberíamos de borrar los datos que contenga

Monitorear el uso de Procedimientos Almacenados

Hace un tiempo habia encontrado estas consultas para monitorear el uso de procedimientos almacenados en SQL 2005 y posterior. Los comparto porque siempre es util conocer que tanto y que tan frecuentemente se ejecutan nuestros procedimientos almacenados.

1. [Conteo Total de Ejecuciones]

SELECT 
    Db_name(st.dbid) [Base de Datos],
    Object_schema_name(st.objectid, dbid) [Schema],
    Object_name(st.objectid, dbid)        [USP],
    Max(cp.usecounts)                     [Total Ejecuciones]
FROM   
    sys.dm_exec_cached_plans cp
    CROSS apply sys.Dm_exec_sql_text(cp.plan_handle) st
WHERE  
    Db_name(st.dbid) IS NOT NULL
    AND cp.objtype = 'proc'
GROUP  BY 
    cp.plan_handle,
    Db_name(st.dbid),
    Object_schema_name(objectid, st.dbid),
    Object_name(objectid, st.dbid)
ORDER  BY 
        Max(cp.usecounts)

2. [Procedimiento que Consume mas CPU]

 SELECT 
    Db_name(st.dbid)
    [Base de Datos, Object_schema_name(st.objectid, dbid) [Schema],
    Object_name(st.objectid, dbid) [USP],
    Max(cp.usecounts) [Total Ejecuciones],
    Sum(qs.total_worker_time) [Consumo Total CPU],
    Sum(qs.total_worker_time) / ( Max(cp.usecounts) * 1.0 )    [Consumo Promedio CPU]
FROM   
    sys.dm_exec_cached_plans cp
    INNER JOIN sys.dm_exec_query_stats qs
        ON cp.plan_handle = qs.plan_handle
    CROSS apply sys.Dm_exec_sql_text(cp.plan_handle) st
WHERE  
    Db_name(st.dbid) IS NOT NULL
    AND cp.objtype = 'proc'
GROUP  BY 
    Db_name(st.dbid),
    Object_schema_name(objectid, st.dbid),
    Object_name(objectid, st.dbid)
ORDER  BY 
    Sum(qs.total_worker_time) DESC   

3. [Procedimientos con mayor tiempo de ejecucion]

SELECT 
    Db_name(st.dbid) [Base de Datos, 
    Object_schema_name(objectid, st.dbid) [Schema],
    Object_name(objectid, st.dbid) [USP],
    Max(cp.usecounts) [Total Ejecuciones],
    Sum(qs.total_elapsed_time) [Tiempo Total Ejecuciones],
    Sum(qs.total_elapsed_time) / Max(cp.usecounts) [Tiempo Promedio Ejecuciones]
FROM   
    sys.dm_exec_query_stats qs
    CROSS apply sys.Dm_exec_sql_text(qs.plan_handle) st
    INNER JOIN sys.dm_exec_cached_plans cp
        ON qs.plan_handle = cp.plan_handle
WHERE  
    Db_name(st.dbid) IS NOT NULL
    AND cp.objtype = 'proc'
GROUP  BY 
    Db_name(st.dbid),
    Object_schema_name(objectid,st.dbid),
    Object_name(objectid, st.dbid)
ORDER  BY 
    Sum(qs.total_elapsed_time) DESC  

Fuente SQL Journal

Cual es el progreso de mi respaldo de base de datos (backup)?

Saber cual es el progreso o porcentaje de ejecución de un respaldo nativo en SQL Server es tan simple como ejecutar una consulta sobre una de las tan útiles vistas del sistema. En este caso particular sobre la vista sys.dm_exec_requests que devuelve información de lo que se esta ejecutando en SQL Server.

El código que cito a continuación lista el identificador de la sesión, la tarea que esta ejecutando, el porcentaje completado y un estimado de cuanto tiempo tendremos que esperar para que la tarea termine en minutos; nótese que este tiempo es un estimado

--Progreso de mis backups
SELECT 
    dm_er.session_id id_Sesion,
    dm_er.command Comando,
    CONVERT(NUMERIC(6, 2), dm_er.percent_complete) AS [Porcentaje Completedo],
    CONVERT(VARCHAR(20), Dateadd(ms, dm_er.estimated_completion_time, Getdate()),20) AS [Tiempo Estimado Finalizacion],
    CONVERT(NUMERIC(6, 2), dm_er.estimated_completion_time / 1000.0 / 60.0) AS [Minutos pendientes]
FROM
    sys.dm_exec_requests dm_er
WHERE  
    dm_er.command IN ( 'RESTORE VERIFYON', 'RESTORE DATABASE','BACKUP DATABASE','RESTORE LOG','BACKUP LOG', 'RESTORE HEADERON' )

Hoy tuve la suerte de que un buen amigo me recordara que no es bueno olvidar las fuentes, y este código no es de mi autoría, el mismo lo encontré por primera vez por el 2010; los cambios que le he hecho son pocos, hacer un tipo de dato mas grande para evitar desbordamientos, etiquetas, agregar procesos en ejecución. El mismo lo encontré por primera vez en esta pagina; siendo utilizado por mis compañeros de trabajo en general

http://www.sp-configure.com/estimate-backuprestore-times/