Forums Últimos mensajes - Powered by IBM
 

Trigger con select dinamicos o campos text

17/04/2006 - 17:13 por Elcritico | Informe spam
Estoy intentando crear un registro de auditoria para las tablas operativas,
en el formato

Tabla AudOper
hostname ( varchar) -> Nombre de equipo
loginame ( varchar) -> Nombre del Usuario
clave ( varchar) -> clave del registro
campo ( varchar) -> Nombre del campo
Antes (sql_variant ) -> dato antes de modificacion
Despues (sql_variant ) -> dato despues de modificacion
fecaccion (datetime) -> fecha de la modificacion

Como solemos agregar frecuentemente campos, no quiero tener un "Listado" de
campos a comparar, por ello utilizo un cursor.
Con las recientes pruebas descubri que a los trigger no le gustan los campos
text, ntext, o image, en otras palabras ni los lee y da error.

Como se ve en el codigo que acompaña, lo que intente fue crear un cursor que
lea la estructura de la tabla que quiero recorrer, y omitiendo los campos
text compare los datos y los agregue a mi tabla de auditoria. el problema es
que SQL no puede "ejecutar" una consulta dinamica sobre las tablas inserted o
deleted.

Que me sugieren?? le ve alguna solucion a este problema o me veo obligado a
crear manualmente la lista de campos y con ello la modificacion del trigger
por cada campo nuevo?


SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO

ALTER TRIGGER UpdAudiOperEMH ON expohbl
FOR UPDATE
AS
SET NOCOUNT ON

DECLARE antes_cursor CURSOR FOR
select column_name as field
from information_schema.columns c where c.table_name='expohbl' AND
C.COLUMN_NAME<>'TIMESTAMP_COLUMN' and data_type <> 'text'


DECLARE @column_name varchar(25), @lcSQL varchar(1500), @lcSQL2
varchar(1500), @message varchar(180), @lccampo1 varchar(50), @lccampo2
varchar(50)
OPEN antes_cursor

FETCH NEXT FROM antes_cursor
INTO @column_name

WHILE @@FETCH_STATUS = 0
BEGIN
'''+@column_name+''',a.'+@column_name+' as antes ,b.'+@column_name+' as
despues , getdate() from inserted a inner join deleted b on( a.hme_numeroint
= b.hme_numeroint and a.'+@column_name+' not like b.'+@column_name+' )inner
join dbo.fn_who(null) on (spid=@@spid) '

/* set @lccampo1 = case when dbo.type('expohbl',@column_name) = 'M' then
'convert(char(70),a.'+@column_name+')' else
'a.'+@column_name end
set @lccampo2 = case when dbo.type('expohbl',@column_name) = 'M' then
'convert(char(70),b.'+@column_name+')' else
'b.'+@column_name end
set @lcSQL=' insert into audoper select hostname,
loginame,a.hme_numeroint, '''+@column_name+''', '
+ @lccampo1 + ' as antes , '
+ @lccampo2 + ' as despues ,
getdate() from inserted a inner join deleted b on( a.hme_numeroint =
b.hme_numeroint and a.'+@column_name+' not like b.'+@column_name+' )inner
join dbo.fn_who(null) on (spid=@@spid) '
*/
set @lcSQL= ' insert into audoper select hostname,
loginame,a.hme_numeroint, '''+@column_name+''', '+
'a.'+@column_name + ' as antes , ' +
'b.'+@column_name + ' as despues ,
getdate() from inserted a inner join deleted b on( a.hme_numeroint =
b.hme_numeroint and a.'+@column_name+' not like b.'+@column_name+' )inner
join dbo.fn_who(null) on (spid=@@spid) '

exec(@lcSQL)
FETCH NEXT FROM antes_cursor
INTO @column_name
END

CLOSE antes_cursor
DEALLOCATE antes_cursor

GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO



PD: el porque uso @@spid en este ejemplo se debe a que en algunos clientes
usa autenficacion NT y otros con usuario "SA".

La funcion fn_who()

SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS OFF
GO

create function fn_who ( @loginame as sysname=null)
returns @t table (spid int,ecid int,status varchar(25),
loginame sysname null,hostname sysname null,blk int,
dbname sysname null,cmd sysname null)
as
begin
declare @spidlow int,
@spidhigh int,
@spid int,
@sid varbinary(85)
select @spidlow = 0 ,@spidhigh = 32767

