forked from gleu/pgdocs_fr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ddl.xml
2996 lines (2630 loc) · 114 KB
/
ddl.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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="ISO-8859-15"?>
<!-- Dernière modification
le $Date$
par $Author$
révision $Revision$ -->
<chapter id="ddl">
<title>Définition des données</title>
<para>
Ce chapitre couvre la création des structures de données amenées à contenir
les données. Dans une base relationnelle, les données brutes
sont stockées dans des tables. De ce fait, une grande partie de ce chapitre
est consacrée à l'explication de la création et de la modification des tables
et aux fonctionnalités disponibles pour contrôler les données stockées dans les tables.
L'organisation des tables dans des schémas et l'attribution de privilèges
sur les tables sont ensuite décrits. Pour finir, d'autres fonctionnalités,
telles que l'héritage, les vues, les fonctions et les déclencheurs sont
passées en revue.
</para>
<sect1 id="ddl-basics">
<title>Notions fondamentales sur les tables</title>
<indexterm zone="ddl-basics">
<primary>table</primary>
</indexterm>
<indexterm>
<primary>ligne</primary>
</indexterm>
<indexterm>
<primary>colonne</primary>
</indexterm>
<para>
Une table dans une base relationnelle ressemble beaucoup à un tableau
sur papier : elle est constituée de lignes et de colonnes. Le nombre
et l'ordre des colonnes sont fixes et chaque colonne a un nom. Le
nombre de lignes est variable — il représente le nombre de données
stockées à un instant donné.
</para>
<para>
Le SQL n'apporte aucune garantie sur l'ordre des
lignes dans une table. Quand une table est lue, les lignes
apparaissent dans un ordre aléatoire, sauf si un tri est demandé
explicitement. Tout cela est expliqué dans le <xref linkend="queries"/>.
</para>
<para>
De plus, le SQL n'attribue pas d'identifiant unique aux lignes. Il est
donc possible d'avoir plusieurs lignes identiques au sein d'une table.
C'est une conséquence du modèle mathématique sur lequel repose le SQL,
même si cela n'est habituellement pas souhaitable.
Il est expliqué plus bas dans ce chapitre comment traiter ce problème.
</para>
<para>
Chaque colonne a un type de données. Ce type limite l'ensemble
de valeurs qu'il est possible d'attribuer à une colonne. Il attribue
également une sémantique aux données stockées dans la colonne pour
permettre les calculs sur celles-ci. Par exemple, une colonne déclarée dans un
type numérique n'accepte pas les chaînes textuelles ; les données
stockées dans une telle colonne peuvent être utilisées dans des
calculs mathématiques.
Par opposition, une colonne déclarée de type chaîne de
caractères accepte pratiquement n'importe quel type de donnée mais ne
se prête pas aux calculs mathématiques. D'autres types d'opérations,
telle la concaténation de chaînes, sont cependant disponibles.
</para>
<para>
<productname>PostgreSQL</productname> inclut un ensemble conséquent de types
de données intégrés pour s'adapter à diverses applications. Les
utilisateurs peuvent aussi définir leurs propres types de données.
</para>
<para>
La plupart des types de données intégrés ont des noms et des sémantiques
évidents. C'est pourquoi leur explication détaillée est reportée au
<xref linkend="datatype"/>.
</para>
<!-- fractional : fraction -->
<para>
Parmi les types les plus utilisés, on trouve
<type>integer</type> pour les entiers, <type>numeric</type> pour
les éventuelles fractions, <type>text</type> pour les chaînes de
caractères, <type>date</type> pour les dates, <type>time</type> pour
les heures et <type>timestamp</type> pour les valeurs
qui contiennent à la fois une date et une heure.
</para>
<indexterm>
<primary>table</primary>
<secondary>création</secondary>
</indexterm>
<!-- identifier : identifiant ou indicateur. Voire identificateur -->
<para>
Pour créer une table, on utilise la commande bien nommée
<xref linkend="sql-createtable" endterm="sql-createtable-title"/>. Dans cette
commande, il est nécessaire d'indiquer, au minimum, le nom de la
table, les noms des colonnes et le type de données de chacune d'elles.
Par exemple :
<programlisting>CREATE TABLE ma_premiere_table (
premiere_colonne text,
deuxieme_colonne integer
);</programlisting>
Cela crée une table nommée <literal>ma_premiere_table</literal> avec
deux colonnes. La première colonne, nommée
<literal>premiere_colonne</literal>, est de type <type>text</type> ;
la seconde colonne, nommée <literal>deuxieme_colonne</literal>, est de type
<type>integer</type>.
Les noms des table et colonnes se conforment à la syntaxe des identifiants expliquée
dans la <xref linkend="sql-syntax-identifiers"/>. Les noms des types sont souvent
aussi des identifiants mais il existe des exceptions. Le séparateur de la
liste des colonnes est la virgule. La liste doit être entre parenthèses.
</para>
<!-- capillo-tracté s'il s'agit donc de cheveux de traie ou simplifié à l'extrême -->
<para>
L'exemple qui précède est à l'évidence extrêmement simpliste. On donne
habituellement aux tables et aux colonnes des noms qui indiquent les
données stockées. L'exemple ci-dessous est un peu plus réaliste :
<programlisting>CREATE TABLE produits (
no_produit integer,
nom text,
prix numeric
);</programlisting>
(Le type <type>numeric</type> peut stocker des fractions
telles que les montants.)
</para>
<tip>
<para>
Quand de nombreuses tables liées sont créées, il est préférable de définir
un motif cohérent pour le nommage des tables et des colonnes. On a ainsi
la possibilité d'utiliser le pluriel ou le singulier des noms, chacune
ayant ses fidèles et ses détracteurs.
</para>
</tip>
<!-- Par contre, utilisé par Céline (pas Dion, mais Louis-Ferdinand) dans un
souci de provocation, n'est à l'évidence pas une construction grammaticale
préconisée par l'Académie Française. On lui préférera "En revanche",
"Cependant", "Au contraire"... -->
<para>
Le nombre de colonnes d'un table est limité. En fonction du type de
colonnes, il oscille entre 250 et 1600.
Définir une table avec un nombre de colonnes proche de cette limite est,
cependant, très inhabituel et doit conduire à se poser des questions quant
à la conception du modèle.
</para>
<indexterm>
<primary>table</primary>
<secondary>suppression</secondary>
</indexterm>
<para>
Lorsqu'une table n'est plus utile, elle peut être supprimée à l'aide de la
commande <xref linkend="sql-droptable" endterm="sql-droptable-title"/>. Par exemple :
<programlisting>DROP TABLE ma_premiere_table;
DROP TABLE produits;</programlisting>
Tenter de supprimer une table qui n'existe pas lève une erreur.
Il est, néanmoins, habituel dans les fichiers de scripts SQL d'essayer
de supprimer chaque table avant de la créer. Les messages
d'erreur sont alors ignorés afin que le script fonctionne que la table
existe ou non. (La variante <literal>DROP TABLE IF EXISTS</literal> peut
aussi être utilisée pour éviter les messages d'erreur mais elle ne fait pas partie du
standard SQL.)
</para>
<para>
Pour la procéduure de modification d'une table qui existe déjà, voir la
<xref linkend="ddl-alter"/> plus loin dans ce chapitre.
</para>
<para>
Les outils précédemment décrits permettent de créer des tables
fonctionnelles. Le reste de ce chapitre est consacré à l'ajout de fonctionnalités
à la définition de tables pour garantir l'intégrité des données, la sécurité
ou l'ergonomie. Le lecteur impatient d'insérer des données dans ses tables
peut sauter au <xref linkend="dml"/> et lire le reste de
ce chapitre plus tard.
</para>
</sect1>
<sect1 id="ddl-default">
<title>Valeurs par défaut</title>
<indexterm zone="ddl-default">
<primary>valeur par défaut</primary>
</indexterm>
<para>
Une valeur par défaut peut être attribuée à une colonne. Quand une nouvelle
ligne est créée et qu'aucune valeur n'est indiquée pour certaines de ses
colonnes, celles-ci sont remplies avec leurs valeurs par défaut respectives.
Une commande de manipulation de données peut aussi demander explicitement
que la valeur d'une colonne soit positionnée à la valeur par défaut, sans
qu'il lui soit nécessaire de connaître cette valeur (les détails concernant les
commandes de manipulation de données sont donnés dans le <xref linkend="dml"/>).
</para>
<para>
<indexterm>
<primary>valeur NULL</primary>
<secondary>valeur par défaut</secondary>
</indexterm>
Si aucune valeur par défaut n'est déclarée explicitement, la valeur
par défaut est la valeur NULL. Cela a un sens dans la mesure où l'on
peut considérer que la valeur NULL représente des données inconnues.
</para>
<para>
Dans la définition d'une table, les valeurs par défaut sont listées après
le type de données de la colonne. Par exemple:
<programlisting>CREATE TABLE produits (
no_produit integer,
nom text,
prix numeric <emphasis>DEFAULT 9.99</emphasis>
);</programlisting>
</para>
<para>
La valeur par défaut peut être une expression, alors évaluée à l'insertion
de cette valeur (<emphasis>pas</emphasis> à la création de la
table). Un exemple commun est la colonne de type <type>timestamp</type>
dont la valeur par défaut est <literal>now()</literal>. Elle se voit ainsi
attribuée l'heure d'insertion. Un autre exemple est
la génération d'un <quote>numéro de série</quote> pour chaque ligne.
Dans <productname>PostgreSQL</productname>, cela s'obtient habituellement par
quelque chose comme
<programlisting>CREATE TABLE produits (
no_produit integer <emphasis>DEFAULT nextval('produits_no_produit_seq')</emphasis>,
...
);</programlisting>
où la fonction <literal>nextval()</literal> fournit des valeurs successives à
partir d'un <firstterm>objet séquence</firstterm> (voir la <xref
linkend="functions-sequence"/>). Cet arrangement est suffisamment commun
pour qu'il ait son propre raccourci :
<programlisting>CREATE TABLE produits (
no_produit <emphasis>SERIAL</emphasis>,
...
);</programlisting>
Le raccourci <literal>SERIAL</literal> est discuté plus tard dans la <xref
linkend="datatype-serial"/>.
</para>
</sect1>
<sect1 id="ddl-constraints">
<title>Contraintes</title>
<indexterm zone="ddl-constraints">
<primary>contrainte</primary>
</indexterm>
<para>
Les types de données sont un moyen de restreindre la nature des données qui
peuvent être stockées dans une table. Pour beaucoup d'applications,
toutefois, la contrainte fournie par ce biais est trop grossière.
Par exemple, une colonne qui
contient le prix d'un produit ne doit accepter que des valeurs
positives. Mais il n'existe pas de type de données standard qui n'accepte que
des valeurs positives. Un autre problème peut provenir de la volonté de
contraindre les données d'une colonne par rapport aux autres colonnes ou lignes.
Par exemple, dans une table contenant des informations de produit, il
ne peut y avoir qu'une ligne par numéro de produit.
</para>
<para>
Pour cela, SQL permet de définir des contraintes sur les colonnes
et les tables. Les contraintes donnent autant de contrôle sur les
données des tables qu'un utilisateur peut le souhaiter. Si un utilisateur
tente de stocker des données dans une colonne en violation d'une contrainte, une erreur est
levée. Cela s'applique même si la valeur vient de la définition de la
valeur par défaut.
</para>
<sect2>
<title>Contraintes de vérification</title>
<indexterm>
<primary>contrainte de vérification</primary>
</indexterm>
<indexterm>
<primary>contrainte</primary>
<secondary>vérification</secondary>
</indexterm>
<para>
La contrainte de vérification est la contrainte la plus
générique qui soit. Elle permet d'indiquer que la valeur
d'une colonne particulière doit satisfaire une expression booléenne
(valeur de vérité). Par exemple, pour obliger les prix des produits
à être positifs, on peut utiliser :
<programlisting>CREATE TABLE produits (
no_produit integer,
nom text,
prix numeric <emphasis>CHECK (prix > 0)</emphasis>
);</programlisting>
</para>
<para>
La définition de contrainte vient après
le type de données, comme pour les définitions de valeur par défaut. Les
valeurs par défaut et les contraintes peuvent être données dans
n'importe quel ordre. Une contrainte de vérification s'utilise avec
le mot clé <literal>CHECK</literal> suivi d'une expression entre
parenthèses. L'expression de la contrainte implique habituellement la
colonne à laquelle elle s'applique, la contrainte n'ayant dans le cas
contraire que peu de sens.
</para>
<indexterm>
<primary>contrainte</primary>
<secondary>nom</secondary>
</indexterm>
<para>
la contrainte peut prendre un nom distinct. Cela
clarifie les messages d'erreur et permet de faire référence
à la contrainte lorsqu'elle doit être modifiée.
La syntaxe est :
<programlisting>CREATE TABLE produits (
no_produit integer,
nom text,
prix numeric <emphasis>CONSTRAINT prix_positif</emphasis> CHECK (prix > 0)
);</programlisting>
Pour indiquer une contrainte nommée, on utilise le mot-clé
<literal>CONSTRAINT</literal> suivi d'un identifiant et de la
définition de la contrainte (si aucun nom n'est précisé,
le système en choisit un).
</para>
<para>
Une contrainte de vérification peut aussi faire référence à plusieurs
colonnes. Dans le cas d'un produit, on peut vouloir stocker le prix normal
et un prix réduit en s'assurant que le prix réduit soit bien inférieur au
prix normal.
<programlisting>CREATE TABLE produits (
no_produit integer,
nom text,
prix numeric CHECK (prix > 0),
prix_promotion numeric CHECK (prix_promotion > 0),
<emphasis>CHECK (prix > prix_promotion)</emphasis>
);</programlisting>
</para>
<para>
Si les deux premières contraintes n'offrent pas de nouveauté, la troisième
utilise une nouvelle syntaxe. Elle n'est pas attachée à une colonne
particulière mais apparaît comme un élément distinct dans
la liste des colonnes. Les définitions de
colonnes et ces définitions de contraintes peuvent être définies dans
un ordre quelconque.
</para>
<para>
Les deux premières contraintes sont appelées contraintes de
colonne tandis que la troisième est appelée contrainte de table parce
qu'elle est écrite séparément d'une définition de colonne particulière.
Les contraintes de colonne peuvent être écrites comme des contraintes de
table, mais l'inverse n'est pas forcément possible puisqu'une contrainte de colonne est
supposée ne faire référence qu'à la colonne à laquelle elle est
attachée (<productname>PostgreSQL</productname> ne vérifie pas cette règle
mais il est préférable de la suivre pour s'assurer que les définitions de
tables fonctionnent avec d'autres systèmes de bases de données).
L'exemple ci-dessus peut aussi s'écrire :
<programlisting>CREATE TABLE produits (
no_produit integer,
nom text,
prix numeric,
CHECK (prix > 0),
prix_promotion numeric,
CHECK (prix_promotion > 0),
CHECK (prix > prix_promotion)
);</programlisting>
ou même :
<programlisting>CREATE TABLE produits (
no_produit integer,
nom text,
prix numeric CHECK (prix > 0),
prix_promotion numeric,
CHECK (prix_promotion > 0 AND prix > prix_promotion)
);</programlisting>
C'est une question de goût.
</para>
<para>
Les contraintes de table peuvent être nommées, tout comme
les contraintes de colonne :
<programlisting>CREATE TABLE produits (
no_produit integer,
nom text,
prix numeric,
CHECK (prix > 0),
prix_promotion numeric,
CHECK (prix_promotion > 0),
<emphasis>CONSTRAINT promo_valide</emphasis> CHECK (prix > prix_promotion)
);</programlisting>
</para>
<indexterm>
<primary>valeur NULL</primary>
<secondary sortas="check constraints">avec contraintes de vérification</secondary>
</indexterm>
<para>
Une contrainte de vérification est satisfaite si
l'expression est évaluée vraie ou NULL. Puisque la
plupart des expressions sont évaluées NULL si l'une
des opérandes est nulle, elles n'interdisent pas les valeurs NULL
dans les colonnes contraintes. Pour s'assurer qu'une colonne ne
contient pas de valeurs NULL, la contrainte NOT NULL décrite
dans la section suivante peut être utilisée.
</para>
</sect2>
<sect2>
<title>Contraintes de non nullité (NOT NULL)</title>
<indexterm>
<primary>contrainte NOT NULL</primary>
</indexterm>
<indexterm>
<primary>contrainte</primary>
<secondary>NOT NULL</secondary>
</indexterm>
<para>
Une contrainte NOT NULL indique simplement qu'une colonne ne peut
pas prendre la valeur NULL. Par exemple :
<programlisting>CREATE TABLE produits (
no_produit integer <emphasis>NOT NULL</emphasis>,
nom text <emphasis>NOT NULL</emphasis>,
prix numeric
);</programlisting>
</para>
<para>
Une contrainte NOT NULL est toujours écrite comme une contrainte de
colonne. Elle est fonctionnellement équivalente à la création d'une
contrainte de vérification <literal>CHECK (<replaceable>nom_colonne</replaceable>
IS NOT NULL)</literal>. Toutefois, dans <productname>PostgreSQL</productname>,
il est plus efficace de créer explicitement une contrainte NOT NULL.
L'inconvénient est que les contraintes de non-nullité ainsi créées ne
peuvent pas être explicitement nommées.
</para>
<para>
Une colonne peut évidemment avoir plusieurs contraintes. Il suffit
d'écrire les contraintes les unes après les autres :
<programlisting>CREATE TABLE produits (
no_produit integer NOT NULL,
nom text NOT NULL,
prix numeric NOT NULL CHECK (prix > 0)
);</programlisting>
L'ordre n'a aucune importance. Il ne détermine pas l'ordre de vérification
des contraintes.
</para>
<para>
La contrainte <literal>NOT NULL</literal> a un contraire ; la contrainte
<literal>NULL</literal>. Elle ne signifie pas que la colonne doit
être NULL, ce qui est assurément inutile, mais sélectionne le comportement
par défaut, à savoir que la colonne peut être NULL. La contrainte
<literal>NULL</literal> n'est pas présente dans le standard SQL et ne doit pas
être utilisée dans des applications portables (elle n'a été ajoutée
dans <productname>PostgreSQL</productname> que pour assurer la
compatibilité avec d'autres bases de données). Certains utilisateurs
l'apprécient néanmoins car elle permet de basculer aisément d'une
contrainte à l'autre dans un fichier de script. On peut, par exemple, commencer avec :
<programlisting>CREATE TABLE produits (
no_produit integer NULL,
nom text NULL,
prix numeric NULL
);</programlisting>
puis insérer le mot-clé <literal>NOT</literal> en fonction des besoins.
</para>
<tip>
<para>
Dans la plupart des bases de données, il est préférable que la majorité des
colonnes soient marquées NOT NULL.
</para>
</tip>
</sect2>
<sect2>
<title>Contraintes d'unicité</title>
<indexterm>
<primary>contrainte d'unicité</primary>
</indexterm>
<indexterm>
<primary>contrainte</primary>
<secondary>unicité</secondary>
</indexterm>
<para>
Les contraintes d'unicité garantissent l'unicité des données contenues dans
une colonne ou un groupe de colonnes par rapport à toutes
les lignes de la table. La syntaxe est :
<programlisting>CREATE TABLE produits (
no_produit integer <emphasis>UNIQUE</emphasis>,
nom text,
prix numeric
);</programlisting>
lorsque la contrainte est écrite comme contrainte de colonne et :
<programlisting>CREATE TABLE produits (
no_produit integer,
nom text,
prix numeric,
<emphasis>UNIQUE (no_produit)</emphasis>
);</programlisting>
lorsqu'elle est écrite comme contrainte de table.
</para>
<para>
Lorsqu'une contrainte d'unicité fait référence à un groupe de colonnes,
celles-ci sont listées séparées par des virgules :
<programlisting>CREATE TABLE exemple (
a integer,
b integer,
c integer,
<emphasis>UNIQUE (a, c)</emphasis>
);</programlisting>
Cela précise que la combinaison de valeurs dans les colonnes indiquées
est unique sur toute la table. Sur une colonne prise isolément ce
n'est pas nécessairement le cas (et habituellement cela ne l'est pas).
</para>
<para>
Une contrainte d'unicité peut être nommée, de la
façon habituelle :
<programlisting>CREATE TABLE produits (
no_produit integer <emphasis>CONSTRAINT doit_etre_different</emphasis> UNIQUE,
nom text,
prix numeric
);</programlisting>
</para>
<indexterm>
<primary>valeur NULL</primary>
<secondary sortas="unique constraints">avec contrainte d'unicité</secondary>
</indexterm>
<para>
En général, une contrainte d'unicité est violée lorsqu'au moins deux
lignes de la table possèdent des valeurs identiques sur toutes les
colonnes de la contrainte. En revanche, deux valeurs NULL ne sont pas
considérées égales. Cela signifie qu'il est possible
de stocker des lignes dupliquées contenant une valeur NULL
dans au moins une des colonnes contraintes. Ce comportement est conforme
au standard SQL, mais d'autres bases SQL n'appliquent pas cette règle.
Il est donc préférable d'être prudent lors du développement d'applications
portables.
</para>
</sect2>
<sect2>
<title>Clés primaires</title>
<indexterm>
<primary>clé primaire</primary>
</indexterm>
<indexterm>
<primary>contrainte</primary>
<secondary>clé primaire</secondary>
</indexterm>
<para>
Techniquement, une contrainte de clé primaire n'est
que la combinaison d'une contrainte d'unicité et
d'une contrainte NOT NULL. Les définitions de
table suivantes acceptent de ce fait les mêmes données :
<programlisting>CREATE TABLE produits (
no_produit integer UNIQUE NOT NULL,
nom text,
prix numeric
);</programlisting>
<programlisting>CREATE TABLE produits (
no_produit integer <emphasis>PRIMARY KEY</emphasis>,
nom text,
prix numeric
);</programlisting>
</para>
<para>
Les clés primaires peuvent également contraindre plusieurs colonnes ; la
syntaxe est semblable aux contraintes d'unicité :
<programlisting>CREATE TABLE exemple (
a integer,
b integer,
c integer,
<emphasis>PRIMARY KEY (a, c)</emphasis>
);</programlisting>
</para>
<para>
Une clé primaire indique qu'une colonne ou un groupe de colonnes peut
être utilisé(e) comme identifiant unique des lignes de la table. (C'est
une conséquence directe de la définition d'une clé primaire. Une
contrainte d'unicité ne suffit pas à fournir un identifiant unique
car elle n'exclut pas les valeurs NULL). Ceci est utile à la fois
pour des raisons documentaires et pour les applications clientes. Par
exemple, une application graphique qui permet de modifier les valeurs de
lignes a probablement besoin de connaître la clé primaire d'une table pour
pouvoir identifier les lignes de manière unique.
</para>
<para>
Une table a, au plus, une clé primaire. (Le nombre de contraintes UNIQUE NOT NULL,
qui assurent la même fonction, n'est pas limité, mais une seule
peut être identifiée comme clé primaire.) La théorie des
bases de données relationnelles impose que chaque table ait
une clé primaire. Cette règle n'est pas forcée par
<productname>PostgreSQL</productname>, mais il est préférable de la
respecter.
</para>
</sect2>
<sect2 id="ddl-constraints-fk">
<title>Clés étrangères</title>
<indexterm>
<primary>clé étrangère</primary>
</indexterm>
<indexterm>
<primary>contrainte</primary>
<secondary>clé étrangère</secondary>
</indexterm>
<indexterm>
<primary>intégrité référentielle</primary>
</indexterm>
<para>
Une contrainte de clé étrangère stipule que les valeurs d'une
colonne (ou d'un groupe de colonnes) doivent correspondre aux valeurs
qui apparaissent dans les lignes d'une autre table.
On dit que cela maintient l'<firstterm>intégrité référentielle</firstterm>
entre les deux tables.
</para>
<para>
Soit la table de produits, déjà utilisée plusieurs fois :
<programlisting>CREATE TABLE produits (
no_produit integer PRIMARY KEY,
nom text,
prix numeric
);</programlisting>
Soit également une table qui stocke les commandes de
ces produits. Il est intéressant de s'assurer que la table des
commandes ne contient que des commandes de produits
qui existent réellement. Pour cela, une contrainte
de clé étrangère est définie dans la table des commandes qui référence la
table produit :
<programlisting>CREATE TABLE commandes (
id_commande integer PRIMARY KEY,
no_produit integer <emphasis>REFERENCES produits (no_produit)</emphasis>,
quantite integer
);</programlisting>
Il est désormais impossible de créer des commandes pour lesquelles
<structfield>no_produit</structfield> n'apparaît pas dans la table
produits.
</para>
<!-- referencing/referenced
référençant/référencée ? -->
<para>
Dans cette situation, on dit que la table des commandes est la table
<firstterm>qui référence</firstterm> et la table des produits est la table
<firstterm>référencée</firstterm>. De la même façon, il y a des colonnes
qui référencent et des colonnes référencées.
</para>
<para>
La commande précédente peut être raccourcie en
<programlisting>CREATE TABLE commandes (
id_commande integer PRIMARY KEY,
no_produit integer <emphasis>REFERENCES produits</emphasis>,
quantite integer
);</programlisting>
parce qu'en l'absence de liste de colonnes, la clé primaire de la
table de référence est utilisée comme colonne de référence.
</para>
<para>
Une clé étrangère peut aussi contraindre et référencer un groupe de colonnes.
Comme cela a déjà été évoqué, il faut alors l'écrire sous forme d'une contrainte de table.
Exemple de syntaxe :
<programlisting>CREATE TABLE t1 (
a integer PRIMARY KEY,
b integer,
c integer,
<emphasis>FOREIGN KEY (b, c) REFERENCES autre_table (c1, c2)</emphasis>
);</programlisting>
Le nombre et le type des colonnes contraintes doivent correspondre
au nombre et au type des colonnes référencées.
</para>
<para>
Une contrainte de clé étrangère peut être nommée de la façon habituelle.
</para>
<para>
Une table peut contenir plusieurs contraintes de clé étrangère. Les
relation n-n entre tables sont implantées ainsi. Soient
des tables qui contiennent des produits et des commandes, avec la
possibilité d'autoriser une commande à contenir plusieurs produits
(ce que la structure ci-dessus ne permet pas). On peut pour cela
utiliser la structure de table suivante :
<programlisting>CREATE TABLE produits (
no_produit integer PRIMARY KEY,
nom text,
prix numeric
);
CREATE TABLE commandes (
id_commande integer PRIMARY KEY,
adresse_de_livraison text,
...
);
CREATE TABLE commande_produits (
no_produit integer REFERENCES produits,
id_commande integer REFERENCES commandes,
quantite integer,
PRIMARY KEY (no_produit, id_commande)
);</programlisting>
La clé primaire de la dernière table recouvre les clés étrangères.
</para>
<indexterm>
<primary>CASCADE</primary>
<secondary>action clé étrangère</secondary>
</indexterm>
<indexterm>
<primary>RESTRICT</primary>
<secondary>action clé étrangère</secondary>
</indexterm>
<para>
Les clés étrangères interdisent désormais la création
de commandes qui ne soient pas liées à un produit. Qu'arrive-t-il si un produit
est supprimé alors qu'une commande y fait référence ? SQL
permet aussi de le gérer. Intuitivement, plusieurs options existent :
<itemizedlist spacing="compact">
<listitem><para>interdire d'effacer un produit référencé ;</para></listitem>
<listitem><para>effacer aussi les commandes ;</para></listitem>
<listitem><para>autre chose ?</para></listitem>
</itemizedlist>
</para>
<para>
Pour illustrer ce cas, la politique suivante est implantée sur
l'exemple de relations n-n évoqué plus haut :
<itemizedlist spacing="compact">
<listitem><para>quand quelqu'un veut retirer un produit qui est encore
référencé par une commande
(au travers de <literal>commande_produits</literal>), on
l'interdit ;</para></listitem>
<listitem><para>si quelqu'un supprime une commande, les éléments
de la commande sont aussi supprimés.</para></listitem>
</itemizedlist>
<programlisting>CREATE TABLE produits (
no_produit integer PRIMARY KEY,
nom text,
prix numeric
);
CREATE TABLE commandes (
id_commande integer PRIMARY KEY,
adresse_de_livraison text,
...
);
CREATE TABLE commande_produits (
no_produit integer REFERENCES produits <emphasis>ON DELETE RESTRICT</emphasis>,
id_commande integer REFERENCES commandes <emphasis>ON DELETE CASCADE</emphasis>,
quantite integer,
PRIMARY KEY (no_produit, id_commande)
);</programlisting>
</para>
<!-- CASCADE : cascader ? -->
<para>
Restreindre les suppressions et les cascader sont les deux
options les plus communes. <literal>RESTRICT</literal> empêche la
suppression d'une ligne référencée. <literal>NO ACTION</literal> impose
la levée d'une erreur si des lignes référençant existent lors de la
vérification de la contrainte. Il s'agit du comportement par
défaut en l'absence de précision. La différence entre
<literal>RESTRICT</literal> et <literal>NO ACTION</literal>
est l'autorisation par
<literal>NO ACTION</literal> du report de la vérification à la fin de la
transaction, ce que <literal>RESTRICT</literal> ne permet pas.
<literal>CASCADE</literal> indique que, lors de la suppression d'une ligne
référencée, les lignes la référençant doivent être automatiquement
supprimées. Il existe deux autres options :
<literal>SET NULL</literal> et <literal>SET DEFAULT</literal>.
Celles-ci imposent que les colonnes qui référencent soient
réinitialisées à NULL ou à leur valeur par défaut, respectivement, lors
de la suppression d'une ligne référencée. Elles ne dispensent pas pour
autant d'observer les contraintes. Par exemple, si une action précise
<literal>SET DEFAULT</literal> mais que la valeur par défaut ne
satisfait pas la clé étrangère, l'opération échoue.
</para>
<para>
À l'instar de <literal>ON DELETE</literal>, existe
<literal>ON UPDATE</literal>, évoqué lorsqu'une colonne référencée
est modifiée (actualisée). Les actions possibles sont les mêmes.
</para>
<para>
Le <xref linkend="dml"/> contient de plus amples informations sur
l'actualisation et la suppression de données.
</para>
<para>
Une clé étrangère peut faire référence à des colonnes qui constituent une clé
primaire ou forment une contrainte d'unicité. Si la clé étrangère référence
une contrainte d'unicité, des possibilités supplémentaires sont offertes
concernant la correspondance des valeurs NULL. Celles-ci sont expliquées
dans la documentation de référence de
<xref linkend="sql-createtable" endterm="sql-createtable-title"/>.
</para>
</sect2>
</sect1>
<sect1 id="ddl-system-columns">
<title>Colonnes système</title>
<para>
Chaque table contient plusieurs <firstterm>colonnes système</firstterm>
implicitement définies par le système. De ce fait, leurs noms ne peuvent
pas être utilisés comme noms de colonnes utilisateur (ces restrictions sont
distinctes de celles sur l'utlisation de mot-clés ; mettre le nom
entre guillemets ne permet pas d'échapper à cette règle). Il n'est pas
vraiment utile de se préoccuper de ces colonnes, mais au minimum de
savoir qu'elles existent.
</para>
<indexterm>
<primary>colonne</primary>
<secondary>colonne système</secondary>
</indexterm>
<variablelist>
<varlistentry>
<term><structfield>oid</structfield></term>
<listitem>
<para>
<indexterm>
<primary>OID</primary>
<secondary>colonne</secondary>
</indexterm>
L'identifiant objet (<foreignphrase>object ID</foreignphrase>) d'une ligne. Cette
colonne n'est présente que si la table a été créée en précisant
<literal>WITH OIDS</literal> ou si la variable de configuration
<xref linkend="guc-default-with-oids"/> était activée à ce moment-là.
Cette colonne est de type oid (même nom que la colonne) ; voir la
<xref linkend="datatype-oid"/> pour obtenir plus d'informations sur ce type.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><structfield>tableoid</structfield></term>
<listitem>
<indexterm>
<primary>tableoid</primary>
</indexterm>
<para>
L' OID de la table contenant la ligne. Cette colonne est
particulièrement utile pour les requêtes qui utilisent des hiérarchies
d'héritage (voir <xref linkend="ddl-inherit"/>). Il est, en effet,
difficile, en son absence, de savoir de quelle table provient une ligne.
<structfield>tableoid</structfield>
peut être joint à la colonne <structfield>oid</structfield> de
<structname>pg_class</structname> pour obtenir le nom de la table.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><structfield>xmin</structfield></term>
<listitem>
<indexterm>
<primary>xmin</primary>
</indexterm>
<para>
L'identifiant (ID de transaction) de la transaction qui a inséré cette
version de la ligne. (Une version de ligne est un état individuel
de la ligne ; toute mise à jour d'une ligne crée une nouvelle
version de ligne pour la même ligne logique.)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><structfield>cmin</structfield></term>
<listitem>
<indexterm>
<primary>cmin</primary>
</indexterm>
<para>
L'identifiant de commande (à partir de zéro) au sein de la transaction
d'insertion.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><structfield>xmax</structfield></term>
<listitem>
<indexterm>
<primary>xmax</primary>
</indexterm>
<para>
L'identifiant (ID de transaction) de la transaction de suppression, ou zéro
pour une version de ligne non effacée. Il est possible que la colonne ne
soit pas nulle pour une version de ligne visible ; cela
indique habituellement que la transaction de suppression n'a pas été
effectuée, ou qu'une tentative de suppression a été annulée.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><structfield>cmax</structfield></term>
<listitem>
<indexterm>
<primary>cmax</primary>
</indexterm>
<para>
L'identifiant de commande au sein de la transaction de suppression, ou
zéro.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><structfield>ctid</structfield></term>
<listitem>
<indexterm>
<primary>ctid</primary>
</indexterm>
<para>
La localisation physique de la version de ligne au sein de sa table.
Bien que le <structfield>ctid</structfield> puisse être utilisé
pour trouver la version de ligne très rapidement, le
<structfield>ctid</structfield> d'une ligne change si
la ligne est actualisée ou déplacée par un <command>VACUUM FULL</command>.
<structfield>ctid</structfield> est donc inutilisable comme
identifiant de ligne sur le long terme. Il est préférable d'utiliser l'OID,
ou, mieux encore, un numéro
de série utilisateur, pour identifier les lignes logiques.
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
Les OID sont des nombres de 32 bits et sont attribués à partir d'un
compteur unique sur le cluster. Dans une base de données volumineuse ou
agée, il est possible que le compteur boucle. Il est de ce fait peu
pertinent de considérer que les OID puissent être uniques ; pour
identifier les lignes d'une table, il est fortement recommandé d'utiliser
un générateur de séquence. Néanmoins, les OID peuvent également être
utilisés sous réserve que quelques précautions soient prises :
<itemizedlist>
<listitem>
<para>
une contrainte d'unicité doit être ajoutée sur la colonne OID de chaque
table dont l'OID est utilisé pour identifier les lignes.
Dans ce cas (ou dans celui d'un index d'unicité), le système
n'engendre pas d'OID qui puisse correspondre à celui d'une ligne
déjà présente. Cela n'est évidemment possible que si la table contient
moins de 2<superscript>32</superscript> (4 milliards) lignes ; en pratique, la
taille de la table a tout intérêt à être bien plus petite que ça, dans
un souci de performance ;
</para>