Reparar una base dañada de MS SQL Server

Últimamente me ha tocado quemarme las pestañas por casos de bases de datos dañadas. Una mala infraestructura, y/o un mal plan de backups termina provocando este tipo de post luego de sufrir por un rato…

Reparar una base dañada

El siguiente es un error común cuando se daña una base:

Msg 824, Level 24, State 2, Line 11
SQL Server detected a logical consistency-based I/O error: incorrect pageid (expected 1:49876; actual 17253:-991032068). It occurred during a read of page (1:49876) in database ID 15 at offset 0x000000185a8000 in file ‘E:\Bases\SBDAXXXX.MDF’.  Additional messages in the SQL Server error log or system event log may provide more detail. This is a severe error condition that threatens database integrity and must be corrected immediately. Complete a full database consistency check (DBCC CHECKDB). This error can be caused by many factors; for more information, see SQL Server Books Online.

A continuación los pasos que se debería seguir:

  1. Intentar hacer un backup de la base
  2. Obtener la cantidad de registros que hay en ese momento en cada tabla (script adjunto al final)
  3. Colocar la base en modo de usuario único
    ALTER DATABASE SBDAXXXX SET SINGLE_USER WITH ROLLBACK IMMEDIATE

     

  4. Intentar reparar la base
    DBCC CHECKDB(SBDAXXXX,REPAIR_ALLOW_DATA_LOSS)

     

  5. Verificar la cantidad de registros actual (script adjunto al final)
DECLARE @Tabla VARCHAR(250)
DECLARE @SQL VARCHAR(2000)
CREATE TABLE #TablaTemp(ID INT IDENTITY(1,1),Tabla_Nombre VARCHAR(200),Cant_Registros INT)

DECLARE TINFO_CUR CURSOR FOR 
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES 
WHERE TABLE_TYPE='BASE TABLE'

