8.10. Tableaux

PostgreSQL autorise de d�finir des colonnes d'une table comme des tableaux multidimensionnels � longueur variable. Des tableaux de n'importe quel type, m�me d�fini par l'utilisateur, peuvent �tre cr��s. (N�anmoins, les tableaux de type composite ou de domaines ne sont pas encore support�s.)

8.10.1. D�claration des types de tableaux

Pour illustrer l'utilisation des types de tableaux, cr�ons cette table :

CREATE TABLE sal_emp (
    nom              text,
    paye_par_semaine integer[],
    planning         text[][]
);

Comme indiqu� ci-dessus, un type de donn�e tableau est nomm� en ajoutant des crochets ([]) au type de donn�e des �l�ments du tableau. La commande ci-dessus cr�era une table nomm�e sal_emp avec une colonne de type text (nom), un tableau � une dimension de type integer (paye_par_semaine), repr�sentant le salaire d'un employ� par semaine et un tableau � deux dimensions de type text (planning), repr�sentant le planning hebdomadaire de l'employ�.

La syntaxe pour CREATE TABLE permet de sp�cifier la taille exacte des tableaux, par exemple :

CREATE TABLE tictactoe (
    carres   integer[3][3]
);

N�anmoins, l'impl�mentation actuelle n'oblige pas au respect des limites en taille du tableau -- le comportement est identique � celui des tableaux dont la longueur n'a pas �t� sp�cifi�e.

En fait, l'impl�mentation actuelle n'oblige pas non plus � d�clarer le nombre de dimensions. Les tableaux d'un type d'�l�ment particulier sont tous consid�r�s comme �tant du m�me type, sans v�rification de la taille ou du nombre de dimensions. Donc, d�clarer le nombre de dimensions ou la taille dans CREATE TABLE a uniquement un but de documentation, cela n'affecte pas le comportement lors de l'ex�cution.

Une syntaxe alternative, conforme au standard SQL:1999, pourrait �tre utilis�e pour les tableaux � une dimension. paye_par_semaine pourrait avoir �t� d�finie ainsi :

    paye_par_semaine  integer ARRAY[4],

Cette syntaxe n�cessite une constante de type entier pour indiquer la taille du tableau. N�anmoins, comme indiqu� pr�c�demment, PostgreSQL n'impose aucune restriction sur la taille.

8.10.2. Saisie de valeurs de type tableau

Pour �crire une valeur de type tableau comme une constante litt�rale, encadrez les valeurs des �l�ments par des accolades et s�parez-les par des virgules. (Si vous connaissez le C, ce n'est pas diff�rent de la syntaxe C pour initialiser les structures.) Vous pouvez mettre des guillemets doubles autour des valeurs des �l�ments, et devez le faire si elles contiennent des virgules ou des accolades. (Plus de d�tails ci-dessous.) Le format g�n�ral d'une constante de type tableau est donc le suivant :

'{ val1 delim val2 delim ... }'

o� delim est le caract�re de d�limitation pour ce type, tel qu'il est enregistr� dans son entr�e pg_type. Parmi les types de donn�es standards fournis par la distribution PostgreSQL, le type box utilise un point-virgule (;) mais tous les autres utilisent une virgule (,). Chaque val est soit une constante du type des �l�ments du tableau ou un sous-tableau. Voici un exemple d'une constante tableau

'{{1,2,3},{4,5,6},{7,8,9}}'

Cette constante a deux dimensions, un tableau 3 par 3 consistant en trois sous-tableaux d'entiers.

(Ces types de constantes de tableau sont en fait un cas particulier des constantes de type g�n�rique abord�es dans la Section 4.1.2.5. La constante est trait�e initialement comme une cha�ne et pass�e � la routine de conversion d'entr�es de tableau. Une sp�cification explicite du type pourrait �tre n�cessaire.)

Maintenant, nous pouvons montrer quelques instructions INSERT.

INSERT INTO sal_emp
    VALUES ('Bill',
    '{10000, 10000, 10000, 10000}',
    '{{"rendez-vous", "repas"}, {}}');

INSERT INTO sal_emp
    VALUES ('Carol',
    '{20000, 25000, 25000, 25000}',
    '{{"rencontre", "repas"}, {"rencontre"}}');
    ERROR:  multidimensional arrays must have array expressions with matching dimensions

Notez que les tableaux � plusieurs dimensions doivent avoir des limites correspondantes pour chaque dimension. Une diff�rence provoque une erreur � l'ex�cution.

INSERT INTO sal_emp
    VALUES ('Bill',
    '{10000, 10000, 10000, 10000}',
    '{{"rencontre", "repas"}, {"entrainement", "pr�sentation"}}');

INSERT INTO sal_emp
    VALUES ('Carol',
    '{20000, 25000, 25000, 25000}',
    '{{"petit-d�jeuner", "consulting"}, {"rencontre", "repas"}}');

Une limitation de l'impl�mentation actuelle des tableaux existe, les �l�ments individuels d'un tableau ne peuvent avoir la valeur SQL NULL. Le tableau entier peut �tre NULL mais vous ne pouvez pas avoir des �l�ments NULL dans un tableau avec d'autres �l�ments non NULL. (Ceci est susceptible de changer dans le futur.)

Le r�sultat des deux insertions pr�c�dentes ressemble � ceci :

SELECT * FROM sal_emp;
 nom   |      paye_par_semaine     |      planning
-------+---------------------------+--------------------
Bill  | {10000,10000,10000,10000} | {{rencontre,repas},{entrainement,pr�sentation}}
Carol | {20000,25000,25000,25000} | {{petit-d�jeuner,consulting},{rencontre,repas}}
(2 rows)

La syntaxe du constructeur ARRAY peut aussi �tre utilis�e :

INSERT INTO sal_emp
    VALUES ('Bill',
    ARRAY[10000, 10000, 10000, 10000],
    ARRAY[['rendez-vous', 'repas'], ['entrainement','pr�sentation']]);

INSERT INTO sal_emp
    VALUES ('Carol',
    ARRAY[20000, 25000, 25000, 25000],
    ARRAY[['petit-d�jeuner', 'consulting'], ['rencontre', 'repas']]);

Notez que les �l�ments du tableau sont des constantes SQL ordinaires ou des expressions ; par exemple, les cha�nes de caract�res litt�rales sont encadr�es par des guillemets simples au lieu de guillemets doubles comme cela serait le cas dans un tableau litt�ral. La syntaxe du constructeur ARRAY est discut�e plus en profondeur dans la Section 4.2.10.

8.10.3. Acc�s aux tableaux

Maintenant, nous pouvons lancer quelques requ�tes sur la table. Tout d'abord, montrons comment acc�der � un seul �l�ment du tableau � la fois. Cette requ�te retrouve le nom des employ�s dont la paye a chang� la deuxi�me semaine :

SELECT nom FROM sal_emp WHERE paye_par_semaine[1] <> paye_par_semaine[2];

 nom
-------
 Carol
(1 row)

Les nombres du tableau sont �crit entre crochets. Par d�faut, PostgreSQL utilise la convention des nombres commen�ant � 1 pour les tableaux, c'est-�-dire un tableau � n �l�ments commence avec array[1] et finit avec array[n].

Cette requ�te r�cup�re la paye de la troisi�me semaine pour tous les employ�s :

SELECT paye_par_semaine[3] FROM sal_emp;

 paye_par_semaine
------------------
          10000
          25000
(2 rows)

Nous pouvons aussi acc�der � des parties rectangulaires arbitraires ou � des sous-tableaux. Une partie d'un tableau est not�e par l'�criture extr�mit� basse:extr�mit� haute quelle que soit la dimension des tableaux. Par exemple, cette requ�te retrouve le premier �l�ment du planning de Bill pour les deux premiers jours de la semaine :

SELECT planning[1:2][1:1] FROM sal_emp WHERE nom = 'Bill';

      planning
--------------------
 {{rendez-vous},{entrainement}}
(1 row)

Nous aurions aussi pu �crire

SELECT planning[1:2][1] FROM sal_emp WHERE nom = 'Bill';

en ayant le m�me r�sultat. Une op�ration d'indi�age de rang�e est toujours prise pour repr�senter une tranche de rang�e si un indice quelconque est �crit sous la forme inf�rieur:sup�rieur. Une limite basse de 1 est suppos�e pour toute tranche dont seule une valeur est sp�cifi�e. Voici un autre exemple :

SELECT planning[1:2][2] FROM sal_emp WHERE nom = 'Bill';

         planning
---------------------------
 {{rendez-vous,repas},{entrainement,pr�sentation}}
(1 row)

R�cup�rer en dehors des limites actuelles d'un tableau am�ne une valeur SQL NULL, pas une erreur. Par exemple, si planning a les dimensions [1:3][1:2], alors r�f�rencer planning[3][3] a un r�sultat NULL. De la m�me fa�on, une r�f�rence sur un tableau avec le mauvais nombre d'indices am�ne une valeur NULL plut�t qu'une erreur. R�cup�rer une partie d'un tableau compl�tement en dehors des limites actuelles renvoie un tableau NULL ; mais si la partie demand�e est partiellement int�gr�e aux limites du tableau, alors il est silencieusement r�duit � la r�gion d'intersection.

Les dimensions actuelles de toute valeur d'un tableau sont disponibles avec la fonction array_dims :

SELECT array_dims(planning) FROM sal_emp WHERE nom = 'Carol';

 array_dims
------------
 [1:2][1:2]
(1 row)

array_dims donne un r�sultat de type text, ce qui est pratique � lire mais peut-�tre moins simple � interpr�ter pour les programmes. Les dimensions sont aussi r�cup�rables avec array_upper et array_lower, qui renvoient respectivement la limite haute et basse d'un tableau sp�cifi�.

SELECT array_upper(planning, 1) FROM sal_emp WHERE nom = 'Carol';

 array_upper
-------------
           2
(1 row)

8.10.4. Modification de tableaux

La valeur d'un tableau peut �tre compl�tement remplac�e :

UPDATE sal_emp SET paye_par_semaine = '{25000,25000,27000,27000}'
    WHERE nom = 'Carol';

ou en utilisant la syntaxe de l'expression ARRAY :

UPDATE sal_emp SET paye_par_semaine = ARRAY[25000,25000,27000,27000]
    WHERE nom = 'Carol';

On peut aussi mettre � jour un seul �l�ment d'un tableau :

UPDATE sal_emp SET paye_par_semaine[4] = 15000
    WHERE nom = 'Bill';

ou faire une mise � jour par tranche :

UPDATE sal_emp SET paye_par_semaine[1:2] = '{27000,27000}'
    WHERE nom = 'Carol';

Une valeur de tableau enregistr�e peut �tre agrandie pour affecter un �l�ment adjacent � ceux d�j� pr�sents ou en affectant � une partie adjacente une partie des donn�es d�j� pr�sentes. Par exemple, si le tableau mon_tableau a pour le moment quatre �l�ments, il en aura cinq apr�s une mise � jour qui a affect� mon_tableau[5]. Actuellement, l'agrandissement de cette fa�on est seulement autoris� pour les tableaux � une dimension, et non pas pour les tableaux multidimensionnels.

L'affectation de parties d'un tableau permet la cr�ation de tableaux dont l'indice de d�part n'est pas 1. Par exemple, vous pourriez affecter mon_tableau[-2:7] pour cr�er un tableau avec les valeurs d'indices allant de -2 � 7.

Les valeurs de nouveaux tableaux peuvent aussi �tre construites en utilisant l'op�rateur de concat�nation, ||.

SELECT ARRAY[1,2] || ARRAY[3,4];
   ?column?
---------------
 {1,2,3,4}
(1 row)

SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
      ?column?
---------------------
 {{5,6},{1,2},{3,4}}
(1 row)

L'op�rateur de concat�nation autorise un �l�ment � �tre plac� au d�but ou � la fin d'un tableau � une dimension. Il accepte aussi deux tableaux � N dimensions, ou un tableau � N dimensions et un � N+1 dimensions.

Lorsqu'un �l�ment seul est plac� au d�but d'un tableau � une dimension, le r�sultat est un tableau disposant d'une limite inf�rieure �gale � la limite inf�rieure de l'op�rande du c�t� droit moins un. Lorsqu'un �l�ment est plac� � la fin d'un tableau � une dimension, le r�sultat est un tableau contenant la limite inf�rieure de l'op�rande gauche. Par exemple :

SELECT array_dims(1 || ARRAY[2,3]);
 array_dims
------------
 [0:2]
(1 row)

SELECT array_dims(ARRAY[1,2] || 3);
 array_dims
------------
 [1:3]
(1 row)

Lorsque deux tableaux ayant un m�me nombre de dimensions sont concat�n�s, le r�sultat conserve la limite inf�rieure de l'op�rande gauche. Le r�sultat est un tableau comprenant chaque �l�ment de l'op�rande gauche suivi de chaque �l�ment de l'op�rande droit. Par exemple :

SELECT array_dims(ARRAY[1,2] || ARRAY[3,4,5]);
 array_dims
------------
 [1:5]
(1 row)

SELECT array_dims(ARRAY[[1,2],[3,4]] || ARRAY[[5,6],[7,8],[9,0]]);
 array_dims
------------
 [1:5][1:2]
(1 row)

Lorsqu'un tableau � N dimensions est plac� au d�but ou � la fin d'un tableau � N+1 dimensions, le r�sultat est analogue au cas ci-dessus. Chaque sous-tableau de dimension N est en quelque sorte un �l�ment de la dimension externe d'un tableau � N+1 dimensions. Par exemple :

SELECT array_dims(ARRAY[1,2] || ARRAY[[3,4],[5,6]]);
 array_dims
------------
 [0:2][1:2]
(1 row)

Un tableau peut aussi �tre construit en utilisant les fonctions array_prepend, array_append ou array_cat. Les deux premi�res supportent seulement les tableaux � une dimension alors que array_cat supporte les tableaux multidimensionnels. Notez que l'op�rateur de concat�nation vu ci-dessus est pr�f�r� � l'utilisation directe de ces fonctions. En fait, les fonctions sont utilis�es principalement pour l'impl�mentation de l'op�rateur de concat�nation. N�anmoins, elles pourraient �tre directement utiles dans la cr�ation d'agr�gats d�finis par l'utilisateur. Quelques exemples :

SELECT array_prepend(1, ARRAY[2,3]);
 array_prepend
---------------
 {1,2,3}
(1 row)

SELECT array_append(ARRAY[1,2], 3);
 array_append
--------------
 {1,2,3}
(1 row)

SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
   array_cat
---------------
 {1,2,3,4}
(1 row)

SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
      array_cat
---------------------
 {{1,2},{3,4},{5,6}}
(1 row)

SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
      array_cat
---------------------
 {{5,6},{1,2},{3,4}}

8.10.5. Recherche dans des tableaux

Pour rechercher une valeur dans un tableau, vous devez v�rifier chaque valeur dans le tableau. Ceci peut se faire � la main si vous connaissez la taille du tableau. Par exemple :

SELECT * FROM sal_emp WHERE paye_par_semaine[1] = 10000 OR
                            paye_par_semaine[2] = 10000 OR
                            paye_par_semaine[3] = 10000 OR
                            paye_par_semaine[4] = 10000;

N�anmoins, ceci devient rapidement fastidieux pour les gros tableaux et n'est pas tr�s utile si la taille du tableau n'est pas connue. Une autre m�thode est d�crite dans Section 9.17. La requ�te ci-dessus est rempla�able par :

SELECT * FROM sal_emp WHERE 10000 = ANY (paye_par_semaine);

De plus, vous pouvez trouvez les lignes o� le tableau n'a que des valeurs �gales � 10000 avec :

SELECT * FROM sal_emp WHERE 10000 = ALL (paye_par_semaine);

Astuce�: Les tableaux ne sont pas toujours initialis�s ; rechercher des �l�ments sp�cifiques d'un tableau pourrait �tre un signe d'une mauvaise conception de la base de donn�es. Utilisez plut�t une table s�par�e avec une ligne pour chaque �l�ment faisant parti du tableau. Cela sera plus simple pour une recherche et fonctionnera mieux dans le cas d'un grand nombre d'�l�ments.

8.10.6. Syntaxe d'entr�e et de sortie des tableaux

La repr�sentation externe du type texte d'une valeur d'un tableau consiste en des �l�ments interpr�t�s suivant les r�gles de conversion d'entr�es/sorties pour le type de l'�l�ment du tableau, plus des d�corations indiquant la structure du tableau. L'affichage consiste en des accolades ({ et }) autour des valeurs du tableau et des caract�res de d�limitation entre �l�ments adjacents. Le caract�re d�limiteur est habituellement une virgule (,) mais peut �tre autre chose : il est d�termin� par le param�tre typdelim du type de l'�l�ment tableau (parmi les types de donn�es standards support�s par l'impl�mentation de PostgreSQL, le type box utilise un point-virgule (;) mais tous les autres utilisent la virgule). Dans un tableau multidimensionnel, chaque dimension (row, plane, cube, etc.) utilise son propre niveau d'accolades et les d�limiteurs doivent �tre utilis�s entre des entit�s adjacentes au sein d'accolades de m�me niveau.

La routine de sortie du tableau placera des guillemets doubles autour des valeurs des �l�ments si elles sont des cha�nes vides ou contiennent des accolades, des caract�res d�limiteurs, des guillemets doubles, des antislash ou des espaces. Les guillemets doubles et les antislash int�gr�s aux valeurs des �l�ments seront �chapp�s avec un antislash. Pour les types de donn�es num�riques, on peut supposer sans risque que les doubles guillemets n'appara�tront jamais, mais pour les types de donn�es texte, vous devez vous pr�parer � g�rer la pr�sence et l'absence de guillemets. (Ceci est un changement du comportement � partir de la version pr�-7.2 de PostgreSQL.)

Par d�faut, la valeur de la limite basse d'un tableau est initialis�e � 1. Si une des dimensions du tableau a une limite basse diff�rente de 1, un affichage suppl�mentaire indiquant les dimensions r�elles du tableau pr�c�de l'affichage de la structure du tableau. Cet affichage consiste en des crochets ([]) autour de chaque limite basse et haute d'une dimension avec un d�limiteur deux-points (:) entre chaque. L'affichage des dimensions du tableau est suivie par un signe d'�galit� (=). Par exemple :

SELECT 1 || ARRAY[2,3] AS array;

    array
---------------
[0:2]={1,2,3}
(1 row)

SELECT ARRAY[1,2] || ARRAY[[3,4]] AS array;

          array
--------------------------
[0:1][1:2]={{1,2},{3,4}}
(1 row)

Cette syntaxe peut aussi �tre utilis�e pour sp�cifier des indices de tableau diff�rents des indices par d�faut. Par exemple :

SELECT f1[1][-2][3] AS e1, f1[1][-1][5] AS e2
FROM (SELECT '[1:1][-2:-1][3:5]={{{1,2,3},{4,5,6}}}'::int[] AS f1) AS ss;

 e1 | e2
----+----
  1 |  6
(1 row)

Comme indiqu� pr�c�demment, lors de l'�criture d'une valeur de tableau, vous pourriez �crire des guillemets doubles autour de chaque �l�ment individuel de tableau. Vous devez le faire si leur absence autour d'un �l�ment induit en erreur l'analyseur de la valeur du tableau. Par exemple, les �l�ments contenant des crochets, virgules (ou un caract�re d�limiteur), guillemets doubles, antislashs ou espace (en d�but comme en fin) doivent avoir des guillemets doubles. Pour placer un guillemet double ou un antislash dans une valeur d'�l�ment d'un tableau, faites-le pr�c�der d'un antislash. Autrement, vous pouvez �chapper tous les caract�res de donn�es qui sont utilis�s dans la syntaxe du tableau.

Vous pouvez ajouter des espaces avant un crochet gauche ou apr�s un crochet droit. Vous pouvez aussi ajouter des espaces avant tout �l�ment individuel. Dans tous les cas, les espaces seront ignor�s. Par contre, les espaces � l'int�rieur des �l�ments entre guillemets doubles ou entour�s par des caract�res autres que des espaces ne sont pas ignor�s.

Note�: Rappelez-vous que ce que vous �crivez comme commande SQL sera tout d'abord interpr�t� en tant que cha�ne litt�rale puis en tant que tableau. Ceci double le nombre d'antislash dont vous aurez besoin. Par exemple, pour ins�rer une valeur de tableau de type text contenant un antislash et un guillemet double, vous aurez besoin d'�crire

INSERT ... VALUES ('{"\\\\","\\""}');

Le processeur de la cha�ne litt�rale supprime un niveau d'antislash, donc ce qui arrive � l'analyseur de tableau ressemble � {"\\","\""}. � la place, les cha�nes remplissant l'entr�e du type de donn�es text deviennent respectivement \ et ". (Si nous travaillions avec un type de donn�es dont la routine d'entr�e traitait aussi les antislash de mani�re sp�ciale, bytea par exemple, nous pourrions avoir besoin d'au plus huit antislash dans la commande pour en obtenir un dans l'�l�ment stock�.) Les guillemets dollar (voir Section 4.1.2.2) pourraient �tre utilis�s pour �viter le besoin des doubles antislashs.

Astuce�: La syntaxe du constructeur ARRAY (voir Section 4.2.10) est souvent plus facile � utiliser que la syntaxe du tableau litt�ral lors de l'�criture de valeurs du tableau en commandes SQL. Avec ARRAY, les valeurs de l'�l�ment individuel sont �crites de la m�me fa�on qu'elles auraient �t� �crites si elles n'avaient pas fait partie d'un tableau.