if ( @loginame is not NULL AND upper(@loginame) = 'ACTIVE')
begin
insert into @t
select spid , ecid, status
,loginame=rtrim(loginame)
,hostname ,blk=convert(char(5),blocked)
,dbname = case when dbid = 0 then null
when dbid <> 0 then db_name(dbid)
end ,cmd
from master.dbo.sysprocesses
where spid >= @spidlow and spid <= @spidhigh AND
upper(cmd) <> 'AWAITING COMMAND'
return
end
if (@loginame is not NULL AND upper(@loginame) <> 'ACTIVE' )
begin
if (@loginame like '[0-9]%') -- is a spid.
begin
select @spid = convert(int, @loginame)
insert into @t
select spid, ecid, status,
loginame=rtrim(loginame),
hostname,blk = convert(char(5),blocked),
dbname = case
when dbid = 0 then null
when dbid <> 0 then
db_name(dbid)
end ,cmd
from master.dbo.sysprocesses
where spid = @spid
end
else
begin
select @sid = suser_sid(@loginame)
if (@sid is null)
begin
return
end
insert into @t
select spid, ecid, status,
loginame=rtrim(loginame),
hostname ,blk=convert(char(5),blocked),
dbname = case
when dbid = 0 then null
when dbid <> 0 then db_name(dbid)
end
,cmd
from master.dbo.sysprocesses
where sid = @sid
end
return
end

/* loginame arg is null */
insert into @t
select spid,
ecid,
status,
loginame=rtrim(loginame),
hostname,
blk=convert(char(5),blocked),
dbname = case
when dbid = 0 then null
when dbid <> 0 then db_name(dbid)
end
,cmd
from master.dbo.sysprocesses
where spid >= @spidlow and spid <= @spidhigh

return -- sp_who
end

GO

SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO





Saludos y Muchas Gracias por sus tiempos.

Lic. Flavio A. Dellarupe<br>
Visual Fox Developer
Bs.As.-Argentina
 

Leer las respuestas

#1 Maxi
17/04/2006 - 17:23 | Informe spam
Hola, no creo q sea para nada una buena idea esto tan generico, atenta
contra la seguridad y la performance del sistema


Salu2
Maxi [MVP SQL SERVER]
www.sqlgurus.org


"Elcritico" escribió en el mensaje
news:
Estoy intentando crear un registro de auditoria para las tablas
operativas,
en el formato

Tabla AudOper
hostname ( varchar) -> Nombre de equipo
loginame ( varchar) -> Nombre del Usuario
clave ( varchar) -> clave del registro
campo ( varchar) -> Nombre del campo
Antes (sql_variant ) -> dato antes de modificacion
Despues (sql_variant ) -> dato despues de modificacion
fecaccion (datetime) -> fecha de la modificacion

Como solemos agregar frecuentemente campos, no quiero tener un "Listado"
de
campos a comparar, por ello utilizo un cursor.
Con las recientes pruebas descubri que a los trigger no le gustan los
campos
text, ntext, o image, en otras palabras ni los lee y da error.

Como se ve en el codigo que acompaña, lo que intente fue crear un cursor
que
lea la estructura de la tabla que quiero recorrer, y omitiendo los campos
text compare los datos y los agregue a mi tabla de auditoria. el problema
es
que SQL no puede "ejecutar" una consulta dinamica sobre las tablas
inserted o
deleted.

Que me sugieren?? le ve alguna solucion a este problema o me veo obligado
a
crear manualmente la lista de campos y con ello la modificacion del
trigger
por cada campo nuevo?


SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO

ALTER TRIGGER UpdAudiOperEMH ON expohbl
FOR UPDATE
AS
SET NOCOUNT ON

DECLARE antes_cursor CURSOR FOR
select column_name as field
from information_schema.columns c where c.table_name='expohbl' AND
C.COLUMN_NAME<>'TIMESTAMP_COLUMN' and data_type <> 'text'


DECLARE @column_name varchar(25), @lcSQL varchar(1500), @lcSQL2
varchar(1500), @message varchar(180), @lccampo1 varchar(50), @lccampo2
varchar(50)
OPEN antes_cursor

FETCH NEXT FROM antes_cursor
INTO @column_name

