AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 118281
Accepted
BateTech
BateTech
Asked: 2015-10-17 07:12:33 +0800 CST2015-10-17 07:12:33 +0800 CST 2015-10-17 07:12:33 +0800 CST

Como capturar o evento sp_refreshsqlmodule

  • 772

Meu problema: a execução sp_refreshsqlmoduleatualizará o sys.objects.modify_datevalor, mas não acionará um evento DDL que possa ser usado por um gatilho DDL. Portanto, se estiver usando um gatilho DDL para mostrar o histórico de alterações do objeto, não consigo reconciliar o motivo pelo qual um procedimento, função etc. teria um recente modify_datequando nenhuma ALTERinstrução foi executada no objeto. Isso pode causar algumas dores de cabeça durante o tempo de auditoria.

Minha pergunta: Existe alguma maneira de capturar sp_refreshsqlmoduleeventos, para que eu possa ter uma maneira automatizada de reconciliar os modify_dateobjetos do banco de dados?

Consegui encontrar este item de conexão para SQL Server 2008 https://connect.microsoft.com/SQLServer/feedback/details/340441/sp-autostats-and-other-system-procedures-does-not-fire-ddl- triggers , que é marcado como fechado com o comentário:

não achamos que seja crítico corrigir isso para o SQL Server 2008. Definitivamente, consideraremos isso para o lançamento depois disso

Também sou capaz de replicar o problema no SQL Server 2014.

Script SQL para replicar o problema:

IF OBJECT_ID('dbo.test_DDL_log ') IS NOT NULL
DROP TABLE dbo.test_DDL_log; 
GO

CREATE TABLE dbo.test_DDL_log (id int not null identity primary key, DDL_EventData xml, dateCreated datetime, contextInfo varchar(128));
go

CREATE TRIGGER [TEST_ddlDatabaseTriggerLog] 
ON DATABASE 
FOR DDL_DATABASE_LEVEL_EVENTS 
AS 
--Log all DDL operations on this database to an audit table.
BEGIN
    SET NOCOUNT ON;

    INSERT INTO dbo.test_DDL_log(DDL_EventData, dateCreated, contextInfo)
    VALUES (
          EVENTDATA()
        , GETDATE()
        , REPLACE(CAST(CONTEXT_INFO() AS VARCHAR(128)), CHAR(0), '')
    );
END
GO 

--Context info used in DDL trigger to tie to build and/or Support Ticket # to this change
DECLARE @c varbinary(128);
SET @c=cast('Ticket 123 v1.2.345' as varbinary(128));
SET CONTEXT_INFO @c;
GO

--Repro Example taken from:  https://msdn.microsoft.com/en-us/library/bb326754.aspx
-- Create an alias type.
IF EXISTS (SELECT 'TEST_mytype' FROM sys.types WHERE name = 'TEST_mytype')
DROP TYPE TEST_mytype;
GO

CREATE TYPE TEST_mytype FROM nvarchar(5);
GO

IF OBJECT_ID ('dbo.TEST_to_upper', 'FN') IS NOT NULL
DROP FUNCTION dbo.TEST_to_upper;
GO

CREATE FUNCTION dbo.TEST_to_upper (@a TEST_mytype)
RETURNS TEST_mytype
WITH ENCRYPTION
AS
BEGIN
RETURN upper(@a)
END;
GO

-- Increase the length of the alias type.
EXEC sp_rename 'TEST_mytype', 'TEST_myoldtype', 'userdatatype';
GO

CREATE TYPE TEST_mytype FROM nvarchar(10);
GO

---- The function parameter still uses the old type.
-- and would Fail here because of truncation:
--SELECT dbo.TEST_to_upper('abcdefgh'); 
GO

select modify_date_BEFORE = o.modify_date
from sys.objects o 
where o.name = 'TEST_to_upper'
;
go

WAITFOR DELAY '00:00:05'; --allow some time to elapse so that modify_date change is more noticable
GO

-- Refresh the function to bind to the renamed type.
EXEC sys.sp_refreshsqlmodule 'dbo.TEST_to_upper';
go

select modify_date_AFTER = o.modify_date
from sys.objects o 
where o.name = 'TEST_to_upper'
;
go

--only 4 events show here, NOT included the call to sp_refreshsqlmodule which updated the modify_date
SELECT * from dbo.test_DDL_log;
go

