Ao trabalharmos com bancos de dados, temos a ideia de que eles trabalham praticamente da mesma forma, o que não é verdade, pois cada um possui as suas peculiaridades. Neste artigo apresentaremos um comparativo entre os principais bancos de dados em relação a alguns itens que são importantes no momento em que trabalhamos com aplicações diversas, como é o caso de utilização de triggers, comentários e condicionais comparação ocorrerá entre os bancos de dados SQL Server, Oracle e Firebird.

Utilização de triggers

No que diz respeito a triggers, o Oracle e o SQL Server possuem conceitos diferentes para a sua criação.

O SQL Server possui uma abordagem baseada em conjuntos, tendo que as linhas que são afetadas por uma modificação de dados, sejam insert, update ou delete, são armazenadas nas tabelas temporárias inserted e deleted. Em caso de triggers regulares do tipo DML (Data Model Language), DDL (Data Definition Language) e de Logon, estas serão sempre executadas após a passagem de uma instrução. Neste momento, tem-se que uma “imagem” do registro é criada antes da informação definitiva ser armazenada na tabela e, em seguida, a “imagem” é eliminada após o registro ser inserido definitivamente na tabela. Ambos podem ser acessados ​​de dentro da trigger. No entanto, quando é necessário, pode-se juntar as tabelas temporárias inserted e deleted para realizar a atualização da tabela quando a trigger for executada.

No Oracle são encontrados os termos AFTER e BEFORE no corpo das triggers, que podem ser executadas tanto por linha quanto por declaração. Com isso, as triggers utilizando BEFORE são disparadas antes da ocorrência de uma ação. Já as triggers apresentadas com o AFTER ocorrem após a ação ter sido executada.

Nos três bancos temos momentos como no Oracle. Porém, no SQL Server temos os momentos AFTER e INSTEAD OF, mas não o momento BEFORE, enquanto que o Firebird suporta múltiplos disparos da trigger a nível de linha por tabela, o que não ocorre com o Oracle.

No que diz respeito ao Oracle, quando trabalhamos com triggers BEFORE, estas não podem ver uma imagem que seja declarada como AFTER, onde de igual forma uma trigger declarada como AFTER não poderá ver uma imagem que seja declarada com BEFORE. Apenas triggers de linha podem visualizar as imagens BEFORE e AFTER, ao mesmo tempo. Nestes casos em que utilizamos as triggers de linha, podemos fazer referência aos valores contidos nas colunas, sabendo qual a informação que existia antes de uma atualização e após essa atualização ter sido realizada. Para isso, a Oracle oferece os pseudorecords OLD e NEW, os quais são utilizados para acessar as informações antigas e novas, respectivamente. Com base nisso, podemos dizer que o OLD e o NEW são, portanto, semelhantes as tabelas temporárias deleted e inserted, presentes no SQL Server, com a diferença que estes estão em nível de linha. Outra propriedade presente nas triggers da Oracle, que também é uma fonte regular de erros, é quando a tabela na qual a trigger está atuando é chamada por uma outra operação qualquer como, por exemplo, uma consulta.

Uma outra diferença entre o Oracle e o Firebird é que o Firebird não levanta exceções referentes a mutação de tabelas, como a Oracle faz, e as triggers, por padrão, podem ser aninhadas e recursivas, diferentemente do SQL Server, que não aceita recursividade por padrão. No SQL Server as operações de trigger recursiva são possíveis, mas pelo menos para triggers normais este não é o comportamento padrão, além de que triggers disparando de forma recursiva precisam ser definidas explicitamente como uma opção de servidor.

As triggers do Firebird usam as variáveis de contexto NEW e OLD e provêm flag de inserção, atualização e exclusão para indicar o status atual da trigger.

Já o PostgreSQL é muito parecido com o Firebird, pois para obtermos as informações basta atribuirmos o prefixo OLD para realizarmos as consultas, e o NEW quando for um novo valor. Eles, por conseguinte, são diferentes do Oracle devido a utilização do (:) antes dos termos OLD e NEW. Com relação a compilação das triggers, temos que o banco de dados exclui normalmente as versões anteriores, caso já tenham sido compiladas anteriormente. Dessa forma, tanto para o Postgres quanto para o Oracle, basta utilizarmos a instrução CREATE ou REPLACE, enquanto que no SQL Server precisamos verificar no dicionário de dados se o objeto com mesmo nome já existe.

