Criando uma função para retornar múltiplas linhas como resultado

8Comments

Continuando nossa série de posts falando sobre funções/procedures no PostgreSQL, neste artigo falaremos sobre como criar uma função para retornar múltiplas linhas no resultado.

Como vimos no post anterior, em criando uma função que retorna linha usando o tipo record é bem simples retornar uma única linha como resultado da função. O problema é que dessa forma não conseguimos retornar mais de uma linha nesse resultado. O retorno de uma única linha nos atende quando queremos por exemplo, retornar todos os dados de um único cliente ou os dados de login de um determinado usuário através do seu e-mail e senha. Mas se quisermos listar todos os clientes ativos da nossa tabela ou então, todos os produtos de um catálogo ficamos bastante limitados.

Então vamos modificar um pouco a função criada no exemplo do artigo anterior para criar uma função que retorna muitas linhas em uma única execução.

Basicamente a modificação consiste em 2 detalhes:

  1. O tipo de retorno da função, especificado no RETURNS logo no cabeçalho;
  2. A definição de um LOOP interno na função

Especificação no cabeçalho

Na função anterior o cabeçalho era formado retornando um simples RECORD:

CREATE OR REPLACE FUNCTION retorna_linhas()
 RETURNS record AS

Para retornar mais de uma linha continuaremos retornando um tipo RECORD, porém, precisamos definir que retornaremos um SETOF RECORD:

CREATE OR REPLACE FUNCTION retorna_linha()
 RETURNS SETOF record AS

A utilidade do SETOF é justamente essa: retornar uma lista ao invés de uma única linha. Na verdade, nada impede que mesmo utilizando o SETOF seja retornada apenas uma única linha, mas ao se deparar com o SETOF o interpretador estará preparado para retornar também diversas linhas da função PostgreSQL ao ser executada.

Definição do LOOP interno

Na função anterior, onde retornamos apenas uma única linha, tínhamos a seguinte ação dentro da função:

SELECT id, nome, email INTO RESULT FROM cadastro WHERE id = 1;

A simples execução do SELECT … INTO faz com que o resultado seja executado dentro do RECORD de nome result. Acontece que sem a execução de um LOOP, a função só consegue retornar uma única linha de resultado. Então adaptando nosso código anterior, esse trecho da função ficaria assim:

FOR RESULT IN SELECT id, nome, email FROM cadastro WHERE id = 1
LOOP
  RETURN NEXT RESULT;
END LOOP;

Note que o resultado da execução da query na tabela cadastro é jogado na variável result (do tipo RECORD) de uma forma diferente, que funciona como uma espécie de ponteiro e através do FOR criamos um LOOP. O trecho RETURN NEXT result é utilizado para definir que cada iteração do LOOP será retornada como resultado da função.

Seria possível também manipular os valores da variável result antes do seu retorno:

FOR RESULT IN SELECT id, nome, email FROM cadastro WHERE id = 1
LOOP
  IF RESULT.nome IS NULL THEN
    RESULT.nome := 'Nao Informado';
  END IF;
  RETURN NEXT RESULT;
END LOOP;

Dessa forma, caso o campo nome não tenha sido informado no momento do cadastro e a tabela aceite valores nulos nessa coluna, ao retornar os resultados, a função já manipulará esse valor nulo e substituirá pela string Não informado. Poderia ser realizado um cálculo, utilizar outras funções como SUBSTRING, COALESCE, tratando o formato da data de retorno usando a função to_date(), etc.. A aplicação vai de acordo com a necessidade!

Como resultado final temos o seguinte resultado:

CREATE OR REPLACE FUNCTION retorna_linhas()
 RETURNS SETOF record AS
$$
DECLARE
  RESULT    RECORD; -- Variavel que armazenará o retorno da query
BEGIN
 
  -- Vamos criar o LOOP para retornar a execução da query na tabela cadastro
  FOR RESULT IN SELECT id, nome, email FROM cadastro WHERE id = 1
  LOOP
    -- O retorno de cada linha
    RETURN NEXT RESULT;
  END LOOP;
 
RETURN RESULT;
 
END;
$$
LANGUAGE 'plpgsql' VOLATILE;

E agora a chamada dessa execução será exatamente igual à chamada da função anterior, que retorna uma única linha:

SELECT * FROM retorna_linhas() AS (id INTEGER, nome VARCHAR, email VARCHAR)

Muito simples!

E você, em que ocasiões costuma utilizar esse tipo de funções em seus projetos? Compartilhe conosco suas experiências deixando seu comentário nos campos abaixo. E não deixe de assinar nosso FEED preenchendo seu e-mail na caixa lateral do site. Assim você receberá automaticamente nossas atualizações e ficará por dentro das novidades!

Até breve…

Tags:, , ,