Categorías
Donaciones

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

Inserte aquí su publicidad

Archivo de la categoría ‘Script’

Bien es sabido que SQL Server Express carece de Agente de SQL Server. ¿Significa eso que no podemos automatizar nada, ni siquiera los backups? ¿Hay alguna forma de agendar backups en SQL Server Express?

La respuesta es si. Claro que podemos.

No tenemos agente, pero sí tenemos el programador de tareas de Windows (Scheduled Tasks), que nos permite ejecutar comandos con SQLCMD a una hora u horas concretas. Así, primero preparamos nuestro script. Si hablamos de tareas de mantenimiento básicas, lo más eficaz es recurrir a la web del MVP Ola Hallengren, https://ola.hallengren.com/. Tomado del primero de los ejemplos de utilización del procedimiento almacenado de backup, este script que aquí acompaño hace backup de todas las bases de datos de una instancia, comprimidos y verificados:

EXECUTE dbo.DatabaseBackup
 @Databases = 'USER_DATABASES',
 @Directory = 'C:\Backup',
 @BackupType = 'FULL',
 @Verify = 'Y',
 @Compress = 'Y',
 @CheckSum = 'Y',
 @CleanupTime = 24

Con ello preparamos la ejecución de los backups, salvando scripts que luego ejecutaremos. La instrucción que irá en la tarea programada es muy simple:

sqlcmd -S miServidor\nombreDeInstancia -i C:\Scripts\MiScriptDeBackup.sql

Ya que sabemos programar los backups, no hay que quedarse ahí, hay que hacer lo mismo con las reindexaciones, chequeos de integridad, monitorización de esperas, etc. Sin SQL Server Agent es todo más engorroso, pero ni mucho menos imposible realizar una administración completa de una instancia de SQL Server Agent.

Para una guía completa de SQLCMD, dejo link a la ayuda: https://msdn.microsoft.com/es-es/library/ms162773.aspx. También a una guía que incluye la creación de tareas programadas para este mismo fin: https://support.microsoft.com/es-es/kb/2019698.

Y para terminar, he de citarme a mí mismo, ya que mi primera publicación en MSDN versaba sobre la automatización de backups y restores. Ha pasado tiempo, pero el artículo sigue vivo.

https://msdn.microsoft.com/es-es/library/bb972243.aspx

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 un post en el foro de SQL Server (link), en el que se preguntaba cómo realizar la tabla de clasificación de una competición deportiva con t-SQL, he preparado este mecanismo para llevarlo a cabo. No va mucho más allá de ser un pasatiempo, pero ha sido un pasatiempo divertido.

Se partía de una tabla, proporcionada por el usuario, con los resultados de la temporada de fútbol 2000-2001 de 1ª división de España. Para elaborar la clasificación, hacen falta unos criterios de ordenación en caso de empates, que están en la normativa del organismo que la organiza. En este caso era una dada por el propio usuario (ver el hilo en el foro). Se añaden unas tablas para obtener un modelo que sea más fácilmente explotable y que se carga partiendo de ese origen único, para finalmente preparar un procedimiento almacenado que actualiza la clasificación.