Antes de começarmos com os exemplos, torna-se necessário a visualização da sintaxe apresentada por cada uma das bases de dados para a criação de uma trigger, o que podemos ver de acordo com as Listagens 1, 2, 3 e 4, respectivamente Oracle, SQL, Firebird e PostgreSQL. Vale salientar que as triggers que serão vistas serão referentes a operações DML.


    CREATE [OR REPLACE ] TRIGGER trigger_name 
      {BEFORE | AFTER | INSTEAD OF } 
      {INSERT [OR] | UPDATE [OR] | DELETE} 
      [OF col_name] 
      ON table_name 
      [REFERENCING OLD AS o NEW AS n] 
      [FOR EACH ROW] 
      WHEN (condition)  
      DECLARE
         Declaration-statements
      BEGIN 
         Executable-statements
      EXCEPTION
         Exception-handling-statements
      END;
Listagem 1. Sintaxe básica de uma trigger no Oracle

    -- Sintaxe do SQL Server 
      Trigger on an INSERT, UPDATE, or DELETE statement to a table or view (DML Trigger)
      CREATE TRIGGER [ schema_name . ]trigger_name 
      ON { table | view } 
      [ WITH <dml_trigger_option> [ ,...n ] ]
      { FOR | AFTER | INSTEAD OF } 
      { [ INSERT ] [ , ] [ UPDATE ] [ , ] [ DELETE ] } 
      [ WITH APPEND ]
      [ NOT FOR REPLICATION ] 
      AS { sql_statement  [ ; ] [ ,...n ] | EXTERNAL NAME <method specifier [ ; ] > }
       
      <dml_trigger_option> ::=
          [ ENCRYPTION ]
          [ EXECUTE AS Clause ]
      <method_specifier> ::= 
          assembly_name.class_name.method_name
Listagem 2. Sintaxe básica da trigger no SQL Server

      {CREATE | RECREATE | CREATE OR ALTER} TRIGGER name FOR {TABLE name | VIEW name}
       [ACTIVE | INACTIVE]
       {BEFORE | AFTER}
       {INSERT [OR UPDATE] [OR DELETE] | UPDATE [OR INSERT] [OR DELETE] | DELETE [OR UPDATE] [OR INSERT] }
       [POSITION n] AS
      BEGIN
       .....
      END
Listagem 3. Sintaxe básica de uma trigger no Firebird

    CREATE [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] }
        ON table_name
        [ FROM referenced_table_name ]
        [ NOT DEFERRABLE | [ DEFERRABLE ] { INITIALLY IMMEDIATE | INITIALLY DEFERRED } ]
        [ FOR [ EACH ] { ROW | STATEMENT } ]
        [ WHEN ( condition ) ]
        EXECUTE PROCEDURE function_name ( arguments )
Listagem 4. Criação de uma trigger no Postgres

Vejamos um exemplo simples de como podemos criar triggers para cada uma das bases de dados apresentadas neste artigo. Começaremos vendo a criação de uma trigger no Oracle, onde apresentaremos uma simples mensagem contendo os valores de salários novos, antigos e a diferença entre eles, como apresentado pela Listagem 5.


    CREATE OR REPLACE TRIGGER Muda_SalarioFuncionario
        BEFORE DELETE OR INSERT OR UPDATE ON Funcionarios
        FOR EACH ROW
      WHEN (new.IdFuncionario > 0)
      DECLARE
          diferenca number;
      BEGIN
          diferenca := :new.salarioFuncionario - :old.salarioFuncionario;
          dbms_output.put('o salário antigo do funcionário foi de ' || :old.salarioFuncionario);
          dbms_output.put(' o salário atual do funcionário é de ' || :new.salarioFuncionario);
          dbms_output.put_line('A diferença entre os salários do funcionário é de ' || diferenca);
      END; 
      /
Listagem 5. Exemplo simples de criação de uma trigger no Oracle