OPEN TINFO_CUR
FETCH NEXT FROM TINFO_CUR INTO @Tabla
WHILE @@FETCH_STATUS =0
BEGIN
SET @SQL='INSERT INTO #TablaTemp(Tabla_Nombre,Cant_Registros) SELECT '''+@Tabla+''',COUNT(*) FROM '+@Tabla+''
EXECUTE (@SQL)
FETCH NEXT FROM TINFO_CUR INTO @Tabla
END
CLOSE TINFO_CUR
DEALLOCATE TINFO_CUR


SELECT * FROM #TablaTemp ORDER BY Tabla_Nombre

DROP TABLE #TablaTemp

 

Si la base está muy dañada posiblemente no se repara, o se pierde información. En ese caso se puede recurrir a un software de 3ro para intentar reparar el .MDF.

Listar Triggers

Como identificar las triggers que hay en tu base de datos

Siempre es útil poder listar las triggers que tienes funcionando en tu server de producción.

Para ellos es que surge el siguente script:

SELECT O.name AS [Trigger]
,OBJECT_NAME(parent_obj) AS Tabla
,OBJECTPROPERTY( id,'ExecIsUpdateTrigger') AS Es_Update
,OBJECTPROPERTY( id,'ExecIsDeleteTrigger') AS Es_Delete
,OBJECTPROPERTY( id,'ExecIsInsertTrigger') AS Es_Insert
,OBJECTPROPERTY( id,'ExecIsAfterTrigger') AS Es_After
,OBJECTPROPERTY( id,'ExecIsInsteadOfTrigger') AS Es_insteadof
 ,OBJECTPROPERTY(id, 'ExecIsTriggerDisabled') AS Habilitado
FROM sysobjects O INNER JOIN sysusers U ON O.uid = U.uid
INNER JOIN sys.tables T ON O.parent_obj = T.object_id
INNER JOIN sys.schemas S ON T.schema_id = S.schema_id
WHERE O.type = 'TR'

 

Resultado en un Microsoft SQL Server 2008 R2 x64 con el Microsoft SQL Server Management Studio:

Listar Triggers con TSQL

Directorio de Backup de MS SQL predeterminado

Restaurar backup es un proceso, que en algunos casos se debe desarrollar diariamente. Por ello es bueno optimizar los tiempos.

A continuación un Tips para que al buscar el backup con el Microsoft
SQL Server Management Studio les abra en un directorio que ustedes elijan.

En este caso estoy trabajando con SQL Server 2008 R2 x64 Enterprise. Al intentar levantar un backup siempre abre el “examinar” en el mismo lugar:
“C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\Backup”

 

Para modificar dicha ruta predeterminada realizamos lo siguiente:

  1. Ingresamos al Editor del Registro de Windows (regedit)
  2. Navegamos dentro del registro por MIPC\HKLM\Software\Microsoft\Microsoft Sql Server\(Motor + Instancia)\MSSQLServer:
  3. Buscamos y modificamos la clave “BackupDirectory” por el directorio que nosotros queramos:

 

Listo! Ahora la próxima vez que intentemos recuperar un backup, abrirá directamente en dicha carpeta:

Mejorar rendimiento de SQL Server – Estadísticas de Espera

En este oficio se escucha de manera repetitiva frases como: “Mi servidor anda lento“, “Se tarda demasiado“, “como hago para que funcione más rápido“.

Al intentar encontrar la causa del problema muchas veces no sabes dónde empezar. En esta serie de artículos veremos algunos pasos a seguir para tratar de determinar el motivo que lo origina.
A tener en cuenta:

Generalmente el resultado es ocasionado, no solo por un motivo, sino por la suma de varios. Por lo se debe analizar y tomar resultados de la mayor cantidad de variables posibles (red, disco, memoria, consultas, etc) y recién ahí, en conjunto tomar medidas. Caso contrario, si se analizan por separado, seguramente cada uno determinará una solución diferente.

 

Estádisticas de Espera (waits statistics)
Nos darán una visión general de cuáles son los mayores tiempos de espera a nivel de recursos y de sistema operativo.
En esta oportunidad nos valemos de un script de http://habrahabr.ru/post/216309/ para listar los mismos:

SELECT TOP 10 
wait_type , max_wait_time_ms wait_time_ms , signal_wait_time_ms , wait_time_ms - signal_wait_time_ms AS resource_wait_time_ms , 
100.0 * wait_time_ms / SUM(wait_time_ms) OVER ( ) AS percent_total_waits , 100.0 * signal_wait_time_ms / 
SUM(signal_wait_time_ms) OVER ( ) AS percent_total_signal_waits , 100.0 * ( wait_time_ms - signal_wait_time_ms ) / 
SUM(wait_time_ms) OVER ( ) AS percent_total_resource_waits FROM sys.dm_os_wait_stats WHERE wait_time_ms > 0 
-- remove zero wait_time 
AND wait_type NOT  IN 
 -- filter out additional irrelevant waits
(	'SLEEP_TASK', 'BROKER_TASK_STOP', 'BROKER_TO_FLUSH', 'SQLTRACE_BUFFER_FLUSH','CLR_AUTO_EVENT', 'CLR_MANUAL_EVENT', 
	'LAZYWRITER_SLEEP', 'SLEEP_SYSTEMTASK', 'SLEEP_BPOOL_FLUSH', 'BROKER_EVENTHANDLER', 'XE_DISPATCHER_WAIT', 'FT_IFTSHC_MUTEX',
	'CHECKPOINT_QUEUE', 'FT_IFTS_SCHEDULER_IDLE_WAIT', 'BROKER_TRANSMITTER', 'FT_IFTSHC_MUTEX', 'KSOURCE_WAKEUP', 'LAZYWRITER_SLEEP', 
	'LOGMGR_QUEUE', 'ONDEMAND_TASK_QUEUE', 'REQUEST_FOR_DEADLOCK_SEARCH', 'XE_TIMER_EVENT', 'BAD_PAGE_PROCESS', 'DBMIRROR_EVENTS_QUEUE',
	'BROKER_RECEIVE_WAITFOR', 'PREEMPTIVE_OS_GETPROCADDRESS', 'PREEMPTIVE_OS_AUTHENTICATIONOPS', 'WAITFOR', 'DISPATCHER_QUEUE_SEMAPHORE', 
	'XE_DISPATCHER_JOIN', 'RESOURCE_QUEUE' ) 
ORDER  BY wait_time_ms DESC

 


 

El resultado dependerá de su servidor:
A continuación una descripción de los waits mas comunes:

  • CXPACKET o A menudo indica nada más que ciertas consultas se ejecutan con el paralelismo; CXPACKET no son una señal inmediata de los problemas, a pesar de que puede ser el síntoma de otro problema, asociado con uno de los otros tipos de espera alto valor en la instancia.
  • SOS_SCHEDULER_YIELD o Las tareas que se ejecutan en el sistema pueden haber superado su cantidad permitida, y están teniendo que esperar en la cola de ejecutables. Esto puede indicar que el servidor está bajo demasiada presión.
  • ThreadPool o Una tarea tuvo que esperar a tener un trabajador libre para poder ejecutarla. Esta podría ser una señal de inanición de subproceso de trabajo, lo que requiere un aumento en el número de CPU en el servidor, para manejar una carga de trabajo altamente concurrente, o puede ser una señal de bloqueo, lo que resulta en un gran número de tareas paralelas que consumen los subprocesos de trabajo por largos períodos.
  • LCK_ * o Estos esperan tipos significan que el bloqueo se produce en el sistema y que las sesiones han tenido que esperar para adquirir un bloqueo de un tipo específico, que fue detenido por otra sesión de base de datos. Este problema puede investigarse más a fondo utilizando, por ejemplo, la información de los sys.dm_db_index_operational_stats.
  • PAGEIOLATCH_ *, IO_COMPLETION, WriteLog o Estas esperas son comúnmente asociados con el disco I / O y ocasionan los cuellos de botella, aunque la causa raíz del problema puede ser, y es comúnmente, una consulta de bajo rendimiento que está consumiendo cantidades excesivas de memoria en el servidor.
  • PAGEIOLATCH_ * se asocian específicamente con los retrasos en la capacidad de leer o escribir datos desde los archivos de base de datos.
  • WriteLog están relacionados con problemas con la escritura en archivos de registro. Estas esperas deben evaluarse conjuntamente con las estadísticas de archivos virtuales, así como los contadores de rendimiento de disco físico, para determinar si el problema es específico de una sola base de datos, archivo o disco, o es ejemplo de ancho.
  • PAGELATCH_ * o Non-I / O espera pestillos de páginas de datos en el grupo de búferes. Muchas veces PAGELATCH_ * esperas están asociados con problemas de contención de la asignación. Uno de los temas más conocidos de las asignaciones asociadas a PAGELATCH_ * espera que ocurra en tempdb cuando se crea y se destruye en el tempdb un gran número de objetos y el sistema experimenta la contención en el Mapa Global Shared Asignación (SGAM), mapa de asignación global (GAM ), y Página espacio libre (PFS) páginas en la base de datos tempdb.
  • LATCH_ * o Estas esperas se asocian con objetos de peso ligero a corto plazo de sincronización que se utilizan para proteger el acceso a las memorias caché internas, pero no el caché del búfer. Estas esperas pueden indicar una serie de problemas, dependiendo del tipo pestillo. La determinación de la clase de enganche específica que tiene el tiempo de espera más acumulado asociado a él se puede encontrar mediante la consulta de los sys.dm_os_latch_stats del DMV.
  • ASYNC_NETWORK_IO o Esta espera es a menudo erróneamente atribuido a un cuello de botella de la red. De hecho, la causa más común de esta espera es que una aplicación cliente está realizando el procesamiento de los datos fila por fila (tipo cursor). Y si bien están siendo transmitidos desde SQL Server como un conjunto de resultados el cliente los procesa fila por fila. La corrección de este tipo de espera generalmente requiere cambiar el código del lado del cliente para que se lea el conjunto de resultados lo más rápido posible, y luego se realiza el procesamiento por el lado del cliente.

 Una vez hecho algún cambio deberé reiniciar los contadodes, y empezar a recopilar las estadísticas de nuevo, para verificar si hubo alguna mejora:

DBCC SQLPERF('sys.dm_os_wait_stats',clear)

Esta primer entrada es el primer paso para determinar obtener una visión generar. En próximas entradas veremos algunas tareas específicas para continuar con la mejora del rendimiento del motor SQL Server.

Como especificar el Puerto en el SQL Server Management Studio

Últimamente por seguridad he estado migrando el puerto default del MS SQL Server de 1433 a otro diferente. Y en consecuencia más de uno me pregunto como usar el IDE de Microsoft para conectarse luego de este cambio. Si quieres saber como cambiar el puerto en el que corre tu motor SQL mirá este post (https://maurobernal.com.ar/blog/mssql/cambiar-el-puerto-del-ms-sql-server)

Aquí te cuento como cambiar el puerto de la conexión.

Si tienen configurado el motor SQL Server en un puerto diferente al predeterminado (1433), para poder realizar una conexión desde el SQL Server Management Studio se debe ingresar de la siguiente manera:

[HOST] , [PUERTO]

Es decir que separamos el mismo con una coma (,).

Ejemplo:

Si llegaste hasta aquí pero no sabes como se puede volver a cambiar el mismo, mirá este post: (https://maurobernal.com.ar/blog/mssql/cambiar-el-puerto-del-ms-sql-server)

Averiguar la intercalación de una base de datos

De la siguiente manera podrán averiguar la intercalación (collation) de una base de datos en MS SQL Server:

TSQL:
SELECT
DATABASEPROPERTYEX(‘MASTER’, ‘Collation’)
as Intercalacion;

SQL Server Management Studio:

Obtener Base Actual

Cuando trabajamos con campos calculados a veces es necesario incluir el nombre de la base sobre la que está realizando la consulta. De esta manera si realizamos esta misma consulta en varias veces, y unimos el resultado tendremos siempre un dato agrupador que nos identificará la base origen.

Esta función es DB_NAME().

Ejemplo:
select db_name() as BaseActual

Resultado:

BaseActual
master

 

Listar Tamaño de las Bases de Datos

El siguiente script en TSQL lista todas las bases de datos con sus respectivos tamaños en Megas y Gigas.

SELECT DB_NAME(db.database_id) DatabaseName,
round((CAST(mfrows.RowSize AS FLOAT)*8)/1024,2) RowSizeMB,
round((CAST(mflog.LogSize AS  FLOAT)*8)/1024,2) LogSizeMB,
round((CAST(mfrows.RowSize AS FLOAT)*8)/1024+(CAST(mflog.LogSize AS FLOAT)*8)/1024,2) DBSizeMB,
round((CAST(mfrows.RowSize AS FLOAT)*8)/1024/1024+(CAST(mflog.LogSize AS FLOAT)*8)/1024/1024,2) DBSizeGB
FROM sys.databases db LEFT JOIN
(SELECT database_id, SUM(size) RowSize FROM sys.master_files WHERE type = 0
  GROUP BY database_id, type) mfrows
ON mfrows.database_id = db.database_id LEFT JOIN
(SELECT database_id, SUM(size) LogSize FROM sys.master_files WHERE type = 1
  GROUP BY database_id, type) mflog
ON mflog.database_id = db.database_id LEFT JOIN
(SELECT database_id,SUM(size) StreamSize FROM sys.master_files WHERE type = 2
 GROUP BY database_id, type) mfstream
ON mfstream.database_id = db.database_id LEFT JOIN
(SELECT database_id, SUM(size) TextIndexSize FROM sys.master_files  WHERE type = 4
  GROUP BY database_id, type) mftext
ON mftext.database_id = db.database_id
ORDER BY 4 DESC

Complementa al siguiente script para listar las bases de datos con su ubicación:
https://maurobernal.com.ar/blog/t-sql/listar-todas-las-bases-de-datos-con-su-respectiva-ubicacion-y-tamanos/

Verificar Backups por TSQL

Verificar que se hayan realizado correctamente los backups es tarea fundamental de un DBA.

Para ello contamos con una serie de tablas provistar por Microsoft SQL Server para almacenar dicha información:

  • msdb.dbo.backupfile
  • msdb.dbo.backupmediafamily
  • msdb.dbo.backupmediaset
  • msdb.dbo.backupset

 

Los siguientes scripts pueden facilitar dicha tarea.

Más adelante trabajaremos con más detalle.

 

Atención:   Si copian y pegan puede que la comilla simple (‘) se convierta a comilla doble (“”).

/*******************************************************************************
Último Backup de la Base
******************************************************************************/
SELECT    S.Name AS DB, MAX(B.backup_finish_date) Fecha,DATEDIFF(hh,MAX(B.backup_finish_date),GETDATE()) Antiguedad_HS ,
CASE
  WHEN DATEDIFF(hh , MAX(B.backup_finish_date, GETDATE()) > 24 THENSI’
WHEN MAX(B.backup_finish_dateIS NULL THEN ‘SI’ ELSE ”
END AS SinBackupUltimas24hs
FROM sys.sysdatabases S LEFT OUTER JOIN msdb.dbo.backupset B ON B.database_name = S.name
WHERE B.type NOT LIKE ‘L’ AND S.name NOT LIKE  ‘tempdb’
GROUP BY S.Name ORDER BY S.Name

/*******************************************************************************
Ultimo Backup del Log
******************************************************************************/
SELECT S.Name AS DB,MAX(B.backup_finish_date) Fecha ,
CASE WHEN DATEDIFF(hh , MAX(B.backup_finish_date, GETDATE()) > 4 THEN ‘SI’
WHEN MAX(B.backup_finish_dateIS NULL THEN ‘SI’ ELSE
END AS SinBackupUltimas24hs
FROM sys.sysdatabases S LEFT OUTER JOIN msdb.dbo.backupset B ON B.database_name = S.name
WHERE B.type LIKE ‘L’ AND S.name NOT LIKE ‘tempdb’
GROUP BY S.Name ORDER BY S.Name

/*******************************************************************************
Detalle por Backups de los últimos 7 días
******************************************************************************/
SELECT    database_name as DB, name AS Backup_Name, backup_finish_date AS LastBackup,
CASE [type] WHEN ‘D’ THEN ‘Full’ 
WHEN ‘L’ THEN ‘Log’
WHEN ‘I’ THEN ‘Differential’ ELSE ‘Other’
END AS Tipo_Backup
FROM    msdb.dbo.backupset B
WHERE backup_finish_date DATEADD(dd,-7,GETDATE())
ORDER BY database_name, backup_finish_date desc

Error al Adjuntar una base de Datos

Error al adjuntar las bases de datos. Haga clic en el hipervínculo de la columna de mensajes para obtener más información.

“…Server detectó un error de E/S de coherencia lógico: página rasgada (firma esperada: 0x55555555; firma obtenida: 0xaaaaaaa9)…”

 

Hoy parecía ser un día normal y rutinario, cuando me enfrentó con el inconveniente de que una base se encuentra en modo sospechoso.

Generalmente con desadjuntar (detach) y nuevamente adjuntar (atach) se corrige el problema, pero en esta ocasión luego de volver de separar la misma, ya no se podía adjuntar.

Les paso una serie de instrucciones y pasos que les puede ser útil. En mi caso me dieron resultado:

  • Creamos una base con el mismo nombre, y con los mismos archivos de datos (mdf y ldf) que la base a recuperar
  • Detenemos el motor
  • Reemplazamos los archivos de la base generada con los de la base original dañada
  • Encendemos nuevamente el motor, por lo que la base dañada quedará en modo sospechoso
  • Ejecutamos el siguiente script

     

ALTER
DATABASE MYBASE SET
EMERGENCY
— Colocamos la base en modo “EMERGENCY”

ALTER
DATABASE MYBASE SET
SINGLE_USER
— La ponemos en modo de usuario único.

DBCC CHECKDB
(MYBASE, REPAIR_ALLOW_DATA_LOSS )
— Chequeamos la bd con la opción REPAIR_ALLOW_DATA_LOSS

ALTER
DATABASE MYBASE SET
MULTI_USER
— Por ultimo la ponemos en modo multiusuario