Solução em MS SQL
As funções de gatilho do MS SQL possuem tabelas deleted
de inserted
sistema nas quais todas as linhas afetadas pela operação são armazenadas. Você pode contar linhas atualizadas:
set @updatedCount = (select count(*) from deleted)
ou descubra o valor mínimo:
set @updatedMinimumCol1 = (select min(col1) from deleted)
Problema com PostgreSQL
Para FOR EACH ROW
triggers posso usar registros de sistema OLD e NEW, mas eles armazenam apenas 1 linha para cada chamada de trigger. As chamadas do gatilho são separadas, portanto, se o usuário atualizar 10 linhas, o gatilho será chamado 10 vezes, mas a cada vez posso saber apenas cerca de 1 linha atual, não todas as 10 linhas.
Pois FOR EACH STATEMENT
não conheço nenhum mecanismo de acesso a linhas atualizadas. Eu uso o PostgreSQL v9.6 e fui introduzido na v10.OLD TABLE
NEW TABLE
O PostgreSQL não permite que as tabelas antigas e novas sejam referenciadas em triggers de nível de instrução, ou seja, as tabelas que contêm todas as linhas antigas e/ou novas, que são referenciadas pelas cláusulas OLD TABLE e NEW TABLE no padrão SQL.
Tente com a coluna adicional transaction_timestamp()
Posso adicionar uma coluna especial DEFAULT transaction_timestamp()
à tabela principal e usá-la para distinguir as linhas recém-atualizadas de outras, mas não é uma solução, pois várias INSERTs/UPDATEs
podem estar em uma transação e elas terão o mesmo carimbo de data e hora da transação. Provavelmente eu poderia limpar essa coluna de carimbo de data/hora no gatilho após cada instrução para evitar esse problema, mas como fazer isso se essa limpeza emitir o gatilho de atualização novamente - será uma chamada de gatilho de atualização infinita.
Portanto, esta tentativa falhou.
Solução ruim no PostgreSQL
A única maneira que eu sei é que:
Primeiro, use FOR EACH ROW
o gatilho para coletar estatísticas atuais (min e contagem) como funções agregadas. Eu uso a tabela temporária para armazená-la entre as chamadas (esse gatilho é chamado 1 vez para cada linha). Mas não saberemos qual linha é a última (quando chegará a hora de usar essas estatísticas).
CREATE TEMP TABLE IF NOT EXISTS _stats (
_current_min int,
_current_count int
) ON COMMIT DROP;
IF EXISTS(SELECT 1 FROM _stats LIMIT 1) THEN
--Current row is not first, there is statistics for previous rows.
UPDATE _stats
SET _current_min = (CASE WHEN NEW.col1 < _current_min THEN NEW.col1
ELSE _current_min END)
, _current_count = _current_count + 1;
ELSE
--There is no stats because current row is first for this INSERT/UPDATE
INSERT INTO _stats (_current_min, _current_count)
VALUES (NEW.col1, 1);
END IF;
Segundo, use FOR EACH STATEMENT
o gatilho para usar as estatísticas coletadas. Não se esqueça de limpar a tabela temporária (se o usuário executar vários INSERTs/UPDATEs em uma transação, as estatísticas antigas permanecerão na tabela temporária e corromperão todos os próximos cálculos!).
Para tarefas mais complexas podemos criar tabelas temporárias inserted
e deleted
da mesma forma que _stats
.
A solução alternativa
No PostgreSQL podemos usar a cláusula RETURNING para INSERT/UPDATE/DELETE para obter novos valores de todas as linhas afetadas pela operação. Então podemos manipular com eles, mas cada função com INSERTs/UPDATEs tem que implementar essa tecnologia ===> 1. código adicional em funções com tais INSERTs/UPDATEs - duplicação do RETURNING; 2. podemos esquecer de implementar tal tecnologia para nova função; 3. os dados serão corrompidos, pois as manipulações necessárias não serão chamadas automaticamente (como os gatilhos).
A questão
Talvez você conheça uma maneira melhor de acessar todas as linhas afetadas por INSERT/UPDATE?