7.1.8. Comandos de Repetição
PL/pgSQL
possui diversos tipos de estruturas para laços de repetição: LOOP
, WHILE
, FOR
e FOREACH
. Além de comandos para desvio do fluxo de instruções: EXIT
e CONTINUE
.
7.1.8.1. FOR
com variável de controle do tipo inteiro
Uma das variantes do laço tipo FOR
possibilita iterar em uma variável do tipo inteiro. Como exemplo deste tipo de laço, vamos criar uma função para computar um certo número de pontos posicionados de forma aleatória. Este exemplo irá mostrar também como retornar um conjunto de registros. No caso, os registros terão o seguinte esquema: (INTEGER, GEOMETRY)
.
CREATE OR REPLACE FUNCTION random_pt_generator(npts NUMERIC)
RETURNS SETOF RECORD
AS $$
DECLARE
tupla RECORD;
longitude NUMERIC;
latitude NUMERIC;
pt GEOMETRY;
BEGIN
RAISE NOTICE 'Computando % pontos aleatórios...', npts;
FOR i IN 1..npts LOOP
longitude := 360.0 * random() - 180.0;
latitude := 180.0 * random() - 90.0;
pt := ST_SetSRID(ST_MakePoint(longitude, latitude), 4326);
tupla := (i, pt); -- ou: SELECT i, pt INTO tupla;
RETURN NEXT tupla;
IF (i % 1000) = 0 THEN
RAISE NOTICE 'random_pt_generator: iteração %', i;
END IF;
END LOOP;
RETURN;
END;
$$
LANGUAGE plpgsql;
A função random_pt_generator
ilustra também um tipo de função que não pode ser utilizada somente na cláusula SELECT
pois ela retorna um conjunto de tuplas. Neste caso, deve-se utilizar a função na cláusula FROM
, mas com um cuidado especial, que é definir o tipo do conjunto retornado. Abaixo, mostramos como criar uma consulta que computa 5 pontos:
SELECT gid, ST_AsText(geom)
FROM random_pt_generator(5) AS tabela(gid INTEGER, geom GEOMETRY);
Saída:
NOTICE: Computando 5 pontos aleatórios...
gid | st_astext
-----+--------------------------------------------
1 | POINT(25.8069811575115 4.40172334201634)
2 | POINT(-31.4398757368326 18.4167264495045)
3 | POINT(-42.4448257684708 -74.7917356621474)
4 | POINT(12.7397736534476 -52.6765340007842)
5 | POINT(87.8538537584245 -22.9236561711878)
(5 rows)
7.1.8.2. FOR
sobre o resultado de uma consulta
Outro tipo de laço FOR
pode ser empregado para iterar nas tuplas resultantes de uma consulta. Para ilustrar este tipo de laço, vamos construir uma função chamada build_pt_table
que irá criar uma tabela geométrica com pontos gerados de forma aleatória usando como base nossa função random_pt_generator
. Ao final esta função irá criar uma chave primária e um índice espacial sobre a coluna geométrica da tabela criada.
CREATE OR REPLACE FUNCTION build_pt_table(table_name TEXT,
npts NUMERIC)
RETURNS VOID
AS
$$
DECLARE
tupla RECORD;
i INTEGER DEFAULT 1;
BEGIN
RAISE NOTICE 'Criando tabela %...', table_name;
EXECUTE 'CREATE TABLE ' || table_name ||
'(gid INTEGER, geom GEOMETRY(POINT,4326))';
FOR tupla IN SELECT *
FROM random_pt_generator(npts) AS tabela(gid INTEGER, geom GEOMETRY)
LOOP
EXECUTE 'INSERT INTO ' || table_name ||
'(gid, geom) VALUES($1, $2)' USING tupla.gid, tupla.geom;
IF (i % 1000) = 0 THEN
RAISE NOTICE 'Inseridos % tuplas!', i;
END IF;
i = i + 1;
END LOOP;
RAISE NOTICE 'Criando chave primária...';
EXECUTE format('ALTER TABLE %I ADD PRIMARY KEY(gid)', table_name);
RAISE NOTICE 'Criando índice espacial...';
EXECUTE 'CREATE INDEX spidx_' || table_name ||
'_geom ON ' || table_name || ' USING GIST(geom)';
RETURN;
END;
$$
LANGUAGE plpgsql;
Usando a função build_pt_table
, vamos criar uma tabela chamada pt10k
contendo 10.000 pontos:
SELECT build_pt_table('pt10k', 10000);
Saída:
NOTICE: Criando tabela pt10k...
NOTICE: Computando 10000 pontos aleatórios...
...
NOTICE: Inseridos 1000 tuplas!
NOTICE: Inseridos 2000 tuplas!
NOTICE: Inseridos 3000 tuplas!
NOTICE: Inseridos 4000 tuplas!
NOTICE: Inseridos 5000 tuplas!
NOTICE: Inseridos 6000 tuplas!
NOTICE: Inseridos 7000 tuplas!
NOTICE: Inseridos 8000 tuplas!
NOTICE: Inseridos 9000 tuplas!
NOTICE: Inseridos 10000 tuplas!
NOTICE: Criando chave primária...
NOTICE: Criando índice espacial...
build_pt_table
----------------
(1 row)
Outra forma útil deste tipo de FOR
utiliza o comando EXECUTE
:
[<<rótulo>>]
FOR variável-tupla IN EXECUTE query-string [USING expressão [, ... ]] LOOP
comandos;
END LOOP [rótulo];
7.1.8.3. WHILE
[<<rótulo>>]
WHILE expressão-booleana LOOP
comandos;
END LOOP [rótulo];
7.1.8.4. LOOP
: repetições incondicionais
[<<rótulo>>]
LOOP
comandos;
END LOOP [rótulo];
7.1.8.5. EXIT
: interrompendo um laço ou bloco de comandos
EXIT [rótulo] [WHEN expressão-booleana];
7.1.8.6. CONTINUE
: desviando o fluxo de uma laço
CONTINUE [rótulo] [WHEN expressão-booleana];
7.1.8.7. FOREACH
: iterando sobre arrays
[<<rótulo>>]
FOREACH variável [SLICE número] IN ARRAY expressão-array LOOP
comandos;
END LOOP [rótulo];