--CLEANUP 
IF OBJECT_ID ('dbo.TEST_to_upper', 'FN') IS NOT NULL
DROP FUNCTION dbo.TEST_to_upper;
GO
IF EXISTS (SELECT 'TEST_myoldtype' FROM sys.types WHERE name = 'TEST_myoldtype')
DROP TYPE TEST_myoldtype;
GO
IF EXISTS (SELECT 'TEST_mytype' FROM sys.types WHERE name = 'TEST_mytype')
DROP TYPE TEST_mytype;
GO
if exists(select 1 from sys.triggers t where t.name = 'TEST_ddlDatabaseTriggerLog')
DROP TRIGGER [TEST_ddlDatabaseTriggerLog] ON DATABASE
GO

IF OBJECT_ID('dbo.test_DDL_log ') IS NOT NULL
DROP TABLE dbo.test_DDL_log; 
GO

Os resultados deste script mostram:

modify_date_BEFORE
-----------------------
2015-10-16 10:59:10.447

modify_date_AFTER
-----------------------
2015-10-16 10:59:15.487

id          DDL_EventData                                                                                         dateCreated             contextInfo
----------- ------------------------------------------------------------------------------------------------ -------------------------    ------------------------
1           <EVENT_INSTANCE><EventType>CREATE_TYPE</EventType><PostTime>2015-10-16T10:59:10.443</PostTime>....... 2015-10-16 10:59:10.443 Ticket 123 v1.2.345
2           <EVENT_INSTANCE><EventType>CREATE_FUNCTION</EventType><PostTime>2015-10-16T10:59:10.447</PostTime>... 2015-10-16 10:59:10.447 Ticket 123 v1.2.345
3           <EVENT_INSTANCE><EventType>RENAME</EventType><PostTime>2015-10-16T10:59:10.450</PostTime>............ 2015-10-16 10:59:10.450 Ticket 123 v1.2.345
4           <EVENT_INSTANCE><EventType>CREATE_TYPE</EventType><PostTime>2015-10-16T10:59:10.453</PostTime>....... 2015-10-16 10:59:10.453 Ticket 123 v1.2.345

O evento para o sp_refreshsqlmodulenão foi encontrado em nenhum lugar e todos os eventos registrados são anteriores ao sp_refreshsqlmodulehorário de 16/10/2015 10:59:15.487 (nota: coloquei um atraso de 5 segundos antes da sp_refreshsqlmodulechamada no script acima para torná-lo mais perceptível que este evento não estava sendo registrado).

sql-server trigger
  • 2 2 respostas
  • 697 Views

