You are on page 1of 16

SQL Server Triggers Aprenda a utilizar triggers em views e auditar as colunas atualizadas em uma tabela

Certamente voc j ouviu falar muito sobre triggers. Mas o qu so triggers? Quando e como utiliz-las? A verdade que triggers nada mais so que um tipo especial de stored procedurecom a capacidade de poder ser vinculada a uma tabela ou view e que so disparadas (da o nometriggers) em resposta a eventos de INSERT, UPDATE ou DELETE que ocorrem sobre uma tabela ou view a qual esto vinculadas. Em verses mais antigas do SQL Server as triggers eram muito utilizadas para garantir a integridade referencial dos dados. No entanto, com as verses mais recentes como SQL Server 2000 e 2005, muitas destas tarefas passaram a ser melhor executadas pelas constraints eforeign keys. Diante disso, as triggers so hoje muito utilizadas para criar regras de auditoria, como inclusive j foi destacado em outros artigos. O fato que as triggers so verdadeiras ferramentas que quando bem utilizadas podem ajudar a implementar complexas regras de negcio, permitir a modificao de dados em mltiplas tabelas ou at executar rollback de transaes inteiras. Neste artigo, ser feita uma introduo aos tipos de triggers disponveis no SQL Server e ser demonstrado como utilizar um tipo especial detrigger chamada INSTEAD OF para criar triggers em views, permitindo controlar as atualizaes em tabelas atravs das views. Alem disso, ser apresentado tambm como podemos identificar ou auditar quais colunas de uma tabela esto sendo atualizadas em uma instruo de UPDATE, utilizando uma funo especial chamada COLUMNS_UPDATE. Tipos de Triggers O ponto chave para uma correta utilizao das triggers conhecer seus tipos e principalmente entender o funcionamento e a diferena entre cada uma delas. O SQL Server oferece basicamente dois tipos de triggers, estas so chamadas AFTER e INSTEAD OF triggers. At a chegada do SQL Server 2000 as AFTER triggers eram o nico tipo de trigger disponvel no SQL Server. Estas so executadas aps a ocorrncia de um evento de INSERT, UPDATE ou DELETE podendo ser usadas para fazer rollback de uma modificao de dados e ainda ser chamada de forma recursiva at 32 nveis, ou seja, trigger1 que chama a trigger2 que chama a trigger3 e assim sucessivamente at atingir a trigger32. No entanto, uma das limitaes das AFTER triggers est no fato de que elas no podem ser usadas em views, vinculadas a mais de uma tabela e tambm no podem ser utilizadas sobre colunas do tipo text, ntext ou image. Para que voc possa entender mais claramente o funcionamento de uma AFTER trigger, vamos a um exemplo prtico. O exemplo da Listagem 1 cria uma AFTER trigger sobre a tabela Authors do banco de dados Pubs.

Listagem 1. Exemplo de AFTER trigger. 1. CREATE TRIGGER tr_after_update_authors ON Authors AFTER UPDATE 2. AS 3. IF UPDATE(city) 4. 5. 6. 7. 8. 9. GO END BEGIN RAISERROR ('A cidade no pode ser alterada!', 16, 1) SELECT au_id,city FROM authors WHERE au_id= '238-95-7766' ROLLBACK

Com esta trigger, quando um evento de UPDATE for executado sobre a tabela Authors a triggerser disparada e verificar se a coluna city foi atualizada (linha 3). Em caso afirmativo, a triggerir disparar uma mensagem dizendo que a alterao no pode ser realizada (linha 5), far um SELECT sobre a tabela Author e ir desfazer o comando de UPDATE usando o ROLLBACK da linha 7. O importante ao se trabalhar com AFTER triggers saber que elas somente so disparadas aps o evento de UPDATE, DELETE ou INSERT ter ocorrido, ou seja, aps o valor ter sido alterado na tabela afetada. Isso pode ser facilmente verificado se voc utilizar o script da Listagem 2 para disparar a trigger criada pelo script da Listagem 1. O script executa um UPDATE sobre a coluna city alterando a cidade para So Paulo onde o au_id for igual a 238-95-7766, isso ir causar o disparo da trigger, e logo em seguida faz um SELECT sobre a tabela authors.

Listagem 2. Script para disparar a AFTER trigger -- Causar o disparo da trigger UPDATE authors set city='So Paulo' WHERE au_id= '238-95-7766' GO

