Categorías
Enlaces de interes
Donaciones

Todo el contenido es gratuito y en beneficio de la comunidad. Puedes reconocer el esfuerzo con una donación si lo deseas.

Patrocinadores
Inserte aquí su publicidad

La palabra “conocer” es polisémica. Aparte del sentido bíblico, podemos establecer dos niveles. Está “saber lo que es conceptualmente, ser consciente de su existencia” y luego está el nivel “dominar y poseer experiencia en la materia en cuestión”. yo no hace tanto que llegué a ese segundo nivel con OpenQuery. Los que tenemos la “suerte” de lidiar con motores que no son SQL Server, como DB2, estamos acostumbrados a que aquello, vía linked server tal cual (por nombres de cuatro partes), sea un suplicio. Sin embargo, con OpenQuery, le mandas lo que haya que buscar, filtradito, y la diferencia es como la noche y el día. Y lo mismo para leer que para escribir. No hay que olvidar que con OpenQuery estamos enviando la sentencia completa al servidor de destino para que la resuelva él.

Ahora estoy en la fase de, como lo que tengo es un martillo, todo me parece un clavo, y lo último que me he preparado es un recopilador de datos de jobs por esta vía. Es decir, ya no es sólo para acceder a DB2, sino también a otros motores SQL Server. Que se puede hacer de otras formas, que no rinde tan mal incluso directamente, pero como ya le he cogido el truco y la inercia al “select * from OpenQuery(…”, sabe hasta mejor.

Así, la sentencia básica sería:

select 'INSTANCIA' as Server, Job, DiaEjecucion, 
	HoraEjecucion, Duracion, Paso, NombrePaso, Mensaje
from OpenQuery([INSTANCIA], '
select g.name as Job, cast(h.run_date as varchar(20)) as DiaEjecucion, 
	right(''000000'' + cast(h.run_time as varchar(20)), 6) as HoraEjecucion,
	right(''000000'' + cast(h.run_duration as varchar(20)), 6) as Duracion,
	h.step_id as Paso,
	h.step_name as NombrePaso,
	h.message as Mensaje
from (
select j.name,h.instance_id id   
from 
	msdb.dbo.sysjobs j with(nolock) inner join 
	msdb.dbo.sysjobhistory h with(nolock) on j.job_id = h.job_id   
and h.run_status=0    
) g inner join msdb.dbo.sysjobhistory h with(nolock)on g.id = h.instance_id   
where h.step_id > 0 and
	h.run_date >= cast(CONVERT(char(8),dateadd(dd,-1,getdate()),112) as int)') T

 

Y si eso lo hacemos dinámico, empleando los servidores vinculados, volcando el resultado a una tabla, obtenemos lo siguiente (lleva una segunda parte para formatear un HTML que permita enviar el resultado por mail):

create proc [dbo].[Jobs_Fallidos] 
  @pOut varchar(max) = null output, 
  @pDestino varchar(10) = 'Mail', 
  @pTODO_OK bit = 0 output,
  @pFechaHoraDesde smalldatetime = null
  
  as

--Para sólo recuperar los de las últimas x horas
select @pFechaHoraDesde = isnull(@pFechaHoraDesde, dateadd(hh, -14, getdate()))

declare @sql nvarchar(max), @parte_fija nvarchar(max)

select @sql = '', @parte_fija = N'
    select ''#NombreLinkedServer#'' as Server, Job, DiaEjecucion, 
	  HoraEjecucion, Duracion, Paso, NombrePaso, Mensaje
	from OpenQuery([#NombreLinkedServer#], ''
	select g.name as Job, cast(h.run_date as varchar(20)) as DiaEjecucion, 
		right(''''000000'''' + cast(h.run_time as varchar(20)), 6) as HoraEjecucion,
		right(''''000000'''' + cast(h.run_duration as varchar(20)), 6) as Duracion,
		h.step_id as Paso,
		h.step_name as NombrePaso,
		h.message as Mensaje
	from (
	select j.name,h.instance_id id   
	from 
		msdb.dbo.sysjobs j with(nolock) inner join 
		msdb.dbo.sysjobhistory h with(nolock) on j.job_id = h.job_id   
	and h.run_status=0    
	) g inner join msdb.dbo.sysjobhistory h with(nolock)on g.id = h.instance_id   
	where h.step_id > 0 and
	  h.run_date >= cast(CONVERT(char(8),dateadd(dd,-1,getdate()),112) as int)'') T'  


if object_id ('tempdb..#fjobs') > 0
	 drop table #fjobs   
	 
create table #fjobs 
  (Server varchar(15) not null, 
   Job varchar(100) not null, 
   DiaEjecucion char(8) not null, 
   HoraEjecucion char(6) not null, 
   Duracion char(6) not null,
   Paso int null,
   NombrePaso varchar(100) null,
   Mensaje varchar(max) null
   )


select @sql = @sql + replace(@parte_fija, '#NombreLinkedServer#', name) + ' union all
'
from sys.servers where provider = 'SQLNCLI'

select @sql = left(@sql, len(@sql) - 12)

insert #fjobs
exec sp_executesql @sql

delete from #fjobs 
where DiaEjecucion + ' ' + 
  left(HoraEjecucion,2)+':'+substring(HoraEjecucion,3,2) < @pFechaHoraDesde

--Listado de jobs que han fallado
 select cast(h.Server as nvarchar(15)) Server, 
  cast(h.Job as nvarchar(100)) as Job, 
  cast(h.DiaEjecucion as nvarchar(8)) as DiaEjecucion,  
  cast(HoraEjecucion as nvarchar(6)) as HoraEjecucion, 
  cast(Duracion as nvarchar(8)) as Duracion,
  cast(Paso as nvarchar(10)) as Paso, 
  cast(NombrePaso as nvarchar(100)) as NombrePaso, 
  cast(Mensaje as nvarchar(500)) as Mensaje
 from #fjobs h 
 order by Server, Job, DiaEjecucion, HoraEjecucion, Paso 
 

--Apartir de aquí, es un divertimento para enviar un mail con este listado
if @pDestino = 'Mail' and exists(select top 1 h.Server from #fjobs h)  
 begin
  select @pOut = '<html> 
<head> 
<STYLE TYPE="text/css"> 
<!-- 
body{font-family: Segoi UI; font-size: 9pt;} 
TH{font-family: Segoi UI; font-size: 9pt; color: 000099} 
TD{font-family: Segoi UI; font-size: 9pt;} 
---> 
</STYLE> 
</head> 
<body><p>'+ 
  'Jobs Fallidos en las últimas horas:</p>' +    
'<table  border=1 cellspacing=0 cellpadding=3> 
<tr><th>Servidor</th><th>Job</th><th>Dia</th><th>Hora</th><th>Duracion</th>
<th>Step</th><th>Nombre</th><th>Mensaje</th>
</tr>'
 
  select @pOut = @pOut +  ' 
  <tr>' + 
  '<td>' + h.Server + '</td>' + 
  '<td>' + rtrim(h.Job)  + ' </td>' +   
  '<td>' + h.DiaEjecucion  + ' </td>' +   
  '<td>' + left(h.HoraEjecucion,2)+':'+substring(h.HoraEjecucion,3,2) + ' </td>' +   
  '<td>' + left(h.Duracion,2)+':'+substring(h.Duracion,3,2)+':'+right(h.Duracion,2)+''' </td>'+
  '<td>' + cast(h.Paso as varchar(10)) + ' </td>' +
  '<td>' + h.NombrePaso + ' </td>' +
  '<td>' + left(h.Mensaje, 200) + ' </td>' +
  + '</tr>'
  from #fjobs h  
  order by Server, Job, DiaEjecucion, HoraEjecucion, Paso
 
 select @pOut = @pOut + '</table>' + 
 '<p>No responda a este mensaje</p>' +
 '<p>Sistemas de Información.<BR>
 Plus Ultra Seguros.</p>' +
'</body> 
</html>'

 end

if not exists(select top 1 h.Server from #fjobs h) 
 select @pTODO_OK = 1
else 
 select @pTODO_OK = 0

if object_id ('tempdb..#fjobs') > 0
 drop table #fjobs
 
set nocount off

return 0

 

Y bueno, ya está. Hemos hecho uso y abuso de OpenQuery.

A raíz de una pregunta surgida en el foro, aprovecho para hacer un breve post que recuerde esta funcionalidad, la posibilidad de capturar los registros de una operación DML para recuperarlos o tratarlos en general. La pregunta concreta en cuestión era cómo insertar un registro en una tabla a la que lo borras de otra:

http://social.msdn.microsoft.com/Forums/es-ES/be6b1c73-154b-41c4-9c35-d385d86e2083/como-hacer-para-que-antes-de-eliminar-un-registro-me-lo-inserte-en-otra-tabla?forum=sqlserveres

Habiendo varias alternativas, una es emplear la cláusula OUPUT. Tan fácil como esto:

CREATE TABLE dummy (Id int, campo varchar(10))
GO
CREATE TABLE Otra (Id int, campo varchar(10))
GO

INSERT dummy (Id, campo)
VALUES
(1, 'a'),
(2, 'b'),
(3, 'c'),
(4, 'd'),
(5, 'e')

GO

DELETE dummy
OUTPUT DELETED.* INTO Otra
WHERE Id = 3
GO

SELECT * FROM Otra
GO

Y ya está.

Me desayuno con un post de Brent Ozar en su blog. Este tío es una de las personalidades más afamadas del mundo SQL Server por algo. Le comentan que en una empresa, el DBA se marcha, ¿qué se le pregunta de cara al traspaso de conocimientos? Como la mejor definición de tonto es “aquél que cree saberlo todo”, le da por preguntarle al Imperio (sus más de 16.000 followers), a los que lanza esa misma pregunta: El DBA se marcha. ¿Qué le pregunto? Esta es la entrada:

http://www.brentozar.com/archive/2014/01/what-do-you-ask-the-leaving-dba/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+BrentOzar-SqlServerDba+%28Brent+Ozar+Unlimited%29

De esto saco dos enseñanzas. La primera, hete aquí una base de conocimiento excelente para saber por dónde empezar a preparar un plan de contingencia ante la contingencia de que uno de tus DBAs, o el principal, deje la empresa y tengas unos pocos días para extraer información de su cabecita. Estamos acostumbrados a tener planificado y hasta ensayado cada cosita que se rompe para arreglarla, ya que como sabemos, tarde o temprano, todo acaba fallando y hay que estar listos para recuperarlo. Pero si lo que “fallan” son las personas… Es una cuestión que hay que tener lista para cuando ocurra, porque ocurrirá. Esto mismo vale para un DBA que para cualquier otra persona de tu equipo.

La segunda, hay que hacer testamento. Trabajo en una compañía de seguros que cuenta con lo que se conoce como “Plan de continuidad del negocio”, una mezcla entre DRP, línea sucesoria y otras macabras lindezas. En este plan, además de registrar quién ha de tomar las decisiones en los momentos iniciales si aquellos que las toman ya no pueden hacerlo, se describe cómo volver a montar los sistemas de información partiendo de copias de seguridad y un documento. Ahora bien, esto es otra cosa. No se informa ahí de lo que muchas veces me encuentro que se guarda como lo único que les hace necesarios para una empresa, el know-how mal entendido (gran mentira, lo que nos hace valiosos, opino, es lo que podemos hacer, no lo que hicimos alguna vez). Aquello que se sabe y no se transmite ni se documenta, porque en realidad es como Data Mining, los datos están ahí, pero no saltan a la vista. En esta categoría entra desde saber que tal usuario es un pieza al que atar en corto porque te la ha jugado varias veces o quién sabe de aquel negociado, hasta en qué bar de la zona ponen los mejores pinchos.

Otra cosa es que, como testamento, éste se guarde hasta que el albacea deba darle lectura…

Comentaba anoche en twitter un artículo que me llamó la atención, no tanto por lo que decía, sino por una buena práctica que siempre me ha hecho mucha gracia.

Es algo que no discuto, un crecimiento automático del log de transacciones conlleva una degradación en el rendimiento. Lo que pasa es que si se impide crecer al log de transacciones, nos arriesgamos a que se llene y que tengamos que atender la incidencia (que ya será de una gravedad importante) a horas en las que a lo mejor no estamos en la mejor disposición de hacerlo, como puede ser a las cuatro de la mañana.

Se puede argumentar que una instancia adecuadamente administrada tendrá un sistema de alertas que avise con antelación. Mismo caso, te avisará en el momento más inoportuno. Se puede argumentar que eso no debería ocurrir nunca si se tiene adecuadamente dimensionado el fichero, y si se realizan los backups del log con la suficiente frecuencia (incluso con la frecuencia que el propio crecimiento requiera, algo que fácilmente se puede automatizar). Para empezar, “eso no debería ocurrir nunca” no es una frase que deba pronunciar un DBA, ya que tarde o temprano todo acaba fallando, y hay que tener un plan para recuperarse. Pero más allá de esa ley básica, eso no impide que se llene el log, ya que hay varias causas que impiden el reciclado del log, como algo tan simple y tan fuera de nuestro control como que una aplicación tenga un bug que deje una transacción sin finalizar.

A mí sólo se me ocurre una circunstancia y es esa, que el DBA que lo deja fijo no sea el que tenga que ocuparse de arreglarlo, por ejemplo porque la organización disponga de suficiente personal como para hacer turnos 24×7. Que se pierde rendimiento. Pues sí. Pero, ¿tanto rendimiento se pierde? Obviamente no, es algo que he medido muchas de veces para estar seguro, y que es preciso verificar en cualquier entorno, no hay que descartar que condiciones muy concretas puedan llevar a una penalización sustancial.

En cualquier caso, y para concluir, existen docenas de cosas en las que fijarse antes de que esa para mejorar el rendimiento de nuestros servidores, muchas de ellas no requieren de desvelos. Imagino que habrá montones de instalaciones en las que el log se ha dejado de tamaño fijo siguiendo la “recomendación”, pero que toleran el uso de cursores, los índices no tienen un mantenimiento adecuado o cualquier otra de esas 1000 cosas que nos entretienen a diario. Al menos yo prefiero no hilar tan fino en este aspecto.

Hoy, Dejan Sarka (@DejanSarka) ha completado su serie de 5 artículos sobre fraude y cómo SQL Server nos ayuda a combatirlo. Yo tengo un proyecto de lucha contra el fraude en curso, tema de seguros, cómo no. Pero aun así, es indispensable aunque no hayas trabajado en un proyecto de estas características. Dejo links a toda la serie.

http://sqlblog.com/blogs/dejan_sarka/archive/2013/10/15/fraud-detection-with-the-sql-server-suite-part-1.aspx

http://sqlblog.com/blogs/dejan_sarka/archive/2013/10/15/fraud-detection-with-the-sql-server-suite-part-2.aspx

http://sqlblog.com/blogs/dejan_sarka/archive/2013/10/15/fraud-detection-with-the-sql-server-suite-part-3.aspx

http://sqlblog.com/blogs/dejan_sarka/archive/2013/10/15/fraud-detection-with-the-sql-server-suite-part-4.aspx

http://sqlblog.com/blogs/dejan_sarka/archive/2013/10/15/fraud-detection-with-the-sql-server-suite-part-5.aspx