A trigger é disparada quando uma das operações DML (INSERT, UPDATE e DELETE) é realizada sobre a tabela. Devido ao fato de estarmos utilizando a palavra-chave BEFORE, a trigger pode acessar os novos valores antes que estes sejam inseridos na tabela, além da possibilidade de poder alterar o valor utilizando a atribuição :NEW.Column_name.

Já com relação ao AFTER, este também pode ser utilizado, mas apenas para consultar ou alterar as informações presentes na tabela, após as mudanças iniciais terem sido realizadas e a tabela estiver com os dados já gravados e consistentes. Estamos utilizando neste caso a cláusula FOR EACH ROW, para que possamos executar a instrução várias vezes, o que ocorre em casos de atualização ou exclusão de várias linhas ao mesmo tempo.

Após criarmos a trigger, realizaremos agora uma inserção na tabela de Funcionários e em seguida, uma atualização no item salarioFuncionario para que possamos ver as informações serem apresentadas. Para que possamos inserir os dados de teste na tabela, temos que a query de inserção e de update que serão utilizadas estão sendo apresentadas pela Listagem 6.


      -- Instrução de Insert para gravar os registros dos funcionários.
      INSERT INTO Funcionarios (IdFuncionario, NomeFunc, EnderecoFunc, salarioFuncionario) 
      VALUES (1, ‘Edson Dionisio’, ‘Rua dos testes’, 3000);
      INSERT INTO Funcionarios (IdFuncionario, NomeFunc, EnderecoFunc, salarioFuncionario) 
      VALUES (5, ‘José Edson dos Santos’, ‘Starling city’, 2000);
      INSERT INTO Funcionarios (IdFuncionario, NomeFunc, EnderecoFunc, salarioFuncionario) 
      VALUES (9, ‘Tatsu Yamashiro’, ‘Rua dos Navegantes’, 4500);
      INSERT INTO Funcionarios (IdFuncionario, NomeFunc, EnderecoFunc, salarioFuncionario) 
      VALUES (15, ‘Carlos Edson Andrade’, ‘Rua dos testes’, 1700);
      INSERT INTO Funcionarios (IdFuncionario, NomeFunc, EnderecoFunc, salarioFuncionario) 
      VALUES (25, ‘Mariana Ximenes’, ‘Rio de Janeiro’, 12000);
      -- Instrução de update para atualizar os registros dos funcionários.
      UPDATE Funcionarios SET salarioFuncionario = salarioFuncionario + 1500 
      WHERE NomeFunc LIKE ‘%Edson%’; 
Listagem 6. Inserção e atualização de dados na base de dados

Ao utilizarmos a instrução UPDATE, ela irá disparar a trigger para cada linha que deverá ser atualizada, apresentando dessa forma tanto o salário novo do funcionário, quanto o antigo e ainda a diferença entre os valores.

O próximo exemplo que veremos será referente a trigger do banco de dados Firebird, onde utilizaremos o mesmo conceito de mudança de salário, como mostra a Listagem 7.


    create trigger audita_salarioFuncionario for funcionario
         after update
      as
      begin
         if (old.salarioFuncionario <> new.salarioFuncionario) then
            insert into historico
               (idfuncionario, datamodificacao,
                usuario, salarioFuncionario_antigo,
                percentagem
               )
               values (
                  old.idfuncionario, 'now', user, old.salarioFuncionario,
                  (new.salarioFuncionario/old.salarioFuncionario-1) * 100
               );
      end;
Listagem 7. Exemplo de trigger no Firebird

A trigger realiza uma atualização das informações principais referentes a mudança do salário de um funcionário, apresentando além disso a porcentagem de mudança entre o salário antigo e o novo salário. Mas para isso, verificamos antes se o novo salário é diferente do antigo para que possamos realizar as operações necessárias.

Por último, temos o exemplo que mostra como criar a trigger no SQL Server, que tem o objetivo de que, quando um registro for inserido na tabela funcionários, também realizemos a inserção dos dados na tabela de backup, mantendo uma cópia de algumas das informações do funcionário na tabela funcionarioBackup. Após a criação das tabelas, iremos preenchê-las com alguns dados, como mostra a Listagem 8.


    create trigger trg_copiafuncionario 
      on dbo.funcionario
      after insert as
      begin
      insert into funcionarioBackup
      select * from inserted
      end
      go
