Postgres stored procedure sql - ghdrako/doc_snipets GitHub Wiki
Główną zaletą czystych funkcji SQL jest, jeśli są one dość proste, ich integracja podczas wewnętrznego przepisywania zapytania (inlining): nie są one zatem "czarnymi skrzynkami" dla optymalizatora. I odwrotnie, optymalizator nie wie nic o zawartości funkcji PL/pgSQL.
W poniższym przykładzie funkcja służy jako filtr zapytania. Podobnie jak w czystym SQL, pozwala na użycie indeksu w kolumnie date_embauche poza tabelą employes_big:
CREATE FUNCTION employe_eligible_prime_sql (service int, date_embauche date)
RETURNS boolean
LANGUAGE sql
AS $$
SELECT ( service !=3 AND date_embauche < '2003-01-01') ;
$$ ;
employes_big
EXPLAIN (ANALYZE) SELECT matricule, num_service, nom, prenom
FROM
WHERE
employe_eligible_prime_sql (num_service, date_embauche) ;
QUERY PLAN--------------------------------------------------------------------------------
Index Scan using employes_big_date_embauche_idx on employes_big
(cost=0.42..1.54 rows=1 width=22) (actual time=0.008..0.009 rows=1 loops=1)
Index Cond: (date_embauche < '2003-01-01'::date)
Filter: (num_service <> 3)
Rows Removed by Filter: 1
Planning Time: 0.102 ms
Execution Time: 0.029 ms
W przypadku wersji tej samej funkcji w PL/pgSQL harmonogram nie widział indeksowanego kryterium. Nie ma innego wyjścia, jak tylko przeczytać całą tabelę i wywołać funkcję dla każdego wiersza, która jest oczywiście wolniejsza:
CREATE FUNCTION employe_eligible_prime_pl (service int, date_embauche date)
RETURNS boolean
LANGUAGE plpgsql AS $$
BEGIN
RETURN ( service !=3 AND date_embauche < '2003-01-01') ;
END ;
$$ ;
EXPLAIN (ANALYZE) SELECT matricule, num_service, nom, prenom
FROM employes_big
WHERE employe_eligible_prime_pl (num_service, date_embauche) ;
------------------------------------QUERY PLAN--------------------------------------------
Seq Scan on employes_big (cost=0.00..134407.90 rows=166338 width=22)
(actual time=0.069..269.121 rows=1 loops=1)
Filter: employe_eligible_prime_pl(num_service, date_embauche)
Rows Removed by Filter: 499014
Planning Time: 0.038 ms
Execution Time: 269.157 ms
A function is part of a normal SQL statement and is not allowed to start or commit transactions.
In contrast, a procedure can control transactions and even run multiple transactions one after the other. However, you cannot run it inside a SELECT statement. Instead, you have to invoke CALL.
Code of the function is passed to PostgreSQL as a string (') or $$
.
CREATE OR REPLACE FUNCTION mysum(int, int)
RETURNS int AS
'
SELECT $1 + $2;
' LANGUAGE 'sql';
-- two languages have assigned a meaning to $$. In Perl, as well as in Bash scripts, $$ represents the process ID.
CREATE OR REPLACE FUNCTION mysum(int, int)
RETURNS int AS
$$
SELECT $1 + $2;
$$ LANGUAGE 'sql';
--
CREATE OR REPLACE FUNCTION mysum(int, int)
RETURNS int AS
$body$
SELECT $1 + $2;
$body$ LANGUAGE 'sql';
create or replace function get_all_albums
(
in artistid bigint,
out album text,
out duration interval
)
returns setof record
language sql
as $$
select album.title as album,
sum(milliseconds) * interval '1 ms' as duration
from album
join artist using(artistid)
left join track using(albumid)
where artist.artistid = get_all_albums.artistid
group by album
order by album;
$$;
This function is written in PL/SQL, so it’s basically a SQL query that accepts parameters. To run it, simply do as follows:
select * from get_all_albums(127);
select *
from get_all_albums(
(select artistid
from artist
where name = 'Red Hot Chili Peppers'));
select album, duration
from artist,
lateral get_all_albums(artistid)
where artist.name = 'Red Hot Chili Peppers';
function inlining
numeric AS
$$
SELECT log(2, $1);
$$
LANGUAGE 'sql' IMMUTABLE;
Anonymous code blocks
don’t take parameters and are not permanently stored in the database since they don’t have names.
test=# DO
$$
BEGIN
RAISE NOTICE 'current time: %', now();
END;
$$ LANGUAGE 'plpgsql';
NOTICE: current time: 2022-11-10 11:18:08.640424+01
DO