2 respostas

  • Voted
  1. Best Answer
    Aaron Bertrand
    2015-10-17T08:56:09+08:002015-10-17T08:56:09+08:00

    Infelizmente, existem vários eventos do tipo DDL que na verdade não contam como eventos DDL para uso com gatilhos DDL, notificações de eventos, etc.

    Você pode capturar sp_refreshsqlmodulechamadas usando a seguinte sessão de eventos estendidos:

    CREATE EVENT SESSION [refreshes] ON SERVER 
    ADD EVENT sqlserver.module_end
    (
      SET collect_statement = (1)
      ACTION
      (
         sqlserver.client_app_name, 
         sqlserver.client_hostname,
         sqlserver.database_id,
         sqlserver.database_name,
         sqlserver.username,
         sqlserver.context_info
      )
      WHERE ([object_id] = -419385653) -- OBJECT_ID(N'sys.sp_refreshsqlmodule')
    )
    ADD TARGET package0.asynchronous_file_target
    (
      SET FILENAME = N'C:\temp\refreshes.xel'
    );
    GO
    
    ALTER EVENT SESSION [refreshes] ON SERVER STATE = START;
    GO
    

    Você pode precisar coletar um conjunto diferente de colunas de auditoria; estes foram principalmente emprestados de uma sessão semelhante que eu tenho. Você também pode adicionar um filtro adicional para sp_refreshviewe possivelmente outros eventos, como recompilações e atualizações de estatísticas (não conheço todas as chamadas de procedimento que podem mudar modify_date, mas não são capturadas como eventos DDL adequados).

    Agora, a sessão apenas capturará os dados. Você pode inspecioná-lo manualmente assim:

    ;WITH ee_data AS 
    (
      SELECT x = CONVERT(XML, event_data)
        FROM sys.fn_xe_file_target_read_file
        (N'C:\temp\refreshes*.xel', NULL, NULL, NULL)
    )
    SELECT 
      [statement] = x.value('(event/data[@name="statement"]/value)[1]','nvarchar(4000)'),
      [timestamp] = x.value('(event/@timestamp)[1]','datetime2'),
      --username = x.value('(event/action[@name="username"]/value)[1]','nvarchar(400)'),
      --[host] = x.value('(event/action[@name="client_hostname"]/value)[1]','nvarchar(400)'),
      --app = x.value('(event/action[@name="client_app_name"]/value)[1]','nvarchar(400)')
      -- you'll have to add the xquery stuff to get context_info
    FROM ee_data;
    

    Saída de exemplo:

    statement                                      timestamp
    --------------------------------------------   ---------------------------
    EXEC sys.sp_refreshsqlmodule N'dbo.someview'   2015-10-16 16:45:35.6220000
    

    Você terá que analisar o nome do objeto e colocar as informações em seu próprio formato XML para corresponder EVENTDATA(). Observe também que timestampestá em UTC, não em sua hora local, então você precisará ajustar isso para comparações válidas modify_date(que herdam a hora do servidor). No meu caso, a diferença entre modify_datee timestampfoi de 6 milissegundos, portanto, a sessão não registra o momento exato em que o objeto foi modificado - você precisará permitir uma pequena margem de manobra para "combinar" esses dois valores.

    Em seguida, você precisará colocar qualquer código com o qual terminar em algum tipo de trabalho que pesquisa o arquivo de destino para novas linhas (você pode considerar as notificações de consulta para evitar a pesquisa, mas a pesquisa é muito mais simples) e as insere em seu Tabela de auditoria DDL.

    • 2
  2. BateTech
    2015-10-19T05:30:35+08:002015-10-19T05:30:35+08:00

    A resposta de @AaronBertrand acima funciona bem para SQL 2014 (e provavelmente SQL 2012, embora eu não tenha uma instância de 2012 para testar).

    No entanto, se estiver usando o SQL Server 2008 R2, você deve remover as seguintes linhas ao criar a sessão do evento, pois elas causarão os seguintes erros:

    SET collect_statement = (1) --error: Msg 25629, Level 16, State 1, For event, "sqlserver.module_end", the customizable attribute, "collect_statement", does not exist.
    sqlserver.database_name  --within the ACTION block. Error: Msg 25623, Level 16, State 3, The event action name, "sqlserver.database_name", is invalid, or the object could not be found
    sqlserver.context_info   --within the ACTION block. Error: Msg 25623, Level 16, State 3, The event action name, "sqlserver.context_info", is invalid, or the object could not be found
    

    Como SET collect_statementnão funciona no SQL 2008 R2, você deve adicionar sqlserver.sql_textà Actionlista e o código para analisar o nome do objeto que foi passado sp_refreshsqlmoduleprecisa ser capaz de lidar com várias instruções executadas em um lote.

    Também no SQL2008R2, ao filtrar os valores OBJECT_ID na wherecláusula do CREATE EVENT SESSION, você deve usar #'s positivos object_id para esses objetos do sistema, mesmo que os object_id#'s sejam realmente negativos. Acho que isso ocorre porque no SQL 2008 R2 os eventos estendidos são usados uint32​​internamente para o object_id.

    Aqui está o script para SQL 2008 R2:

    CREATE EVENT SESSION [audit_sp_refreshes] ON SERVER 
    --SQL SERVER 2008 R2 version
    ADD EVENT sqlserver.module_end
    (
      ACTION
      (
         sqlserver.client_app_name, 
         sqlserver.client_hostname,
         sqlserver.database_id,
         sqlserver.sql_text,
         sqlserver.username
      )
      WHERE 
        (
            --Must use POSITIVE object_id #'s here in SQL 2008 R2, even though the object_ids are actually negative
                 [object_id] = 419385653  -- OBJECT_ID(N'sys.sp_refreshsqlmodule')
        ) 
    )
    ADD TARGET package0.asynchronous_file_target
    (
      SET FILENAME = N'C:\TEMP\ExtendedEventLogs\audit_sp_refreshes.xel'
    );
    GO
    
    ALTER EVENT SESSION [audit_sp_refreshes] ON SERVER STATE = START;
    GO
    
    • 2

relate perguntas

  • SQL Server - Como as páginas de dados são armazenadas ao usar um índice clusterizado

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve