forked from gleu/pgdocs_fr
/
xaggr.xml
174 lines (158 loc) · 7.36 KB
/
xaggr.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
<?xml version="1.0" encoding="UTF-8"?>
<!-- Dernière modification
le $Date$
par $Author$
révision $Revision$ -->
<sect1 id="xaggr">
<title>Agrégats utilisateur</title>
<indexterm zone="xaggr">
<primary>fonctions agrégat</primary>
<secondary>extension</secondary>
</indexterm>
<para>
Dans <productname>PostgreSQL</productname>, les fonctions d'agrégat
sont exprimées comme des <firstterm>valeurs d'état</firstterm>
et des <firstterm>fonctions de transition d'état</firstterm>.
C'est-à-dire qu'un agrégat opère en utilisant une valeur d'état qui est mis
à jour à chaque ligne traitée.
Pour définir une nouvelle fonction d'agrégat, on choisit
un type de donnée pour la valeur d'état, une valeur initiale pour l'état et
une fonction de transition d'état. La fonction de transition d'état est
une fonction ordinaire qui peut être utilisée hors agrégat.
Une <firstterm>fonction finale</firstterm> peut
également être spécifiée pour le cas où le résultat désiré comme agrégat est
différent des données conservées comme valeur d'état courant.
</para>
<para>
Ainsi, en plus des types de données d'argument et de résultat vus par
l'utilisateur, il existe un type de données pour la valeur d'état interne qui
peut être différent des deux autres.</para>
<para>
Un agrégat qui n'utilise pas de fonction finale est un agrégat qui utilise
pour chaque ligne une fonction dépendante des valeurs de colonnes.
<function>sum</function> en est un exemple.
<function>sum</function> débute à zéro et ajoute la valeur de la ligne
courante à son total en cours. Par exemple, pour obtenir un agrégat
<function>sum</function> qui opère sur un type de données nombres
complexes, il suffira décrire la fonction d'addition pour ce type de donnée.
La définition de l'agrégat sera :
<screen>CREATE AGGREGATE somme (complex)
(
sfunc = ajout_complexe,
stype = complexe,
initcond = '(0,0)'
);
SELECT somme(a) FROM test_complexe;
somme
-----------
(34,53.9)
</screen>
(Notez que nous nous reposons sur une surcharge de fonction : il existe
plus d'un agrégat nommé <function>sum</function> mais
<productname>PostgreSQL</productname> trouve le type de somme s'appliquant à
une colonne de type <type>complex</type>.)
</para>
<para>
La définition précédente de <function>sum</function> retournera zéro (la
condition d'état initial) s'il n'y a que des valeurs d'entrée NULL.
Dans ce cas, on peut souhaiter qu' elle retourne NULL — le
standard SQL prévoit que la fonction <function>sum</function> se comporte
ainsi. Cela peut être obtenu par l'omission de l'instruction
<literal>initcond</literal>, de sorte que la condition d'état initial soit
NULL.
Dans ce cas, <literal>sfunc</literal> vérifie l'entrée d'une condition
d'état NULL mais, pour <function>sum</function> et quelques
autres agrégats simples comme <function>max</function> et <function>min</function>, il
suffit d'insérer la première valeur d'entrée non NULL dans la variable d'état
et d'appliquer la fonction de transition d'état à partir de la
seconde valeur non NULL.
<productname>PostgreSQL</productname> fait cela automatiquement si la
condition initiale est NULL et si la fonction de transition est marquée
<quote>strict</quote> (elle n'est pas appelée pour les entrées NULL).
</para>
<para>
Par défaut également, pour les fonctions de transition <quote>strict</quote>,
la valeur d'état précédente reste inchangée pour une entrée NULL.
Les valeurs NULL sont ainsi ignorées.
Pour obtenir un autre comportement, il suffit de ne
pas déclarer la fonction de transition <quote>strict</quote>. À la place,
codez-la de façon à ce qu'elle vérifie et traite les entrées NULL.
</para>
<para>
<function>avg</function> (average = moyenne) est un exemple plus complexe d'agrégat.
Il demande deux états courants : la somme des entrées et le
nombre d'entrées. Le résultat final est obtenu en divisant ces quantités. La
moyenne est typiquement implantée en utilisant comme valeur d'état un
tableau. Par exemple, l'implémentation intégrée de
<function>avg(float8)</function> ressemble à :
<programlisting>CREATE AGGREGATE avg (float8)
(
sfunc = float8_accum,
stype = float8[],
finalfunc = float8_avg,
initcond = '{0,0}'
);
</programlisting>
(<function>float8_accum</function> nécessite un tableau à trois éléments,
et non pas seulement deux, car il accumule la somme des carrés, ainsi que
la somme et le nombre des entrées. Cela permet son utilisation pour
d'autres agrégats que <function>avg</function>.)
</para>
<para>
Les fonctions d'agrégat peuvent utiliser des fonctions d'état transitionnelles
ou des fonctions finales polymorphes. De cette façon, les mêmes fonctions
peuvent être utilisées pour de multiples agrégats. Voir la <xref
linkend="extend-types-polymorphic"/> pour une explication des fonctions polymorphes.
La fonction d'agrégat elle-même peut être
spécifiée avec un type de base et des types d'état polymorphes, ce qui permet
ainsi à une unique définition de fonction de servir pour de multiples types
de données en entrée. Voici un exemple d'agrégat polymorphe :
<programlisting>CREATE AGGREGATE array_accum (anyelement)
(
sfunc = array_append,
stype = anyarray,
initcond = '{}'
);
</programlisting>
Dans ce cas, le type d'état effectif pour tout appel d'agrégat est le type
tableau avec comme éléments le type effectif d'entrée.
</para>
<para>
Voici le résultat pour deux types de données différents en arguments :
<programlisting>SELECT attrelid::regclass, array_accum(attname)
FROM pg_attribute WHERE attnum > 0
AND attrelid = 'pg_tablespace'::regclass GROUP BY attrelid;
attrelid | array_accum
---------------+---------------------------------------
pg_tablespace | {spcname,spcowner,spclocation,spcacl}
(1 row)
SELECT attrelid::regclass, array_accum(atttypid)
FROM pg_attribute WHERE attnum > 0
AND attrelid = 'pg_tablespace'::regclass GROUP BY attrelid;
attrelid | array_accum
---------------+-----------------
pg_tablespace | {19,26,25,1034}
(1 row)
</programlisting>
</para>
<!-- J'ai besoin de la VO, vraiment !!! -->
<para>
En fonction du <quote>contexte</quote> de l'appel, consultable
par le nœud <structname>AggState</structname>, une fonction C
sait si elle est appelée en tant que
transition d'agrégat ou en tant que fonction finale. Par exemple :
<programlisting> if (fcinfo->context && IsA(fcinfo->context, AggState))
</programlisting>
L'intérêt de cette vérification est que, lorsque cela est vrai, la première
entrée doit être une valeur de transition, temporaire, et peut donc être
modifiée sans risque plutôt que d'allouer une nouvelle copie (ceci est
<emphasis>seulement</emphasis> le cas quand la modification d'une entrée passée par
référence est sûre pour une fonction). Voir <literal>int8inc()</literal> pour un
exemple.
</para>
<!-- Rien compris -->
<para>
Pour de plus amples détails, on se réfèrera à la commande
<xref linkend="sql-createaggregate" endterm="sql-createaggregate-title"/>.
</para>
</sect1>