Oracle Trigger

Eu tenho dois esquema de relação:

FUNCIONARIO (CPF. PNOME, SNOME, CPF_GERENTE, NOME_DEPARTAMENTO, SALARIO)
DEPARTAMENTO (NOME_DEPARTAMENTO, NUMERO_FUNCIONARIOS)

O que eu quero fazer é “ao inserir um novo funcionário de um departamento em FUNCIONARIO, soma-se um ao número que esteja em NUMERO_FUNCIONARIOS em DEPARTAMENTO”. Assim, crio uma trigger em FUNCIONARIO:

[code]create or replace trigger tri_fun_ndep after insert or update or delete on funcionario
for each row
declare
begin
if updating then

  if ( :old.nome_departamento = 'TI' and :new.nome_departamento <> 'TI') then
    //update departamento set numero_funcionarios = :old.numero_funcionarios -1 
    //where nome_departamento = 'TI';
    
    update departamento set numero_funcionarios = (select numero_funcionarios from departamento
    where nome_departamento = :new.nome_departamento) - 1 where nome_departamento = 'TI';
  end if;
end if;

end;
[/code]

A linha comentada é como eu gostaria de fazer, pois é mais simples. Simplesmente pegar o valor antigo que se encontra em NUMERO_FUNCIONARIOS em DEPARTAMENTO, subtrair de um e depois salvar. Não posso fazer isso porque a trigger diz que o contexto global é a tabela FUNCIONARIO, gerando o erro:

Uma maneira de fazer funcionar se encontra nas linhas 11 e 12 acima.

Como é que eu posso pegar o valor antigo e novo da tabela DEPARTAMENTO sem fazer o que fiz?

Acho que seria mais simples (e mais seguro) recontar todos os funcionários de ambos departamentos.
Algo como:

  UPDATE departamento SET numero_funcionarios = ( SELECT COUNT(*) FROM funcionarios WHERE departamento = old.departamento ) WHERE departamento = old.departamento;
  UPDATE departamento SET numero_funcionarios = ( SELECT COUNT(*) FROM funcionarios WHERE departamento = new.departamento ) WHERE departamento = new.departamento;

Aliás, por que está armazenando o número de funcionários na tabela departamento?
Teve problemas de performance para isso?

Ao invés de usar uma trigger, que geralmente é melhor usar com muita moderação, poderia criar uma view com esse count online.

[quote=AbelBueno]Acho que seria mais simples (e mais seguro) recontar todos os funcionários de ambos departamentos.
Algo como:

  UPDATE departamento SET numero_funcionarios = ( SELECT COUNT(*) FROM funcionarios WHERE departamento = old.departamento ) WHERE departamento = old.departamento;
  UPDATE departamento SET numero_funcionarios = ( SELECT COUNT(*) FROM funcionarios WHERE departamento = new.departamento ) WHERE departamento = new.departamento;

Aliás, por que está armazenando o número de funcionários na tabela departamento?
Teve problemas de performance para isso?

Ao invés de usar uma trigger, que geralmente é melhor usar com muita moderação, poderia criar uma view com esse count online.[/quote]

Se a cada inserção eu tiver que contar a tabela inteira, um overhead será criado. A cada inserção de funcionário, já é somado um ao valor que se encontra em NUMERO_FUNCIONARIOS em DEPARTAMENTO.

Agora, tem como eu acessar a o valor “old” de NUMERO_FUNCIONARIOS de DEPARTAMENTO sem um SELECT, como abaixo? Lembrando que o comando abaixo gera erro.

update departamento set numero_funcionarios = :old.numero_funcionarios -1 where nome_departamento = 'TI';

Já existe o overhead do update. O banco costuma ser “esperto” o suficiente para contar registros.
Experimente medir os tempos.

Você não precisa do old, pode usar o valor do campo normalmente.

  UPDATE departamento SET numero_funcionario = numero_funcionario + 1 WHERE departamento = new.departamento;

[quote=AbelBueno][quote=ECO2004]
Se a cada inserção eu tiver que contar a tabela inteira, um overhead será criado. A cada inserção de funcionário, já é somado um ao valor que se encontra em NUMERO_FUNCIONARIOS em DEPARTAMENTO.
[/quote]

Já existe o overhead do update. O banco costuma ser “esperto” o suficiente para contar registros.
Experimente medir os tempos.

UPDATE departamento SET numero_funcionario = numero_funcionario + 1 WHERE departamento = new.departamento; [/quote]

@Abel

Eu estou recebendo um erro de tabela mutante no código abaixo - linha 04.

if deleting then
    
      update departamento set numero_funcionarios = numero_funcionarios - 1 
      where nome_departamento = :old.nome_departamento;
      
      update historico set data_desligamento = sysdate where cpf = :old.cpf;
      
end if;

A minha dúvida:

Quando uma linha é apagada em FUNCIONARIO, o valor de qualquer campo dessa linha pode ser pega com :old, não é? O meu trigger é disparado quando uma determinada linha é apagada. Eu quero saber o valor do CPF que essa linha recém apagada detinha e usar para atualizar o NUMERO_FUNCIONARIOS em DEPARTAMENTO.

Há algo de errado com minha sintaxe e porque está dando erro de tabela mutante? Não vejo acesso à mesma tabela onde o trigger está atuando.

Alguém?