WHILE @@FETCH_STATUS = 0
BEGIN
'''+@column_name+''',a.'+@column_name+' as antes ,b.'+@column_name+' as
despues , getdate() from inserted a inner join deleted b on(
a.hme_numeroint
= b.hme_numeroint and a.'+@column_name+' not like
b.'+@column_name+' )inner
join dbo.fn_who(null) on (spid=@@spid) '

/* set @lccampo1 = case when dbo.type('expohbl',@column_name) = 'M' then
'convert(char(70),a.'+@column_name+')' else
'a.'+@column_name end
set @lccampo2 = case when dbo.type('expohbl',@column_name) = 'M' then
'convert(char(70),b.'+@column_name+')' else
'b.'+@column_name end
set @lcSQL=' insert into audoper select hostname,
loginame,a.hme_numeroint, '''+@column_name+''', '
+ @lccampo1 + ' as antes , '
+ @lccampo2 + ' as despues ,
getdate() from inserted a inner join deleted b on( a.hme_numeroint > b.hme_numeroint and a.'+@column_name+' not like b.'+@column_name+' )inner
join dbo.fn_who(null) on (spid=@@spid) '
*/
set @lcSQL= ' insert into audoper select hostname,
loginame,a.hme_numeroint, '''+@column_name+''', '+
'a.'+@column_name + ' as antes , ' +
'b.'+@column_name + ' as despues ,
getdate() from inserted a inner join deleted b on( a.hme_numeroint > b.hme_numeroint and a.'+@column_name+' not like b.'+@column_name+' )inner
join dbo.fn_who(null) on (spid=@@spid) '

exec(@lcSQL)
FETCH NEXT FROM antes_cursor
INTO @column_name
END

CLOSE antes_cursor
DEALLOCATE antes_cursor

GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO



PD: el porque uso @@spid en este ejemplo se debe a que en algunos clientes
usa autenficacion NT y otros con usuario "SA".

La funcion fn_who()

SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS OFF
GO

create function fn_who ( @loginame as sysname=null)
returns @t table (spid int,ecid int,status varchar(25),
loginame sysname null,hostname sysname null,blk int,
dbname sysname null,cmd sysname null)
as
begin
declare @spidlow int,
@spidhigh int,
@spid int,
@sid varbinary(85)
select @spidlow = 0 ,@spidhigh = 32767

if ( @loginame is not NULL AND upper(@loginame) = 'ACTIVE')
begin
insert into @t
select spid , ecid, status
,loginame=rtrim(loginame)
,hostname ,blk=convert(char(5),blocked)
,dbname = case when dbid = 0 then null
when dbid <> 0 then db_name(dbid)
end ,cmd
from master.dbo.sysprocesses
where spid >= @spidlow and spid <= @spidhigh AND
upper(cmd) <> 'AWAITING COMMAND'
return
end
if (@loginame is not NULL AND upper(@loginame) <> 'ACTIVE' )
begin
if (@loginame like '[0-9]%') -- is a spid.
begin
select @spid = convert(int, @loginame)
insert into @t
select spid, ecid, status,
loginame=rtrim(loginame),
hostname,blk = convert(char(5),blocked),
dbname = case
when dbid = 0 then null
when dbid <> 0 then
db_name(dbid)
end ,cmd
from master.dbo.sysprocesses
where spid = @spid
end
else
begin
select @sid = suser_sid(@loginame)
if (@sid is null)
begin
return
end
insert into @t
select spid, ecid, status,
loginame=rtrim(loginame),
hostname ,blk=convert(char(5),blocked),
dbname = case
when dbid = 0 then null
when dbid <> 0 then db_name(dbid)
end
,cmd
from master.dbo.sysprocesses
where sid = @sid
end
return
end

/* loginame arg is null */
insert into @t
select spid,
ecid,
status,
loginame=rtrim(loginame),
hostname,
blk=convert(char(5),blocked),
dbname = case
when dbid = 0 then null
when dbid <> 0 then db_name(dbid)
end
,cmd
from master.dbo.sysprocesses
where spid >= @spidlow and spid <= @spidhigh

return -- sp_who
end

GO

SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO





Saludos y Muchas Gracias por sus tiempos.

Lic. Flavio A. Dellarupe<br>
Visual Fox Developer
Bs.As.-Argentina

Preguntas similares