-- Mostra valor original da coluna SELECT au_id,city FROM authors WHERE au_id= '238-95-7766'

Ao executar o script teremos o seguinte resultado:

Msg 50000, Level 16, State 1, Procedure tr_after_update_authrs, Line 5 A cidade no pode ser alterada! au_id city

----------- -------------------238-95-7766 So Paulo

au_id

city

----------- -------------------238-95-7766 Berkeley

O ponto de ateno no resultado so os valores da coluna city. Observe que no SELECT executado dentro da trigger, o valor da coluna city est como So Paulo, isso mostra claramente que a trigger foi disparada aps o valor ter sido alterado na tabela. J no SELECT executado aps o UPDATE o valor da coluna est como Berkeley, este o valor original da coluna. Vale lembrar que o valor s voltou para seu original porque dentro da trigger daListagem 1 est sendo executado um ROLLBACK na linha 7. Disponveis a partir do SQL Server 2000, as INSTEAD OF triggers superam as limitaes das AFTER triggers e tambm podem ser usadas nos eventos de INSERT, UPDATE e DELETE. Entretanto, diferente das AFTER triggers, elas so disparadas antes da ocorrncia do evento. Por exemplo, imagine que voc possui uma INSTEAD OF UPDATE trigger sobre a tabela Authors e um UPDATE executado na coluna city. Diferente do que ocorre nas AFTER triggers, o UPDATE no chega a alterar o valor original da coluna. Antes disso a trigger disparada e a voc pode fazer qualquer trabalho ou aplicar regras de negcio antes que o dado seja efetivamente alterado na tabela. Na Listagem 3 temos um exemplo simples de uma INSTEAD OF trigger. Propositalmente deixei o exemplo bem semelhante ao exemplo da AFTER trigger apresentado naListagem 1, com a diferena que desta

vez no se tem o comando de ROLLBACK. Note tambm que na declarao da trigger, ao invs de usar a sintaxe AFTER UPDATE estou utilizando INSTEAD OF UPDATE.

