[firebase-br] Melhorar performance - pedido x situação

Tecnobyte Informática temp2 em tecnobyte.com.br
Seg Set 26 18:06:55 -03 2011


Tenho as seguintes tabelas:

Pedido(
  Id INTEGER NOT NULL,
  DataEmissao DATE NOT NULL,
  ...
  CONSTRAINT PK_Pedido PRIMARY KEY(Id)
);

PedidoSituacao(
  Id INTEGER NOT NULL,
  Pedido_Id INTEGER NOT NULL,
  Situacao SMALLINT NOT NULL,
  DataSituacao DATE NOT NULL,
  ...
  CONSTRAINT PK_PedidoSituacao PRIMARY KEY(Id),
  CONSTRAINT FK_PedidoSituacao_Pedido FOREIGN KEY(Pedido_Id)
    REFERENCES Pedido(Id) ON DELETE CASCADE
);

CREATE DESC INDEX IN_PedidoSituacao_Id ON PedidoSituacao(Id);

A situação de um pedido pode ser:

1-Aberto
2-Conferido
3-Faturado
4-Enviado

Para obter os dados do pedido com respectiva situação pode-se fazer assim:

SELECT
  Pedido.Id,
  Pedido.DataEmissao,
  PedidoSituacao.Situacao,
  PedidoSituacao.DataSituacao,
  ...
FROM Pedido
JOIN PedidoSituacao ON PedidoSituacao.Id =
  (SELECT FIRST 1 PedidoSituacao.Id FROM PedidoSituacao
    WHERE PedidoSituacao.Pedido_Id = Pedido.Id
    ORDER BY PedidoSituacao.Id DESC)
...

Para simplificar criei um campo calculado assim:

ALTER TABLE Pedido
ADD PedidoSituacao_Id =
(SELECT FIRST 1 PedidoSituacao.Id FROM PedidoSituacao
  WHERE PedidoSituacao.Pedido_Id = Pedido.Id);

Desta forma o JOIN mostrado acima fica mais simples:
  ...
FROM Pedido
JOIN PedidoSituacao ON PedidoSituacao.Id = Pedido.PedidoSituacao_Id
...

As duas formas apresentadas acima funcionam razoavelmente bem. Porém para 
filtrar pela última situação do pedido geralmente fica lento se houver uma 
grande quantidade de pedidos (dezenas de milhares, por exemplo).

Para contornar este problema, troquei o campo calculado 
(Pedido.PedidoSituacao_Id) por um campo físico:

ALTER TABLE Pedido
ADD PedidoSituacao_Id INTEGER,
ADD CONSTRAINT FK_Pedido_PedidoSituacao FOREIGN KEY(PedidoSituacao_Id)
  REFERENCES PedidoSituacao(Id) ON DELETE SET NULL;

E criei trigger para atualizar este campo toda vez que inserir, alterar ou 
excluir a situação de um pedido:

SET TERM ^ ;

CREATE OR ALTER TRIGGER TG_PedidoSituacao_Antes FOR PedidoSituacao AFTER 
INSERT OR UPDATE OR DELETE AS
DECLARE VARIABLE Pedido_Id INTEGER;
BEGIN
  IF (INSERTING OR UPDATING) THEN
    Pedido_Id = NEW.Pedido_Id;
  ELSE
    Pedido_Id = OLD.Pedido_Id;
  UPDATE Pedido SET PedidoSituacao_Id =
    (SELECT FIRST 1 PedidoSituacao.Id FROM PedidoSituacao
     WHERE PedidoSituacao.Pedido_Id = Pedido.Id)
  WHERE Pedido.Id = :Pedido_Id;
END^

NOTA! Os comandos acima são apenas para ilustrar a situação. Talvez esteja 
com erros de sintaxe.

Depois que fiz isto, a consulta de pedidos pela última situação ficou ótima, 
mas ficou com cara de gambiarra!

Alguém sabe uma forma mais adequada e profissional de se fazer isto?

Atenciosamente.

Daniel P. Guimarães
Tecnobyte Informática
www.tecnobyte.com.br





Mais detalhes sobre a lista de discussão lista