Fonctions SQL. Vues
2024-10-18
SQL is much like chess—a few hours to learn, a lifetime to master
Lorsqu’une collection de requêtes de même forme est utilisée sur une base de données, il est pertinent de préparer ces requêtes
Les objectifs de cette préparation sont divers:
éviter de coder de façon répétitive
permettre une planification en amont des requêtes
éviter de recalculer des jointures coûteuses
…
Les SGBD relationnels offrent une gamme d’outils pour combler ces besoins
Les PREPARED STATEMENT
Les fonctions
Les vues
Dans SQL et dans PostgreSQL en particulier , il est possible de définir des fonctions ou procédures persistantes (
STORED PROCEDURE
)
Ces fonctions sont des objets permanents d’un schéma (comme les tables, vues, utilisateurs, etc)
Elles permettent de regrouper un traitement complexe en un seul programme que l’on peut appeler à tout moment si on en a le droit
CREATE OR REPLACE FUNCTION
<func_name>(<arg1> <arg1_datatype> [DEFAULT <arg1_default>])
RETURNS <some type> | SETOF <some type> | TABLE (..)
La signature d’une fonction est formée par:
<func_name>
<arg1>
et type attendu <arg1_datatype>
:
CREATE TYPE ...
country.countrycode%TYPE
country%ROWTYPE
…<arg1_default>
: litéral ou expression
Type du résultat:
SETOF ...
(table)TABLE (...)
Les arguments peuvent avoir des valeurs par défaut. Cela permet à l’appelant d’ommettre ces arguments.
Les arguments optionnels doivent être déclarés après les arguments non-optionnels dans la signature de la fonction.
Usage pour une création de table
CREATE TABLE <nom de table> OF
type_utilisateur
(CONSTRAINT <nom de contrainte> PRIMARY KEY (<nom de colonne>));
Pratique pour créer des tables de même schéma
Note
quand on crée une table, PostgreSQL crée automatiquement un type associé (bis)
Les définitions de fonctions comportent parfois des qualifications supplémentaires
VOLATILITY
: IMMUTABLE
, STABLE
, VOLATILE
(default)
SECURITY
: SECURITY DEFINER
, SECURITY INVOKER
SQL est d’abord un langage permettant d’émettre des requêtes, il peut aussi être utilisé pour écrire des fonctions
Dans PostgreSQL, l’utilisation d’un morceau de SQL existant est facile :
prenez vos instructions SQL existantes (éventuellement plusieurs)
ajoutez un en-tête et une conclusion fonctionnels
et … vous avez terminé
Mais cette facilité a un prix
Vous ne pouvez pas :
utiliser des structures de contrôle (boucles, alternatives) ou des définitions de variables locales
exécuter des instructions SQL dynamiques que vous assemblez à la volée en utilisant les arguments passés dans la fonction
Mais, tout de même
Les fonstions SQL exécutent une suite arbitraire d’expressions (requêtes) SQL, elles renvoient le résultat de la dernièrere requête.
In the simple (non-set) case, the first row of the last query’s result will be returned. Bear in mind that “the first row” of a multirow result is not well-defined unless you use ORDER BY. If the last query happens to return no rows at all, the null value will be returned.
La syntaxe du corps de la fonction dépend du language_of_function
Dans le cas où language_of_function
est SQL
On peut faire références aux arguments en les nommant dans le corps de la fonction
Si les arguments ne sont pas nommés, vous faites référence aux arguments par leur position dans la signature : $1
, $2
, $3
, etc
Si vous nommez les arguments, vous pouvez utiliser la notation
lors des appels à la fonction
Les fonctions SQL peuvent retourner des ensembles
Trois manières de faire:
ANSI SQL standard: RETURNS TABLE
spécifier des paramètres OUT
et retourner SETOF RECORD
types de données composés SETOF ...
Dans le schéma world
, on veut écrire une fonction qui prend en argument une région et renvoie la population maximale parmi les capitales de la région
Dans le schéma world
, on veut écrire une fonction qui prend en argument une région et renvoie le nom et la population de la capitale la plus peuplée de cette région
On peut utiliser le qualifiant OUT
pour désigner des paramètres de sortie.
CREATE OR REPLACE
FUNCTION username.capitale(
p_region text,
OUT o_name_capital TEXT,
OUT o_population_capital INTEGER)
RETURNS RECORD
LANGUAGE SQL AS
$$
WITH r AS (
SELECT cc.*, RANK() OVER (PARTITION BY c.region ORDER BY cc.population_city DESC) AS rnk
FROM world.country c JOIN
world.city cc ON (c.capital=cc.id)
WHERE c.region=p_region
)
SELECT r.name_city, r.population_city
FROM r
WHERE r.rnk = 1 ;
$$ ;
Dans le schéma world
, on veut écrire une fonction qui prend en argument une région et renvoie la description de la capitale la plus peuplée de cette région
On se contente de nommer la table pour indiquer que le résultat doit avoir même structure qu’une ligne de la table
CREATE OR REPLACE FUNCTION username.capitale(p_region text)
RETURNS world.city
LANGUAGE SQL AS
$$
WITH r AS(
SELECT cc.*, RANK() OVER (PARTITION BY c.region ORDER BY cc.population_city DESC) AS rnk
FROM world.country c JOIN world.city cc ON (c.capital=cc.id)
WHERE c.region=p_region)
SELECT r.id, r.name_city, r.countrycode, r.district, r.population_city
FROM r
WHERE r.rnk = 1 ;
$$ ;
Dans le schéma world
, on veut écrire une fonction qui prend en argument une région et renvoie la table des capitales de la région
On se contente d’abord de renvoyer le nom de la capitale.
On explicite le schéma de la table résultat
https://www.postgresql.org/docs/15/xfunc-sql.html
On veut maintenant récupérer une table de même schéma que city
.
La solution est très simple.
On profite de ce qu’à chaque table correspond un type de même nom et on utilise le mot-clé SETOF
.
PL/pgSQL
C
PL/Python
PL/R
…
pagila
pagila
p_film_count |
---|
60 |
61 |
62 |
Presque tous les SGBD relationnels proposent des vues comme niveau d’abstraction des tables. Dans une vue, vous pouvez interroger plusieurs tables et présenter des colonnes dérivées supplémentaires basées sur des calculs complexes.
Les vues sont généralement en lecture seule, mais PostgreSQL vous permet de mettre à jour les données sous-jacentes en mettant à jour la vue, à condition que la vue provienne d’une seule table.
Pour mettre à jour des données à partir de vues qui joignent plusieurs tables, vous devez créer une gachette contre la vue.
Les vues matérialisées mettent en cache les données afin d’accélérer les requêtes les plus courantes, au détriment des données les plus récentes.
Astuce
Si vous vous retrouvez à écrire tous les jours la même requête, créer une vue !
Une vue n’est rien d’autre qu’une requête stockée de façon persistante
Une vue s’interroge comme une table :
region |pop |
-------------------------+----------+
Eastern Asia |1507328000|
Southern and Central Asia|1490776000|
Southeast Asia | 518541000|
Middle East | 188380700|
Si
VIEW
) provient d’une table uniqueAlors
on peut modifier la table au travers de la vue. On peut
Exemple
co_asia
provient d’une unique table : world.country
co_asia
contient la clé primaire countrycode
de la table world.country
Cette instruction :
… est légale !
–
Mais, après exécution :
la vue co_asia
est vide
les pays d’Asie de country
sont délocalisés en Antarctique
WITH CHECK OPTION
Ajouter cette modification lors de la création de la vue et PostgreSQL s’opposera à une tentative d’insertion hors de la vue ou à une tentative de mise à jour qui placerait des tuples hors de la vue
CREATE OR REPLACE VIEW pagila.actor_info
AS SELECT a.actor_id,
a.first_name,
a.last_name,
pagila.group_concat(DISTINCT (c.name::text || ': '::text) ||
(( SELECT pagila.group_concat(f.title::text) AS group_concat
FROM pagila.film f
JOIN pagila.film_category fc_1 ON f.film_id = fc_1.film_id
JOIN pagila.film_actor fa_1 ON f.film_id = fa_1.film_id
WHERE fc_1.category_id = c.category_id AND fa_1.actor_id = a.actor_id
GROUP BY fa_1.actor_id))
) AS film_info
FROM pagila.actor a
LEFT JOIN pagila.film_actor fa ON a.actor_id = fa.actor_id
LEFT JOIN pagila.film_category fc ON fa.film_id = fc.film_id
LEFT JOIN pagila.category c ON fc.category_id = c.category_id
GROUP BY a.actor_id, a.first_name, a.last_name;
world
Problème : quelles tables doivent être modifiées ?
supprimer les lignes de country
?
supprimer les lignes de city
?
Les vue matérialisées cachent les données récupérées
Cela se produit
lors de la création de la vue et
lorsque on exécute REFRESH MATERIALIZED VIEW ...
Lorsque vous marquez une vue comme matérialisée, elle n’interrogera à nouveau les données que lorsque vous lancerez la commande
REFRESH
.
L’avantage est que vous ne gaspillez pas de ressources en exécutant des requêtes complexes à plusieurs reprises ;
lL’inconvénient est que vous risquez de ne pas avoir les données les plus à jour lorsque vous utilisez la vue.
Fonctions SQL et Vues
MA15Y030 – Bases de Données – L3 MIASHS – UParis Cité