Listagem 8. Exemplo de trigger no SQL Server

Ao criarmos nossa trigger, inserimos um novo registro na tabela de funcionários, onde neste mesmo instante inserimos um registro na tabela de funcionarioBackup, pois no momento da criação da trigger utilizamos o AFTER INSERT na tabela de funcionarios, o que nos diz que, após a inserção do registro na tabela de funcionários, será realizada a inserção dos mesmos dados na tabela de cópia.

A tabela INSERTED é uma tabela temporária, que tem por objetivo manter apenas as linhas que são inseridas e, devido a isso, escolhemos esta opção para manter as informações sincronizadas em ambas as tabelas. Para testarmos a nossa trigger, iremos inserir alguns registros na tabela de funcionários e, em seguida, realizaremos um select na tabela de funcionarioBackup para ver o resultado, como mostra a Listagem 9.


    insert into funcionario (codigoFunc, nomefunc, email, telefone) 
    values (5, 'Edson Dionisio', 'edson.dionisio@gmail.com', '81997402801') 
    insert into funcionario (codigoFunc, nomefunc, email, telefone) 
    values (8, 'Maitê Silva', 'maite.silva@gmail.com', '81997402801') 
    insert into funcionario (codigoFunc, nomefunc, email, telefone) 
    values (12, 'Marilia Késsia', testekessia@gmail.com', '81997402801')
    select * from funcionario
    select * from funcionariobackup 
    go
Listagem 9. Inserção de dados na tabela de funcionários

Após a inserção ter sido realizada na tabela de funcionários, podemos ver que as mesmas informações foram salvas na tabela de funcionarioBackup, como era de se esperar.

Podemos ver com isso que a utilização das triggers facilita bastante os processos repetitivos, auditoria de informações, relatório de erros, dentre outras opções que podemos achar necessário para a criação de uma trigger.

Um novo exemplo simples a ser adicionado será com relação a base de dados Postgres: nele iremos inserir dados numa tabela chamada Item e verificaremos a quantidade de itens existentes. Caso esta quantidade atinja o seu limite, teremos uma mensagem de erro disparada pela Trigger. Para vermos este exemplo podemos primeiramente criar uma tabela no Postgres, chamada Item, e a ela adicionaremos apenas os campos idItem e produto. Em seguida, teremos a nossa trigger definida de acordo com a Listagem 10.


    -- Criando a função 
    CREATE OR REPLACE FUNCTION item()
      RETURNS trigger AS
    $BODY$
    Declare LTOTALITENS INT;
    Declare LNOTA INT;
    BEGIN
    
    -- Quantidade de itens inseridos na nota
    Select COUNT(*) into LTOTALiTENS From "Item";
    
    If (LTOTALITENS > 6) Then
    Raise Exception 'A nota % já está cheia. Inclusão cancelada.', LNOTA;
    End If;
    Return New;
    END
    $BODY$
      LANGUAGE plpgsql VOLATILE
      COST 100;
    ALTER FUNCTION item()
      OWNER TO postgres;
    
    -- Criando a trigger
    create trigger "InsereItem" 
    BEFORE INSERT ON "Item" FOR EACH ROW
    EXECUTE PROCEDURE ITEM();
Listagem 10. Criando a trigger pelo Postgres

Trabalhando com Comentários

No que diz respeito a comentários, não há muita diferença entre as bases de dados SQL Server, Oracle e Firebird, pois ambas permitem tanto comentário de linha quanto comentários de múltiplas linhas.

Os comentários de uma linha são representados pelos símbolos (--) ou (/**/) para comentários de múltiplas linhas. Um exemplo que representa ambos os bancos de dados pode ser visto na Listagem 9.


    /* Criação de uma tabela Clientes
      * que tem apenas o objetivo de apresentar os comentários de múltiplas linhas.
      */
      create table Clientes (
        nomeFunc varchar(100),            -- NomeFunc do cliente
        enderecoFunc varchar(255), /* este pode conter o endereço do cliente. 
        Para este são adicionadas as seguintes informações: 
      * Rua,
      * cep,
      * Estado, etc
      */
        telefone varchar(14),  -- Campo para atribuição do telefone
        email varchar (200)       -- Campo responsável pela email do usuário
      ) 
Listagem 11. Comentários de única linha e de múltiplas linhas

O que ocorre no Oracle é que podemos adicionar instruções SQL como parte do código da aplicação que executa as instruções, além de adicionar comentários associados a esquema ou objetos nonschema individuais, que são armazenados no dicionário de dados junto aos metadados dos próprios objetos.

Para comentários referentes a Schema e objetos NonSchema, podemos usar o comando COMMENT para associar um comentário com um objeto de esquema (tabela, view, operadores, indextype, etc). Além disso, temos a possibilidade de criar um comentário sobre uma coluna específica, que é parte de um objeto de esquema da tabela. Os comentários associados a esquemas e objetos nonschema são armazenados no dicionário de dados.

Trabalhando com IF

De igual forma às linguagens de programação, as estruturas condicionais IF...ELSE são utilizadas para que sejam estabelecidas condições no decorrer da execução do fluxo de código, onde temos a atribuição de duas ou mais possibilidades de resultado que podem ser executados.

No Oracle, a instrução IF-THEN-ELSE pode variar, onde dependendo do caso, podemos usar a sintaxe IF-THEN-ELSIF, quando precisamos executar um conjunto de instruções quando condition1 é TRUE ou um conjunto diferente de declarações passa a ser TRUE. Um exemplo dessa utilização pode ser visto na Listagem 10.


    IF salarioFuncionario >= 800 AND salarioFuncionario <= 1400 THEN
      profissao := 'Estagiário em Desenvolvimento de sistemas';
    ELSIF salarioFuncionario > 1400 and salarioFuncionario <= 4000 THEN
      profissao := 'Desenvolvedor Junior';
    ELSIF salarioFuncionario > 4000 and salarioFuncionario <= 9000 THEN
      profissao := ' Desenvolvedor Junior';
    ELSE
      profissao := 'Não há uma profissão cadastrada';
    END IF; 
Listagem 12. Utilização do IF-THEN-ELSIF da Oracle

Com o Firebird não ocorre essa variação, pois ele apresenta apenas a estruturação IF-THEN-ELSE. No entanto, ele possui uma particularidade que é uma forma diferente de escrever a instrução, a qual é apresentada por IIF(), que pode ser comparada a uma operação ternário do C#. A sintaxe para esse caso é apresentada na Listagem 11.


    IIF (<condição>, ResultadoA, ResultadoB)
      <condição>  ::=  Expressão booleana. 
Listagem 13. Apresentação da sintaxe IIF()

Para que possamos entender melhor esta sintaxe, apresentaremos tanto a forma normal da estrutura de fluxo apresentada pelo Firebird quanto a sintaxe apresentada pela Listagem 11. Vejamos então as Listagens 12 e 13.


    if (salarioFuncionario < 3000) then
        funcionario = 'Pode ser estagiário!';
      else
        funcionario = 'É desenvolvedor pleno'; 
Listagem 14. Sintaxe IF...THEN...ELSE

    select iif( sexo = 'Masculino', 'Sr', 'Sra' ) from Clientes
Listagem 15. Sintaxe IIF()

Por último, temos o SQL Server, que possui a estrutura representada apenas por IF-ELSE, e apresentando de igual forma a estrutura particular IIF(). Podemos ver um exemplo na Listagem 16.


    -- Utilizando a base de dados exemplo do SQL Server.
      USE AdventureWorks2012;
      GO
      /* Realizando a contagem de produtos que tenham o nomeFunc iniciado por touring-3000 
      E em caso de existir, apresentar uma mensagem.
      */
      if (select count(*) from production.product where name like 'touring-3000%' ) > 5
      print 'Existem mais que 5 bicicletas touring-3000 bicycles cadastradas.'
      else 
      print 'Existem 5 ou menos bicicletas touring-3000 na base de dados.' ;
      go
Listagem 17. Utilizando IF...ELSE no SQL Server

Veja que a diferença na sintaxe não é tão grande em relação a forma de processamento, principalmente no que tange o uso de triggers.