(

CREATE TABLE [dbo].[Resumen 2001][ID] [int] NOT NULL,
[TEMPORADA] [varchar](50) NULL,
[DIVISION] [int] NULL,
[JORNADA] [int] NULL,
[EQUIPO_LOCAL] [varchar](50) NULL,
[EQUIPO_VISITANTE] [varchar](50) NULL,
[GOLES_LOCAL] [int] NULL,
[GOLES_VISITANTE] [int] NULL,
CONSTRAINT [PK_Resumen 2001] PRIMARY KEY CLUSTERED
(
[ID] ASC
)
GO

--Tabla para los equipos
create table Equipos (
  IdEquipo tinyint not null primary key clustered identity (1,1),
  Nombre varchar(20) not null ,
  Pos_Juego_Limpio tinyint not null)
go
-- Tabla para guardar los partidos que cada equipo disputa
-- Cada partido figura dos veces, una vez por cada equipo que lo juega
create table Partidos_Equipo (
  IdPartido smallint not null,
  IdEquipo tinyint not null,
  IdEquipo_Contrario tinyint not null,
  Es_Local bit not null, 
  Goles_A_Favor tinyint not null,
  Goles_En_Contra tinyint not null,
  Puntos tinyint,
  primary key clustered (IdPartido, IdEquipo))
go
-- Tabla para persistir la clasificación
create table Clasificacion (
  IdEquipo tinyint not null primary key clustered,
  PJ tinyint not null,  --Partidos jugados
  PG tinyint not null, --Partidos ganados
  PE tinyint not null, --Partidos empatados
  PP tinyint not null, --Partidos perdidos
  GF tinyint not null, --Goles a favor,
  GC tinyint not null, --Goles en contra,
  DG smallint not null, --Diferencia de goles
  Puntos tinyint not null, --Puntos
  Desempate tinyint not null) --Desempate
go
--El campo desempate es el que permite que, a igualdad de puntos,
--haciendo uso de las reglas de la competición, se ubiquen los equipos

--Datos con los resultados
INSERT INTO [dbo].[Resumen 2001] VALUES  (1,'00/01',1,1,'Osasuna','Celta',0,2),
  (2,'00/01',1,1,'Zaragoza','Espanyol',1,2),
  (3,'00/01',1,1,'Villarreal','Rayo Vallecano',1,5),
  (4,'00/01',1,1,'Las Palmas','Alavés',0,3),
  (5,'00/01',1,1,'Numancia','Oviedo',1,0),
  (6,'00/01',1,1,'Mallorca','Valladolid',1,1),
  (7,'00/01',1,1,'Real Madrid','Valencia',2,1),
  (8,'00/01',1,1,'Barcelona','Málaga',2,1),
  (9,'00/01',1,1,'Dptivo. Coruña','Ath. Bilbao',2,0),
  (10,'00/01',1,1,'Real Sociedad','Rac. Santander',2,2),
  (11,'00/01',1,2,'Celta','Real Sociedad',4,1),
  (12,'00/01',1,2,'Espanyol','Osasuna',1,2),
  (13,'00/01',1,2,'Rayo Vallecano','Zaragoza',0,0),
  (14,'00/01',1,2,'Alavés','Villarreal',0,1),
  (15,'00/01',1,2,'Oviedo','Las Palmas',2,2),
  (16,'00/01',1,2,'Valladolid','Numancia',2,0),
  (17,'00/01',1,2,'Valencia','Mallorca',4,0),
  (18,'00/01',1,2,'Málaga','Real Madrid',3,3),
  (19,'00/01',1,2,'Ath. Bilbao','Barcelona',3,1),
  (20,'00/01',1,2,'Rac. Santander','Dptivo. Coruña',0,3),
  (21,'00/01',1,3,'Celta','Espanyol',1,0),
  (22,'00/01',1,3,'Osasuna','Rayo Vallecano',2,2),
  (23,'00/01',1,3,'Zaragoza','Alavés',2,2),
  (24,'00/01',1,3,'Villarreal','Oviedo',1,0),
  (25,'00/01',1,3,'Las Palmas','Valladolid',1,1),
  (26,'00/01',1,3,'Numancia','Valencia',0,3),
  (27,'00/01',1,3,'Mallorca','Málaga',0,1),
  (28,'00/01',1,3,'Real Madrid','Ath. Bilbao',4,1),
  (29,'00/01',1,3,'Barcelona','Rac. Santander',3,1),
  (30,'00/01',1,3,'Real Sociedad','Dptivo. Coruña',1,1),
  (31,'00/01',1,4,'Espanyol','Real Sociedad',1,2),
  (32,'00/01',1,4,'Rayo Vallecano','Celta',3,0),
  (33,'00/01',1,4,'Alavés','Osasuna',2,0),
  (34,'00/01',1,4,'Oviedo','Zaragoza',2,1),
  (35,'00/01',1,4,'Valladolid','Villarreal',0,0),
  (36,'00/01',1,4,'Valencia','Las Palmas',5,1),
  (37,'00/01',1,4,'Málaga','Numancia',1,3),
  (38,'00/01',1,4,'Ath. Bilbao','Mallorca',2,1),
  (39,'00/01',1,4,'Rac. Santander','Real Madrid',0,0),
  (40,'00/01',1,4,'Dptivo. Coruña','Barcelona',2,0),
  (41,'00/01',1,5,'Espanyol','Rayo Vallecano',0,0),
  (42,'00/01',1,5,'Celta','Alavés',1,1),
  (43,'00/01',1,5,'Osasuna','Oviedo',0,0),
  (44,'00/01',1,5,'Zaragoza','Valladolid',0,0),
  (45,'00/01',1,5,'Villarreal','Valencia',1,1),
  (46,'00/01',1,5,'Las Palmas','Málaga',2,1),
  (47,'00/01',1,5,'Numancia','Ath. Bilbao',0,0),
  (48,'00/01',1,5,'Mallorca','Rac. Santander',2,1),
  (49,'00/01',1,5,'Real Madrid','Dptivo. Coruña',3,0),
  (50,'00/01',1,5,'Real Sociedad','Barcelona',0,6),
  (51,'00/01',1,6,'Rayo Vallecano','Real Sociedad',4,1),
  (52,'00/01',1,6,'Alavés','Espanyol',1,0),
  (53,'00/01',1,6,'Oviedo','Celta',3,1),
  (54,'00/01',1,6,'Valladolid','Osasuna',1,1),
  (55,'00/01',1,6,'Valencia','Zaragoza',1,0),
  (56,'00/01',1,6,'Málaga','Villarreal',2,1),
  (57,'00/01',1,6,'Ath. Bilbao','Las Palmas',0,3),
  (58,'00/01',1,6,'Rac. Santander','Numancia',4,2),
  (59,'00/01',1,6,'Dptivo. Coruña','Mallorca',1,1),
  (60,'00/01',1,6,'Barcelona','Real Madrid',2,0),
  (61,'00/01',1,7,'Rayo Vallecano','Alavés',0,1),
  (62,'00/01',1,7,'Espanyol','Oviedo',2,0),
  (63,'00/01',1,7,'Celta','Valladolid',2,1),
  (64,'00/01',1,7,'Osasuna','Valencia',1,2),
  (65,'00/01',1,7,'Zaragoza','Málaga',1,1),
  (66,'00/01',1,7,'Villarreal','Ath. Bilbao',0,0),
  (67,'00/01',1,7,'Las Palmas','Rac. Santander',2,1),
  (68,'00/01',1,7,'Numancia','Dptivo. Coruña',1,2),
  (69,'00/01',1,7,'Mallorca','Barcelona',2,0),
  (70,'00/01',1,7,'Real Sociedad','Real Madrid',1,4),
  (71,'00/01',1,8,'Alavés','Real Sociedad',0,1),
  (72,'00/01',1,8,'Oviedo','Rayo Vallecano',4,1),
  (73,'00/01',1,8,'Valladolid','Espanyol',1,1),
  (74,'00/01',1,8,'Valencia','Celta',1,0),
  (75,'00/01',1,8,'Málaga','Osasuna',3,1),
  (76,'00/01',1,8,'Ath. Bilbao','Zaragoza',1,2),
  (77,'00/01',1,8,'Rac. Santander','Villarreal',3,1),
  (78,'00/01',1,8,'Dptivo. Coruña','Las Palmas',4,0),
  (79,'00/01',1,8,'Barcelona','Numancia',1,1),
  (80,'00/01',1,8,'Real Madrid','Mallorca',0,2),
  (81,'00/01',1,9,'Alavés','Oviedo',4,0),
  (82,'00/01',1,9,'Rayo Vallecano','Valladolid',2,1),
  (83,'00/01',1,9,'Espanyol','Valencia',1,0),
  (84,'00/01',1,9,'Celta','Málaga',1,0),
  (85,'00/01',1,9,'Osasuna','Ath. Bilbao',1,1),
  (86,'00/01',1,9,'Zaragoza','Rac. Santander',2,0),
  (87,'00/01',1,9,'Villarreal','Dptivo. Coruña',3,2),
  (88,'00/01',1,9,'Las Palmas','Barcelona',0,1),
  (89,'00/01',1,9,'Numancia','Real Madrid',3,1),
  (90,'00/01',1,9,'Real Sociedad','Mallorca',0,1),
  (91,'00/01',1,10,'Oviedo','Real Sociedad',1,0),
  (92,'00/01',1,10,'Valladolid','Alavés',2,1),
  (93,'00/01',1,10,'Valencia','Rayo Vallecano',2,2),
  (94,'00/01',1,10,'Málaga','Espanyol',0,0),
  (95,'00/01',1,10,'Ath. Bilbao','Celta',2,1),
  (96,'00/01',1,10,'Rac. Santander','Osasuna',0,0),
  (97,'00/01',1,10,'Dptivo. Coruña','Zaragoza',2,0),
  (98,'00/01',1,10,'Barcelona','Villarreal',1,2),
  (99,'00/01',1,10,'Real Madrid','Las Palmas',5,1),
  (100,'00/01',1,10,'Mallorca','Numancia',2,1),
  (101,'00/01',1,11,'Oviedo','Valladolid',4,1),
  (102,'00/01',1,11,'Alavés','Valencia',1,1),
  (103,'00/01',1,11,'Rayo Vallecano','Málaga',4,2),
  (104,'00/01',1,11,'Espanyol','Ath. Bilbao',2,1),
  (105,'00/01',1,11,'Celta','Rac. Santander',1,1),
  (106,'00/01',1,11,'Osasuna','Dptivo. Coruña',1,1),
  (107,'00/01',1,11,'Zaragoza','Barcelona',3,1),
  (108,'00/01',1,11,'Villarreal','Real Madrid',0,1),
  (109,'00/01',1,11,'Las Palmas','Mallorca',1,0),
  (110,'00/01',1,11,'Real Sociedad','Numancia',4,1),
  (111,'00/01',1,12,'Valladolid','Real Sociedad',2,1),
  (112,'00/01',1,12,'Valencia','Oviedo',2,0),
  (113,'00/01',1,12,'Málaga','Alavés',3,1),
  (114,'00/01',1,12,'Ath. Bilbao','Rayo Vallecano',4,2),
  (115,'00/01',1,12,'Rac. Santander','Espanyol',1,2),
  (116,'00/01',1,12,'Dptivo. Coruña','Celta',1,0),
  (117,'00/01',1,12,'Barcelona','Osasuna',2,0),
  (118,'00/01',1,12,'Real Madrid','Zaragoza',3,0),
  (119,'00/01',1,12,'Mallorca','Villarreal',2,1),
  (120,'00/01',1,12,'Numancia','Las Palmas',0,1),
  (121,'00/01',1,13,'Valladolid','Valencia',0,0),
  (122,'00/01',1,13,'Oviedo','Málaga',3,2),
  (123,'00/01',1,13,'Alavés','Ath. Bilbao',2,1),
  (124,'00/01',1,13,'Rayo Vallecano','Rac. Santander',4,1),
  (125,'00/01',1,13,'Espanyol','Dptivo. Coruña',0,2),
  (126,'00/01',1,13,'Celta','Barcelona',3,3),
  (127,'00/01',1,13,'Osasuna','Real Madrid',2,3),
  (128,'00/01',1,13,'Zaragoza','Mallorca',1,1),
  (129,'00/01',1,13,'Villarreal','Numancia',0,0),
  (130,'00/01',1,13,'Real Sociedad','Las Palmas',1,1),
  (131,'00/01',1,14,'Valencia','Real Sociedad',2,0),
  (132,'00/01',1,14,'Málaga','Valladolid',3,1),
  (133,'00/01',1,14,'Ath. Bilbao','Oviedo',4,0),
  (134,'00/01',1,14,'Rac. Santander','Alavés',2,1),
  (135,'00/01',1,14,'Dptivo. Coruña','Rayo Vallecano',1,1),
  (136,'00/01',1,14,'Barcelona','Espanyol',4,2),
  (137,'00/01',1,14,'Real Madrid','Celta',3,0),
  (138,'00/01',1,14,'Mallorca','Osasuna',1,1),
  (139,'00/01',1,14,'Numancia','Zaragoza',1,1),
  (140,'00/01',1,14,'Las Palmas','Villarreal',1,5),
  (141,'00/01',1,15,'Valencia','Málaga',2,0),
  (142,'00/01',1,15,'Valladolid','Ath. Bilbao',0,0),
  (143,'00/01',1,15,'Oviedo','Rac. Santander',1,0),
  (144,'00/01',1,15,'Alavés','Dptivo. Coruña',3,1),
  (145,'00/01',1,15,'Rayo Vallecano','Barcelona',2,2),
  (146,'00/01',1,15,'Espanyol','Real Madrid',1,2),
  (147,'00/01',1,15,'Celta','Mallorca',2,2),
  (148,'00/01',1,15,'Osasuna','Numancia',2,0),
  (149,'00/01',1,15,'Zaragoza','Las Palmas',3,1),
  (150,'00/01',1,15,'Real Sociedad','Villarreal',0,2),
  (151,'00/01',1,16,'Málaga','Real Sociedad',3,0),
  (152,'00/01',1,16,'Ath. Bilbao','Valencia',1,1),
  (153,'00/01',1,16,'Rac. Santander','Valladolid',2,2),
  (154,'00/01',1,16,'Dptivo. Coruña','Oviedo',3,0),
  (155,'00/01',1,16,'Barcelona','Alavés',3,2),
  (156,'00/01',1,16,'Real Madrid','Rayo Vallecano',3,1),
  (157,'00/01',1,16,'Mallorca','Espanyol',3,2),
  (158,'00/01',1,16,'Numancia','Celta',4,2),
  (159,'00/01',1,16,'Las Palmas','Osasuna',3,2),
  (160,'00/01',1,16,'Villarreal','Zaragoza',1,1),
  (161,'00/01',1,17,'Málaga','Ath. Bilbao',2,1),
  (162,'00/01',1,17,'Valencia','Rac. Santander',1,0),
  (163,'00/01',1,17,'Valladolid','Dptivo. Coruña',3,1),
  (164,'00/01',1,17,'Oviedo','Barcelona',2,3),
  (165,'00/01',1,17,'Alavés','Real Madrid',1,3),
  (166,'00/01',1,17,'Rayo Vallecano','Mallorca',2,2),
  (167,'00/01',1,17,'Espanyol','Numancia',2,0),
  (168,'00/01',1,17,'Celta','Las Palmas',0,1),
  (169,'00/01',1,17,'Osasuna','Villarreal',1,0),
  (170,'00/01',1,17,'Real Sociedad','Zaragoza',1,1),
  (171,'00/01',1,18,'Real Sociedad','Ath. Bilbao',0,2),
  (172,'00/01',1,18,'Rac. Santander','Málaga',0,1),
  (173,'00/01',1,18,'Dptivo. Coruña','Valencia',2,0),
  (174,'00/01',1,18,'Barcelona','Valladolid',3,1),
  (175,'00/01',1,18,'Real Madrid','Oviedo',4,0),
  (176,'00/01',1,18,'Mallorca','Alavés',4,3),
  (177,'00/01',1,18,'Numancia','Rayo Vallecano',0,2),
  (178,'00/01',1,18,'Las Palmas','Espanyol',1,0),
  (179,'00/01',1,18,'Villarreal','Celta',2,0),
  (180,'00/01',1,18,'Zaragoza','Osasuna',4,2),
  (181,'00/01',1,19,'Ath. Bilbao','Rac. Santander',3,1),
  (182,'00/01',1,19,'Málaga','Dptivo. Coruña',1,3),
  (183,'00/01',1,19,'Valencia','Barcelona',0,1),
  (184,'00/01',1,19,'Valladolid','Real Madrid',2,2),
  (185,'00/01',1,19,'Oviedo','Mallorca',1,1),
  (186,'00/01',1,19,'Alavés','Numancia',0,2),
  (187,'00/01',1,19,'Rayo Vallecano','Las Palmas',1,1),
  (188,'00/01',1,19,'Espanyol','Villarreal',2,1),
  (189,'00/01',1,19,'Celta','Zaragoza',1,1),
  (190,'00/01',1,19,'Osasuna','Real Sociedad',1,1),
  (191,'00/01',1,20,'Celta','Osasuna',1,0),
  (192,'00/01',1,20,'Espanyol','Zaragoza',5,0),
  (193,'00/01',1,20,'Rayo Vallecano','Villarreal',0,1),
  (194,'00/01',1,20,'Alavés','Las Palmas',1,0),
  (195,'00/01',1,20,'Oviedo','Numancia',3,0),
  (196,'00/01',1,20,'Valladolid','Mallorca',2,0),
  (197,'00/01',1,20,'Valencia','Real Madrid',0,1),
  (198,'00/01',1,20,'Málaga','Barcelona',0,0),
  (199,'00/01',1,20,'Ath. Bilbao','Dptivo. Coruña',2,2),
  (200,'00/01',1,20,'Rac. Santander','Real Sociedad',1,4),
  (201,'00/01',1,21,'Real Sociedad','Celta',2,2),
  (202,'00/01',1,21,'Osasuna','Espanyol',1,3),
  (203,'00/01',1,21,'Zaragoza','Rayo Vallecano',6,1),
  (204,'00/01',1,21,'Villarreal','Alavés',2,0),
  (205,'00/01',1,21,'Las Palmas','Oviedo',1,0),
  (206,'00/01',1,21,'Numancia','Valladolid',0,0),
  (207,'00/01',1,21,'Mallorca','Valencia',2,2),
  (208,'00/01',1,21,'Real Madrid','Málaga',4,3),
  (209,'00/01',1,21,'Barcelona','Ath. Bilbao',7,0),
  (210,'00/01',1,21,'Dptivo. Coruña','Rac. Santander',2,1),
  (211,'00/01',1,22,'Espanyol','Celta',0,1),
  (212,'00/01',1,22,'Rayo Vallecano','Osasuna',2,1),
  (213,'00/01',1,22,'Alavés','Zaragoza',1,0),
  (214,'00/01',1,22,'Oviedo','Villarreal',1,3),
  (215,'00/01',1,22,'Valladolid','Las Palmas',1,0),
  (216,'00/01',1,22,'Valencia','Numancia',3,0),
  (217,'00/01',1,22,'Málaga','Mallorca',0,1),
  (218,'00/01',1,22,'Ath. Bilbao','Real Madrid',1,0),
  (219,'00/01',1,22,'Rac. Santander','Barcelona',4,0),
  (220,'00/01',1,22,'Dptivo. Coruña','Real Sociedad',4,1),
  (221,'00/01',1,23,'Real Sociedad','Espanyol',2,1),
  (222,'00/01',1,23,'Celta','Rayo Vallecano',1,1),
  (223,'00/01',1,23,'Osasuna','Alavés',0,1),
  (224,'00/01',1,23,'Zaragoza','Oviedo',5,2),
  (225,'00/01',1,23,'Villarreal','Valladolid',2,1),
  (226,'00/01',1,23,'Las Palmas','Valencia',0,2),
  (227,'00/01',1,23,'Numancia','Málaga',3,2),
  (228,'00/01',1,23,'Mallorca','Ath. Bilbao',1,0),
  (229,'00/01',1,23,'Real Madrid','Rac. Santander',1,0),
  (230,'00/01',1,23,'Barcelona','Dptivo. Coruña',2,3),
  (231,'00/01',1,24,'Rayo Vallecano','Espanyol',1,1),
  (232,'00/01',1,24,'Alavés','Celta',2,2),
  (233,'00/01',1,24,'Oviedo','Osasuna',2,3),
  (234,'00/01',1,24,'Valladolid','Zaragoza',2,0),
  (235,'00/01',1,24,'Valencia','Villarreal',3,1),
  (236,'00/01',1,24,'Málaga','Las Palmas',2,1),
  (237,'00/01',1,24,'Ath. Bilbao','Numancia',3,1),
  (238,'00/01',1,24,'Rac. Santander','Mallorca',2,1),
  (239,'00/01',1,24,'Dptivo. Coruña','Real Madrid',2,2),
  (240,'00/01',1,24,'Barcelona','Real Sociedad',3,0),
  (241,'00/01',1,25,'Real Sociedad','Rayo Vallecano',2,0),
  (242,'00/01',1,25,'Espanyol','Alavés',0,0),
  (243,'00/01',1,25,'Celta','Oviedo',1,0),
  (244,'00/01',1,25,'Osasuna','Valladolid',2,1),
  (245,'00/01',1,25,'Zaragoza','Valencia',1,1),
  (246,'00/01',1,25,'Villarreal','Málaga',1,2),
  (247,'00/01',1,25,'Las Palmas','Ath. Bilbao',0,0),
  (248,'00/01',1,25,'Numancia','Rac. Santander',1,0),
  (249,'00/01',1,25,'Mallorca','Dptivo. Coruña',2,1),
  (250,'00/01',1,25,'Real Madrid','Barcelona',2,2),
  (251,'00/01',1,26,'Alavés','Rayo Vallecano',4,2),
  (252,'00/01',1,26,'Oviedo','Espanyol',2,2),
  (253,'00/01',1,26,'Valladolid','Celta',1,2),
  (254,'00/01',1,26,'Valencia','Osasuna',1,0),
  (255,'00/01',1,26,'Málaga','Zaragoza',2,0),
  (256,'00/01',1,26,'Ath. Bilbao','Villarreal',1,1),
  (257,'00/01',1,26,'Rac. Santander','Las Palmas',2,1),
  (258,'00/01',1,26,'Dptivo. Coruña','Numancia',4,1),
  (259,'00/01',1,26,'Barcelona','Mallorca',1,1),
  (260,'00/01',1,26,'Real Madrid','Real Sociedad',4,0),
  (261,'00/01',1,27,'Real Sociedad','Alavés',1,1),
  (262,'00/01',1,27,'Rayo Vallecano','Oviedo',0,2),
  (263,'00/01',1,27,'Espanyol','Valladolid',1,0),
  (264,'00/01',1,27,'Celta','Valencia',3,2),
  (265,'00/01',1,27,'Osasuna','Málaga',3,3),
  (266,'00/01',1,27,'Zaragoza','Ath. Bilbao',2,2),
  (267,'00/01',1,27,'Villarreal','Rac. Santander',4,2),
  (268,'00/01',1,27,'Las Palmas','Dptivo. Coruña',2,0),
  (269,'00/01',1,27,'Numancia','Barcelona',1,1),
  (270,'00/01',1,27,'Mallorca','Real Madrid',1,0),
  (271,'00/01',1,28,'Oviedo','Alavés',3,3),
  (272,'00/01',1,28,'Valladolid','Rayo Vallecano',1,3),
  (273,'00/01',1,28,'Valencia','Espanyol',0,1),
  (274,'00/01',1,28,'Málaga','Celta',1,4),
  (275,'00/01',1,28,'Ath. Bilbao','Osasuna',0,1),
  (276,'00/01',1,28,'Rac. Santander','Zaragoza',2,1),
  (277,'00/01',1,28,'Dptivo. Coruña','Villarreal',4,2),
  (278,'00/01',1,28,'Barcelona','Las Palmas',4,1),
  (279,'00/01',1,28,'Real Madrid','Numancia',1,0),
  (280,'00/01',1,28,'Mallorca','Real Sociedad',1,1),
  (281,'00/01',1,29,'Real Sociedad','Oviedo',3,0),
  (282,'00/01',1,29,'Alavés','Valladolid',4,2),
  (283,'00/01',1,29,'Rayo Vallecano','Valencia',1,4),
  (284,'00/01',1,29,'Espanyol','Málaga',1,2),
  (285,'00/01',1,29,'Celta','Ath. Bilbao',2,1),
  (286,'00/01',1,29,'Osasuna','Rac. Santander',1,1),
  (287,'00/01',1,29,'Zaragoza','Dptivo. Coruña',2,1),
  (288,'00/01',1,29,'Villarreal','Barcelona',4,4),
  (289,'00/01',1,29,'Las Palmas','Real Madrid',0,1),
  (290,'00/01',1,29,'Numancia','Mallorca',0,2),
  (291,'00/01',1,30,'Valladolid','Oviedo',1,0),
  (292,'00/01',1,30,'Valencia','Alavés',1,2),
  (293,'00/01',1,30,'Málaga','Rayo Vallecano',1,1),
  (294,'00/01',1,30,'Ath. Bilbao','Espanyol',0,1),
  (295,'00/01',1,30,'Rac. Santander','Celta',3,0),
  (296,'00/01',1,30,'Dptivo. Coruña','Osasuna',2,1),
  (297,'00/01',1,30,'Barcelona','Zaragoza',4,4),
  (298,'00/01',1,30,'Real Madrid','Villarreal',4,0),
  (299,'00/01',1,30,'Mallorca','Las Palmas',2,1),
  (300,'00/01',1,30,'Numancia','Real Sociedad',3,3),
  (301,'00/01',1,31,'Real Sociedad','Valladolid',3,1),
  (302,'00/01',1,31,'Oviedo','Valencia',0,0),
  (303,'00/01',1,31,'Alavés','Málaga',1,2),
  (304,'00/01',1,31,'Rayo Vallecano','Ath. Bilbao',1,2),
  (305,'00/01',1,31,'Espanyol','Rac. Santander',3,0),
  (306,'00/01',1,31,'Celta','Dptivo. Coruña',2,1),
  (307,'00/01',1,31,'Osasuna','Barcelona',3,1),
  (308,'00/01',1,31,'Zaragoza','Real Madrid',2,3),
  (309,'00/01',1,31,'Villarreal','Mallorca',2,2),
  (310,'00/01',1,31,'Las Palmas','Numancia',1,1),
  (311,'00/01',1,32,'Valencia','Valladolid',1,0),
  (312,'00/01',1,32,'Málaga','Oviedo',2,2),
  (313,'00/01',1,32,'Ath. Bilbao','Alavés',2,0),
  (314,'00/01',1,32,'Rac. Santander','Rayo Vallecano',1,1),
  (315,'00/01',1,32,'Dptivo. Coruña','Espanyol',1,0),
  (316,'00/01',1,32,'Barcelona','Celta',1,1),
  (317,'00/01',1,32,'Real Madrid','Osasuna',1,1),
  (318,'00/01',1,32,'Mallorca','Zaragoza',2,1),
  (319,'00/01',1,32,'Numancia','Villarreal',1,3),
  (320,'00/01',1,32,'Las Palmas','Real Sociedad',2,1),
  (321,'00/01',1,33,'Real Sociedad','Valencia',1,2),
  (322,'00/01',1,33,'Valladolid','Málaga',0,0),
  (323,'00/01',1,33,'Oviedo','Ath. Bilbao',5,0),
  (324,'00/01',1,33,'Alavés','Rac. Santander',5,1),
  (325,'00/01',1,33,'Rayo Vallecano','Dptivo. Coruña',1,1),
  (326,'00/01',1,33,'Espanyol','Barcelona',0,0),
  (327,'00/01',1,33,'Celta','Real Madrid',3,0),
  (328,'00/01',1,33,'Osasuna','Mallorca',1,0),
  (329,'00/01',1,33,'Zaragoza','Numancia',3,1),
  (330,'00/01',1,33,'Villarreal','Las Palmas',2,1),
  (331,'00/01',1,34,'Málaga','Valencia',3,0),
  (332,'00/01',1,34,'Ath. Bilbao','Valladolid',1,1),
  (333,'00/01',1,34,'Rac. Santander','Oviedo',2,0),
  (334,'00/01',1,34,'Dptivo. Coruña','Alavés',2,1),
  (335,'00/01',1,34,'Barcelona','Rayo Vallecano',5,1),
  (336,'00/01',1,34,'Real Madrid','Espanyol',2,2),
  (337,'00/01',1,34,'Mallorca','Celta',2,0),
  (338,'00/01',1,34,'Numancia','Osasuna',1,0),
  (339,'00/01',1,34,'Las Palmas','Zaragoza',2,1),
  (340,'00/01',1,34,'Villarreal','Real Sociedad',1,3),
  (341,'00/01',1,35,'Real Sociedad','Málaga',4,0),
  (342,'00/01',1,35,'Valencia','Ath. Bilbao',1,0),
  (343,'00/01',1,35,'Valladolid','Rac. Santander',1,1),
  (344,'00/01',1,35,'Oviedo','Dptivo. Coruña',2,3),
  (345,'00/01',1,35,'Alavés','Barcelona',0,1),
  (346,'00/01',1,35,'Rayo Vallecano','Real Madrid',0,1),
  (347,'00/01',1,35,'Espanyol','Mallorca',0,1),
  (348,'00/01',1,35,'Celta','Numancia',1,1),
  (349,'00/01',1,35,'Osasuna','Las Palmas',3,3),
  (350,'00/01',1,35,'Zaragoza','Villarreal',0,0),
  (351,'00/01',1,36,'Ath. Bilbao','Málaga',1,3),
  (352,'00/01',1,36,'Rac. Santander','Valencia',1,1),
  (353,'00/01',1,36,'Dptivo. Coruña','Valladolid',1,2),
  (354,'00/01',1,36,'Barcelona','Oviedo',0,1),
  (355,'00/01',1,36,'Real Madrid','Alavés',5,0),
  (356,'00/01',1,36,'Mallorca','Rayo Vallecano',4,0),
  (357,'00/01',1,36,'Numancia','Espanyol',2,1),
  (358,'00/01',1,36,'Las Palmas','Celta',0,1),
  (359,'00/01',1,36,'Villarreal','Osasuna',2,0),
  (360,'00/01',1,36,'Zaragoza','Real Sociedad',1,1),
  (361,'00/01',1,37,'Ath. Bilbao','Real Sociedad',1,3),
  (362,'00/01',1,37,'Málaga','Rac. Santander',2,1),
  (363,'00/01',1,37,'Valencia','Dptivo. Coruña',0,1),
  (364,'00/01',1,37,'Valladolid','Barcelona',2,2),
  (365,'00/01',1,37,'Oviedo','Real Madrid',1,1),
  (366,'00/01',1,37,'Alavés','Mallorca',2,4),
  (367,'00/01',1,37,'Rayo Vallecano','Numancia',2,1),
  (368,'00/01',1,37,'Espanyol','Las Palmas',3,2),
  (369,'00/01',1,37,'Celta','Villarreal',1,0),
  (370,'00/01',1,37,'Osasuna','Zaragoza',1,0),
  (371,'00/01',1,38,'Rac. Santander','Ath. Bilbao',3,0),
  (372,'00/01',1,38,'Dptivo. Coruña','Málaga',4,0),
  (373,'00/01',1,38,'Barcelona','Valencia',3,2),
  (374,'00/01',1,38,'Real Madrid','Valladolid',2,1),
  (375,'00/01',1,38,'Mallorca','Oviedo',4,2),
  (376,'00/01',1,38,'Numancia','Alavés',2,1),
  (377,'00/01',1,38,'Las Palmas','Rayo Vallecano',1,0),
  (378,'00/01',1,38,'Villarreal','Espanyol',4,0),
  (379,'00/01',1,38,'Zaragoza','Celta',1,1),
  (380,'00/01',1,38,'Real Sociedad','Osasuna',0,1);
go
--insertamos los equipos
insert Equipos (Nombre, Pos_Juego_Limpio)
select  distinct EQUIPO_LOCAL, 0 from [Resumen 2001] order by 1
go
--La clasificación del juego limpio, es ficticia, le asigno el id
update Equipos set Pos_Juego_Limpio = IdEquipo
go

--Consolidación de los partidos
insert Partidos_Equipo
select IdPartido = ID, IdEquipo = E.IdEquipo, IdEquipo_Contrario = V.IdEquipo, Es_Local = 1,
  Goles_A_Favor = R.GOLES_LOCAL, Goles_En_Contra = R.GOLES_VISITANTE,
  Puntos = case
     when R.GOLES_LOCAL = R.GOLES_VISITANTE then 1
     when R.GOLES_LOCAL > R.GOLES_VISITANTE then 3
     when R.GOLES_LOCAL < R.GOLES_VISITANTE then 0 
 end
from
  [Resumen 2001]  R inner join
  Equipos E on R.EQUIPO_LOCAL = E.Nombre inner join
  Equipos V on R.EQUIPO_VISITANTE = V.Nombre
union all
select IdPartido = ID, IdEquipo = V.IdEquipo, IdEquipo_Contrario = E.IdEquipo, Es_Local = 0,
  Goles_A_Favor = R.GOLES_VISITANTE, Goles_En_Contra = R.GOLES_LOCAL,
  Puntos = case
     when R.GOLES_LOCAL = R.GOLES_VISITANTE then 1
     when R.GOLES_LOCAL > R.GOLES_VISITANTE then 0
     when R.GOLES_LOCAL < R.GOLES_VISITANTE then 3 
 end
from
  [Resumen 2001]  R inner join
  Equipos E on R.EQUIPO_LOCAL = E.Nombre inner join
  Equipos V on R.EQUIPO_VISITANTE = V.Nombre
Order by ID

go

--Procedimiento almacenado para actualizar la clasificación
alter proc p_Refrescar_Clasificacion as

set nocount on
--Construir la clasificación
truncate table Clasificacion

insert Clasificacion (IdEquipo, PJ, PG, PE, PP, GF, GC, DG, Puntos, Desempate)
select E.IdEquipo,
  PJ = count(IdPartido),
  PG = sum(case when Puntos = 3 then 1 else 0 end),
  PE = sum(case when Puntos = 1 then 1 else 0 end),
  PP = sum(case when Puntos = 0 then 1 else 0 end),
  GF = SUM(P.Goles_A_Favor),
  GC = sum(P.Goles_En_Contra),
  DG = SUM(P.Goles_A_Favor) - sum(P.Goles_En_Contra),
  Puntos = SUM(P.Puntos),
  Desempate = 0
from Equipos E inner join Partidos_Equipo P on E.IdEquipo = P.IdEquipo
group by E.IdEquipo

--Empates
while exists (select top 1 Puntos from Clasificacion where Desempate = 0 group by Puntos having COUNT(IdEquipo) > 1)
  begin
    begin tran
    ;with Clas as (
      select
        C.IdEquipo, DG, GF, E.Pos_Juego_Limpio
      from Clasificacion C inner join Equipos E on C.IdEquipo = E.IdEquipo
      where Puntos in (select top 1 Puntos from Clasificacion where Desempate = 0 group by Puntos having COUNT(IdEquipo) > 1))
      , Criterios as (
      select
        P.IdEquipo,
        Puntos = SUM(P.Puntos), --1er criterio
        DG = SUM(P.Goles_A_Favor) - SUM(Goles_En_Contra), --2º criterio
        DG_General = C.DG, --3er criterio
        GF_General = C.GF, --4º criterio
        C.Pos_Juego_Limpio --5º criterio
      from
        Partidos_Equipo P inner join
        Clas C on P.IdEquipo = C.IdEquipo
      where P.IdEquipo_Contrario in (select IdEquipo from Clas)
      group by P.IdEquipo, C.DG, C.GF, C.Pos_Juego_Limpio)
      , Desempate as (
      select IdEquipo,
        Desempate = ROW_NUMBER() over (order by Puntos desc, DG desc, DG_General desc, GF_General, Pos_Juego_Limpio asc)
      from Criterios)
      update C set Desempate = D.Desempate
      from Clasificacion C inner join Desempate D on C.IdEquipo = D.IdEquipo

    commit
  end
return 0
go

--Actualización de la clasificación
exec p_Refrescar_Clasificacion

--Obtención de la misma
select E.Nombre, PJ, PG, PE, PP, GF, GC, DG, Puntos
from
   Clasificacion C inner join Equipos E on C.IdEquipo = E.IdEquipo
order by Puntos desc, Desempate asc

No me prodigo mucho en Reporting Services, así que cada vez que me toca pasar por allí aprendo una cuantas cosas. En mi último viaje, me pidieron algo trivial, que se enviara un listado en formato Excel por mail.

Hasta ahí, pues muy sencillo. La complicación vino cuando me pidieron que en el mail se adecuaran de forma dinámica algunas cosas, como incluir el rango de fechas del informe en el asunto y en el cuerpo del mensaje y dotar al fichero de un nombre que incluyera esas características.

Después de ver hasta dónde se podía llegar con una Data-driven subscription, comprobé que se pueden hacer cosas, pero no demasiadas, y desde luego no las que yo necesitaba. Así que hice lo que todos, porque quizá no sepa mucho de SSRS, pero sí sé buscar en Google. Además, esta necesidad es bastante simple a mi entender, con lo que contaba con que no era el primer caso en la historia del mundo mundial en el que a alguien se le ocurría que el fichero a adjuntar a un mail tuviera un nombre que no fuera el del informe.

Y me sorprendió ver que, si bien el problema era común, no había una solución simple. Me costó encontrarlo, y la alternativa no es muy ortodoxa que digamos (ni soportada) así que me pareció interesante compartirlo. Hay varias entradas similares en la web, yo partí de la siguiente: http://www.sqlservercentral.com/scripts/Reporting+Services+(SSRS)/70387/.

Esa es la pista. Hay que crear una suscripción normal y corriente. Luego encontrar el job que la lanza y modificarlo. Antes de lanzar la suscripción, se personaliza (ojo, conservando una referencia de lo que se ha cambiado):

declare @reportID uniqueidentifier, @subscriptionID uniqueidentifier, @path nvarchar(255), @body nvarchar(max), @asunto nvarchar(max)

SELECT
  @reportID  = rs.reportID
, @path = '/Listados/Listado_Semana_' + convert(char(8), dateadd(dd, -7, getdate()), 112)+ '_' + convert(char(8), dateadd(dd, -1, getdate()), 112)
, @subscriptionID = rs.SubscriptionId
,  @body = 'Se adjunta listado con todos los datos de la última semana (del ' +
  substring(convert(char(8), dateadd(dd, -7, getdate()), 112), 7, 2) + '/' +
  substring(convert(char(8), dateadd(dd, -7, getdate()), 112), 5, 2) + '/' +
  substring(convert(char(8), dateadd(dd, -7, getdate()), 112), 1, 4) + ' al ' +
  substring(convert(char(8), dateadd(dd, -1, getdate()), 112), 7, 2) + '/' +
  substring(convert(char(8), dateadd(dd, -1, getdate()), 112), 5, 2) + '/' +
  substring(convert(char(8), dateadd(dd, -1, getdate()), 112), 1, 4) + ').',
  @asunto = 'Listado de datos de la semana del ' +
  substring(convert(char(8), dateadd(dd, -7, getdate()), 112), 7, 2) + '/' +
  substring(convert(char(8), dateadd(dd, -7, getdate()), 112), 5, 2) + '/' +
  substring(convert(char(8), dateadd(dd, -7, getdate()), 112), 1, 4) + ' al ' +
  substring(convert(char(8), dateadd(dd, -1, getdate()), 112), 7, 2) + '/' +
  substring(convert(char(8), dateadd(dd, -1, getdate()), 112), 5, 2) + '/' +
  substring(convert(char(8), dateadd(dd, -1, getdate()), 112), 1, 4)
FROM
        ReportSchedule rs
            INNER JOIN subscriptions s
                ON rs.subscriptionID = s.subscriptionID
            INNER JOIN dbo.[catalog] c
                ON rs.reportID = c.itemID
WHERE
  c.[path] = '/Listados/Listado Última Semana'

update dbo.[Catalog] set [path] = @path where itemID = @reportID

update subscriptions set
   ExtensionSettings = 
      replace(replace(cast(ExtensionSettings as nvarchar(max)), 'Se adjunta listado con todos los datos de la última semana.', @body), '@ReportName', @asunto)
where SubscriptionID = @subscriptionID

exec dbo.AddEvent @EventType='TimedSubscription', @EventData=@subscriptionID

Luego añadimos un segundo paso al job para dejar las cosas como estaban (yo le he añadido un delay de 1 minuto, que seguramente se pueda quitar, es para asegurarse de que el informe sale antes de volver a dejar cambiados los datos otra vez). Así, el segundo paso de ese job sería más o menos así:

waitfor delay '00:01'

--Ahora lo dejo como estaba
declare @reportID uniqueidentifier, @subscriptionID uniqueidentifier, @path nvarchar(255), @body nvarchar(max), @asunto nvarchar(max)

SELECT
  @reportID  = rs.reportID
, @path = '/Listados/Listado Última Semana'
, @subscriptionID = rs.SubscriptionId
,  @body = 'Se adjunta listado con todos los datos de la última semana (del ' +
  substring(convert(char(8), dateadd(dd, -7, getdate()), 112), 7, 2) + '/' +
  substring(convert(char(8), dateadd(dd, -7, getdate()), 112), 5, 2) + '/' +
  substring(convert(char(8), dateadd(dd, -7, getdate()), 112), 1, 4) + ' al ' +
  substring(convert(char(8), dateadd(dd, -1, getdate()), 112), 7, 2) + '/' +
  substring(convert(char(8), dateadd(dd, -1, getdate()), 112), 5, 2) + '/' +
  substring(convert(char(8), dateadd(dd, -1, getdate()), 112), 1, 4) + ').',
  @asunto = 'Listado de datos de la semana del ' +
  substring(convert(char(8), dateadd(dd, -7, getdate()), 112), 7, 2) + '/' +
  substring(convert(char(8), dateadd(dd, -7, getdate()), 112), 5, 2) + '/' +
  substring(convert(char(8), dateadd(dd, -7, getdate()), 112), 1, 4) + ' al ' +
  substring(convert(char(8), dateadd(dd, -1, getdate()), 112), 7, 2) + '/' +
  substring(convert(char(8), dateadd(dd, -1, getdate()), 112), 5, 2) + '/' +
  substring(convert(char(8), dateadd(dd, -1, getdate()), 112), 1, 4)
FROM
        ReportSchedule rs
            INNER JOIN subscriptions s
                ON rs.subscriptionID = s.subscriptionID
            INNER JOIN dbo.[catalog] c
                ON rs.reportID = c.itemID
WHERE
  c.[path] = '/Listado/Listado_Semana_' + convert(char(8), dateadd(dd, -7, getdate()), 112)+ '_' + convert(char(8), dateadd(dd, -1, getdate()), 112)

update dbo.[Catalog] set [path] = @path where itemID = @reportID

update subscriptions set
   ExtensionSettings = 
      replace(replace(cast(ExtensionSettings as nvarchar(max)), @body, 'Se adjunta listado con todos los datos de la última semana.'), @asunto, '@ReportName')
where SubscriptionID = @subscriptionID

Y ya está, hemos personalizado el asunto, el cuerpo del mensaje y el nombre del fichero. Pero con la misma táctica puede modificarse lo que se precise.

A raíz de un hilo del foro de SQL Server, en el que una persona preguntaba sobre cómo rotar un result set (link), sin contar con mucha experiencia en T-SQL, he preparado una pequeña guía que permita configurar estas tareas, algo complicadas, de una forma más sencilla.

Empecemos por los datos de ejemplo (provienen del mismo hilo): descargar

Y poco más que añadir que el propio código:

DECLARE @cols varchar(max), @cols_isnull varchar(max), @query nvarchar(max)

DECLARE @cod_semestre int, @cod_periodo int, @query_a_pivotar nvarchar(max),
  @campo_piv nvarchar(max), @campo_calc nvarchar(max), @campos_adicionales nvarchar(max)

--Sentencia parametrizada que se desea pivotar
select @query_a_pivotar = N'SELECT
  Persona.Apellido_1 +'' ''+ Persona.Apellido_2 +'', ''+ Persona.Nombres as Nombres,
  Semestre.Semestre, Semestre.Paralelo,
  Asignatura.Nombre AS Asignatura,
  Matricula_Asignatura.Estado
FROM
  Persona INNER JOIN
  Alumno ON Persona.Id_Persona = Alumno.Id_Persona INNER JOIN
  Matricula ON Alumno.Id_Alumno = Matricula.Id_Alumno INNER JOIN
  Matricula_Asignatura ON
     Matricula.Cod_Matricula = Matricula_Asignatura.Cod_Matricula INNER JOIN
  Asignatura ON Matricula_Asignatura.Cod_Asignatura = Asignatura.Cod_Asignatura INNER JOIN
  Distribucion ON
     Asignatura.Cod_Asignatura = Distribucion.Cod_Asignatura and
     Matricula.Cod_Periodo = Distribucion.Cod_Periodo INNER JOIN
  Semestre ON
     Matricula.Cod_Semestre = Semestre.Cod_Semestre AND
     Distribucion.Cod_Semestre = Semestre.Cod_Semestre
WHERE
  Matricula.Cod_Semestre = @pCod_Semestre AND Matricula.Cod_Periodo = @pCod_Periodo'

--Variables
select
  @cod_semestre = 27, @cod_periodo = 24,
  @campo_calc = 'max(Estado)',
  @campo_piv = 'Asignatura',
  @campos_adicionales = 'Nombres, Semestre, Paralelo'

; with ListaCols as (
  --> Incluye aquí la sentencia que devolvería la lista de columnas
  --> (llama "Cols" a dicho campo)
   Select Cols = A.Nombre
   from
     Distribucion D inner join
     Asignatura A on D.Cod_Asignatura = A.Cod_Asignatura
   where
     D.Cod_Semestre = @cod_semestre and
     D.Cod_Periodo = @cod_periodo
     )
--Construimos dos cadenas, para el caso en que quieran tratarse valores nulos
--En este ejemplo, es un campo alfanumérico
--Si fuera numérico el campo a computar, cambiar <''-''> por <'0'>
--Si no hay que gestionar nulos,
--sustituir @cols_isnull por @cols en el scritp del pivot dinámico
SELECT
  @cols = STUFF((
   SELECT '],[' + Cols
   from ListaCols
   ORDER BY Cols FOR XML PATH('')), 1, 2, '') + ']',
  @cols_isnull = STUFF((
   SELECT '], isnull([' + Cols + '], ''-'') as [' + Cols
   from ListaCols
   ORDER BY Cols FOR XML PATH('')), 1, 2, '') + ']'

--Construcción del pivot dinámico
SELECT @query = N'SELECT ' + @campos_adicionales +', ' + @cols_isnull +'
FROM
(' + @query_a_pivotar + ') p
PIVOT
( '+ @campo_calc + '
FOR ' + @campo_piv + ' IN
( '+ @cols +' )
) AS pvt
ORDER BY Nombres;'

--Por último, lo ejecutamos
exec sp_executesql @query,
  N'@pCod_Semestre int, @pCod_Periodo int', @cod_semestre, @cod_periodo

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.plugin cookies

ACEPTAR
Aviso de cookies