Listagem 3. Exemplo de INSTEAD OF trigger. 1. CREATE TRIGGER tr_insteadof_update_authors ON Authors INSTEAD OF UPDATE 2. AS 3. IF UPDATE(city) 5. 6. 1) 7. 8. BEGIN RAISERROR ('Instead of trigger, a coluna city ainda no foi alterada!', 16,

SELECT au_id,city FROM authors WHERE au_id= '238-95-7766' END

9. GO

Neste exemplo, quando um UPDATE for executado sobre a tabela Authors a trigger ser disparada antes que o evento atualize o dado na tabela. Se voc re-executar o script da Listagem 2notar que diferente do que ocorreu com a AFTER trigger, os resultado dos dois SELECTs sero iguais. Isso mostra claramente que quando a trigger foi disparada o valor da coluna city ainda no havia sido efetivamente alterado na tabela. Observe no resultado apresentado abaixo que o SELECT executado dentro da trigger no apresenta o valor da coluna city como sendo So Paulo. Lembre-se tambm que desta vez no temos o comando de ROLLBACK dentro da trigger.

Msg 50000, Level 16, State 1, Procedure tr_inseadof_update_authors, Line 5 Instead of trigger, a coluna city ainda no foi alterada! au_id city

----------- -------------------238-95-7766 Berkeley

au_id

city

----------- -------------------238-95-7766 Berkeley

O uso das INTEAD OF triggers interessante, pois permite que o desenvolvedor tenha um maior controle sobre a ao da trigger, permitindo inclusive realizar operaes totalmente diferentes do que o usurio est tentando fazer. No entanto, a principal vantagem das INSTEAD OF triggersest no fato destas poderem ser usadas em views. Adicionar uma INSTEAD OF trigger a umaview permite que voc torne a view atualizvel, tambm conhecido como updatable views. Tornar uma view atualizvel permite que voc possa executar comando de INSERT, UPDATE e DELETE sobre a view sem precisar conhecer a estrutura da tabela ou tabelas afetadas. Por outro lado, trabalhar com views atualizveis permite que voc oculte a estrutura real de suas tabelas podendo disponibilizar para os usurios um conjunto de views que faz o trabalho de atualizao dos dados. No decorrer deste artigo estaremos mostrando como voc pode usar uma INSTEAD OF triggerpara tornar uma view atualizvel e controlar os UPDATEs sobre as tabelas. No incio deste tpico foi dito que o SQL Server possui dois tipos bsicos de triggers: as AFTER e as INSTEAD OF triggers. No entanto, a partir do SQL Server 2005 um novo tipo de trigger foi adicionado ao produto: estas so chamadas de DDL triggers. Diferente das AFTER e INSTEAD OFtriggers que so disparadas em resposta a eventos de manipulao de dados (INSERT, UPDATE e DELETE), as triggers de DDL so disparadas em resposta aos eventos de definio de dados (CREATE, ALTER e DROP), permitindo capturar qualquer evento que possa criar, alterar ou excluir objetos tanto no nvel banco de dados quanto no nvel servidor. Um exemplo simples de criao de uma trigger de DDL pode ser visto na Listagem 4. O exemplo cria uma trigger de DDL de nome ddl_trigger_drop_alter_table que monitora as ocorrncias dos eventos DROP TABLE e ALTER TABLE no banco de dados AdventureWorks no SQL Server 2005 e impede a excluso ou alterao de qualquer tabela dentro do banco de dados.

Listagem 4. Trigger de DDL que impede a excluso e alterao de tabelas. 1. USE AdventureWorks

2. GO 3. --Cria uma tabela de teste 4. IF OBJECT_ID('dbo.TBTESTEDDL') IS NOT NULL 5. DROP TABLE dbo.TBTESTEDDL 6. GO 7. CREATE TABLE dbo.TBTESTEDDL 8. ( 9. ID INT IDENTITY, 10. NOME VARCHAR(30) 11. ) 12. GO

13. --Cria uma trigger de DDL no banco de dados 14. CREATE TRIGGER ddl_trigger_drop_alter_table 15. ON DATABASE 16. FOR DROP_TABLE, ALTER_TABLE 17. AS 18. PRINT 'Voc no tem permisso para excluir ou alterar tabelas.' 19. ROLLBACK 20. GO

21. --Tenta excluir a tabela 22. DROP TABLE dbo.TBTESTEDDL 23. GO

24. -- Tenta alterar tabela 25. ALTER TABLE dbo.TBTESTEDDL 26. ALTER COLUMN NOME VARCHAR (40)

27.

--Exclui os objetos criados

28. DROP TRIGGER DDL_DROP_ALTER_TABLE ON DATABASE 29. DROP TABLE dbo.TBTESTEDDL

Para simular o funcionamento da trigger, das linhas 1 a 12 o exemplo primeiramente cria uma tabela de teste no banco de dados AdvantureWorks e das linhas 13 a 20 parte para a criao da trigger. Como podemos observar, o exemplo cria uma trigger de escopo banco de dados (linha 15), a qual ser disparada aps a execuo dos eventos de banco de dados DROP TABLE e ALTER TABLE (linha 16). Ao ser disparada, a trigger envia para o usurio a mensagem de que ele no possui permisso para excluir ou alterar tabelas e impede que a tabela seja excluda ou alterada fazendo um ROLLBACK da transao que disparou os eventos (linha 19). Das linhas 21 a 29 temos os eventos que ao serem executados iro disparar a trigger e depois remover os objetos criados no exemplo. Criando views atualizveis com as INSTEAD OF triggers Como eu disse no tpico anterior, a principal vantagem na utilizao das INSTEAD OF triggersest no fato que voc pode criar uma INSTEAD OF trigger sobre uma view, tornando a viewatualizvel. Isso permite que voc execute comandos de INSERT, UPDATE e DELETE sobre a viewsem conhecer a estrutura das tabelas afetadas e dentro da trigger fazer todos os tratamentos necessrios, como, tratar regras de negcio e decidir qual tabela ou tabelas devem ser atualizadas. Isso possibilita a atualizao dos dados sem precisar acessar as tabelas diretamente, o que inclusive adiciona maior segurana quanto ao acesso indevido estrutura das tabelas. Para exemplificar a utilizao de INSTEAD OF trigger em views, vamos imaginar um pequeno cenrio que inclui uma tabela de Clientes e uma tabela de Pedidos. O script da Listagem 5 permite a criao das tabelas e as popula com alguns dados de exemplo.

Listagem 5. Script para criar e polular as tabelas de Clientes e Pedidos.

CREATE TABLE Clientes ( Cli_ID INT IDENTITY(1,1) PRIMARY KEY, Nome varchar (20), Cidade varchar(20))

CREATE TABLE Pedidos( PedidoID INT IDENTITY(1,1) PRIMARY KEY, Cli_ID INT REFERENCES Clientes(Cli_ID), ValorPedido decimal (10,2), DataPedido smalldatetime DEFAULT(GETDATE())) -- Carrega tabela de clientes com dados de exemplo INSERT INTO Clientes VALUES('Empresa 1', 'Rio de Janeiro') INSERT INTO Clientes VALUES('Empresa 2', 'So Paulo') INSERT INTO Clientes VALUES('Empresa 3', 'Curitiba') -- Carrega tabela de pedidos com dados de exemplos INSERT INTO Pedidos (Cli_ID,ValorPedido) VALUES (1,100.00) INSERT INTO Pedidos (Cli_ID,ValorPedido) VALUES (2,800.00) INSERT INTO Pedidos (Cli_ID,ValorPedido) VALUES (3,2000.00)

Aps a criao e carga das tabelas, os dados esto como os apresentados na Figura 1.

Figura 1. Dados de exemplo das tabelas clientes e pedidos.

Agora que as tabelas de exemplo esto criadas e com dados, devemos criar uma view que faa um JOIN entre as duas tabelas e permita visualizar os dados de uma forma mais simples. AListagem 6 apresenta o script de criao da view.

Listagem 6. Script de criao de uma view para UNIR as tabelas Clientes e Pedidos. CREATE VIEW vw_clientes_pedidos AS SELECT cli.Cli_ID,cli.nome, ped.ValorPedido, ped.Datapedido FROM Clientes cli INNER JOIN Pedidos ped ON cli.Cli_ID = ped.Cli_ID GO

Na Figura 2 temos o resultado da execuo da view vw_clientes_pedidos.

Figura 2. Dados e colunas da view vw_clientes_pedidos.

Estando com as tabelas e view criadas, precisamos ento criar uma INSTEAD OF trigger sobre aview. Isso permitir que voc realize modificaes (INSERT, UPDATE e DELETE) nas tabelas sem precisar acessar as tabelas de forma direta. Para demonstrar isso, ser utilizado o script daListagem 7 para criar uma INSTEAD OF trigger de INSERT sobre a view criada pela Listagem 6.

Listagem 7. Script de criao da INSTEAD OF trigger 1. 2. 3. 4. CREATE TRIGGER tr_vw_clientes_pedidos ON vw_clientes_pedidos INSTEAD OF INSERT AS BEGIN 5. 6. 7. 8. 9. IF EXISTS (SELECT TOP 1 * FROM INSERTED WHERE Cli_ID IS NOT NULL) BEGIN INSERT INTO Pedidos(Cli_ID, ValorPedido) SELECT i.Cli_ID, i.ValorPedido FROM INSERTED i END

10.END 11.GO

Note no script que na declarao da trigger (linhas 1 e 2) especificado que a trigger ser de INSERT, ou seja, esta trigger ser disparada toda vez que um INSERT for efetuado sobre a view. Depois, a primeira instruo da trigger faz uma checagem na tabela INSERTED para garantir que o Cli_ID no nulo (linha 5). A

partir do momento que sabemos que existe um Cli_ID, podemos ento assumir que est sendo inserido um novo pedido para um cliente e usamos a trigger para fazer um INSERT sobre a tabela de Pedidos atravs dos dados que esto na tabela INSERTED. Quando trabalhamos com AFTER ou INSTEAD OF triggers, estas nos disponibilizam duas tabelas chamadas INSERTED e DELETED. Estas tabelas so tabelas virtuais que existem apenas durante o tempo de execuo da trigger e podem ser usadas para capturar o antes e depois de uma operao INSERT, UPDATE ou DELETE. As tabelas so afetadas de forma diferente dependendo da operao executada. A Tabela 1 mostra o que contm em cada tabela de acordo com a operao.

Operao DELETE INSERT UPDATE

Tabela INSERTED contm No possui registros Novos registros Novos registros

Tabela DELETED contm Registros excludos No possui registros Registros antigos

Tabela 1. Efeito das operaes de INSERT, UPDATE e DELETE sobre as tabelas INSERTED e DELETED.

Com a trigger criada sobre a view, temos agora a capacidade de fazer INSERT sobre as tabelas (neste exemplo estamos usando apenas a tabela Pedidos) utilizando a view. Um exemplo de instruo de INSERT utilizando a view pode ser visto abaixo:

INSERT INTO vw_clientes_pedidos(Cli_ID, ValorPedido) VALUES(1, 1200)

O exemplo executa um INSERT sobre a view vw_clientes_pedidos fornecendo um Cli_ID vlido e um valor de pedido. Com isso teremos um novo registro sendo inserido sobre a tabela de Pedidos. Executando um SELECT sobre a view aps a execuo do INSERT podemos notar na Figura 3 aincluso de um novo pedido para o Cli_ID igual a 1.

Figura 3: Incluso de pedidos atravs da view.

Vale lembrar que da mesma forma que criamos uma INSTEAD OF trigger de INSERT, voc tambm pode estar criando trigger para UPDATE e DELETE. O exemplo utilizado aqui foi apenas para mostrar o poder e a flexibilidade oferecidos pelas INSTEAD OF trigger. Com elas voc pode ocultar de seus analistas ou desenvolvedores a estrutura real das tabelas e impedir que modificaes sejam feitas diretamente nas tabelas. Usando a funo COLUMNS_UPDATED para identificar colunas que sofreram atualizaes Nas edies 34 (setembro/2006) da SQL Magazine e 41 (Junho/2007) da .NET Magazine foram publicados artigos respectivamente sobre Auditoria de Operaes DDL (CREATE, ALTER e DROP) com as triggers de DDL e Auditoria de Operaes DML (INSERT, UPDATE e DELETE) com a clusula OUTPUT no SQL Server 2005. Sabemos que a auditoria de dados muito importante, principalmente quando trabalhamos com informaes confidenciais. Agora, alm de auditar modificaes de dados e objetos, tambm pode ser interessante auditar as colunas que sofreram atualizaes. No entanto, a grande questo : Como fazer para identificar quais colunas de uma tabela foram atualizadas em uma instruo de UPDATE? a que entra a funo COLUMNS_UPDATED. Disponvel apenas dentro de uma trigger, a funo COLUMNS_UPDATED pode ser utilizada em AFTER ou INSTEAD OF triggers para retornar um valor binrio contendo as colunas que foram includas em uma instruo de UPDATE ou INSERT baseado na ordem das colunas da tabela ouview afetada. Ao utilizar esta funo dentro de uma trigger, fica fcil saber qual coluna foi alterada e a basta gravar isso em uma tabela de auditoria. Para exemplificar o uso desta funo, utilizaremos o script da Listagem 8 para criar as tabelas de Pedidos e PedidosAudit. Note que na tabela PedidosAudit temos a coluna ColunaAtualizadaque ser utilizada para armazenar as colunas utilizadas

nas instrues de UPDATE, ou seja, as colunas que sofreram atualizaes. Estes dados sero armazenados em formato XML.

Listagem 8. Script de criao das tabelas de exemplo Pedidos e PedidosAudit CREATE TABLE Pedidos (PedidoID INT IDENTITY(1,1), Produto VARCHAR(30) NOT NULL, DataPedido SMALLDATETIME, ValorPedido DECIMAL(10,2) )

CREATE TABLE PedidosAudit (AuditID INT IDENTITY(1,1), PedidosID INT, Produto VARCHAR(30) NOT NULL, Valorpedido DECIMAL (10,2), ColunaAtualizada XML NULL ) -- Carrega a tabela de Pedidos com dados exemplo INSERT INTO Pedidos (Produto, DataPedido, ValorPedido) VALUES ('Produto 1', '2007-07-10', 560.00) INSERT INTO Pedidos (Produto, DataPedido, ValorPedido) VALUES ('Produto 2', '2007-07-15', 2300.00)

Estando com as tabelas criadas, precisamos agora de uma AFTER trigger para capturar os eventos de UPDATE disparados sobre a tabela Pedidos. Esta trigger deve ento utilizar a funo COLUMNS_UPDATED para que possamos

identificar quais colunas foram atualizadas pelo UPDATE. Na Listagem 9 temos o cdigo de exemplo para criao da trigger.

Listagem 9. Script de criao da AFTER trigger de UPDATE sobre a tabela Pedidos 1. 2. 3. CREATE TRIGGER tr_pedidos ON Pedidos AFTER UPDATE AS BEGIN 4. 5. 6. 7. 8. SET @CamposAtualizados = ( DECLARE @CamposAtualizados XML DECLARE @ColunasAtualizadas VARBINARY(100) SET @ColunasAtualizadas = COLUMNS_UPDATED()

9. SELECT COLUMN_NAME AS Name FROM INFORMATION_SCHEMA.COLUMNS Field 10. WHERE TABLE_NAME = 'Pedidos'

11. ANDsys.fn_IsBitSetInBitmask(@ColunasAtualizadas,COLUMNPROPERTY(OBJE CT_ID(TABLE_SCHEMA + '.' 12. + TABLE_NAME), COLUMN_NAME, 'ColumnID')) <> 0 13. 14. 15. INSERT INTO PedidosAudit (PedidoID, Produto, ValorPedido, ColunaAtualizada) 16. SELECT PedidoID,Produto,ValorPedido, @CamposAtualizados FROM INSERTED 17.END FOR XML AUTO, ROOT('Fields'))

Nesta trigger temos dois pontos chaves. O primeiro a declarao da varivel @ColunasAtualizadas (linha 5). Esta a varivel de tipo binrio que armazena o valor binrio retornado pela funo COLUMNS_UPDATED (linha 6) e que na linha 12

passada como parmetro para a funo de sistema sys.fn_IsBitSetInBitmask (disponvel apenas no SQL Server 2005), o segundo ponto chave da trigger. Na verdade, todo o trabalho pesado da trigger feito por esta funo de sistema. Observe que ela recebe dois parmetros: o valor binrio armazenado na varivel @ColunasAtualizadas (que armazena as colunas sendo atualizadas) e o ColumnID das colunas da tabela Pedidos, este obtido atravs da view de sistema INFORMATION_SCHEMA.COLUMNS. Recebendo estes parmetros, a funo retorna um valor diferente de 0 para cada coluna da tabela Pedidos que estiver contida no valor binrio da varivel @ColunasAtualizadas. A trigger ento armazena o nome das colunas que foram includas na instruo de UPDATE em formato XML na varivel @CamposAtualizados, permitindo que na linha 15 possamos inserir estes campos na tabela de auditoria. Estando a trigger criada, podemos executar uma instruo de UPDATE sobre a tabela Pedidos e consultar a tabela de auditoria PedidosAudit para visualizar o resultado. Um exemplo pode ser visto na Figura 4 onde a instruo de UPDATE atualiza a coluna ValorPedido, atualizando o valor dos pedidos em mais 40. Note que na tabela de auditoria PedidosAudit a coluna ColunaAtualizada informa a coluna que sofreu a atualizao.

Figura 4: Tabela de auditoria mostrando a coluna que sofreu atualizao.

Como um exemplo de update de mltiplas colunas, a Figura 5 mostra um UPDATE sendo efetuado sobre as colunas Produto e ValorPedido. Na tabela de auditoria a coluna ColunaAtualizada mostra as colunas que sofreram atualizaes.

Figura 5: Tabela de auditoria mostrando as colunas que sofreram atualizaes.

Os exemplos apresentados aqui infelizmente s funcionam no SQL Server 2005 devido utilizao do tipo de dados XML, mas tambm principalmente devido funo de sistemasys.fn_IsBitSetInBitmask s estar disponvel no SQL Server 2005. No entanto, voc pode facilmente copiar o script da funo para o SQL Server 2000 e utilizar o tipo de dados varchar() em substituio ao tipo XML. Isso permitir que voc use esta soluo de auditoria tambm no SQL Server 2000. Concluses No decorrer deste artigo foram apresentados os tipos de trigger disponveis no SQL Server e, principalmente, foi mostrado que o ponto chave na utilizao das triggers conhecer seus tipos e entender seu funcionamento. Aps isso, foi mostrado tambm que as INSTEAD OF trigger so excelentes para se utilizar em views, tornando as views atualizveis e podendo realizar operaes de INSERT, UPDATE e DELETE sem precisar acessar as tabelas diretamente. Por fim, vimos que auditar as colunas que sofrem atualizaes em uma tabela bastante simples quando usamos a funo COLUMNS_UPDATE e sys.fn_IsBitSetInBitmask. Bom, agora com vocs, um abrao e at a prxima.

You might also like