Si vous avez déjà fait du SQL et que vous mettez les pieds dans une base de données Oracle, vous risquez d’être confronté⋅e rapidement à 3 pièges dans lesquels tombent beaucoup de débutant⋅e⋅s.


Partager l’article Trois pièges de débutant sous Oracle sur les réseaux sociaux


Premier piège : les SELECT sans FROM en erreur

Vous commencez votre premier jour d’utilisation sur Oracle. vous souhaitez juste lancer un test basique, comme par exemple faire un calcul ou tester une fonction de chaîne.

Vous faites alors :

SELECT 2 + 3;

Ou encore :

SELECT UPPER('coucou');

Et à chaque fois, pour ces requêtes très simples, vous obtenez une erreur :

ORA-00923: mot-clé FROM absent à l'emplacement prévu

Notez bien le début du message d’erreur : ils commencent tous ainsi, avec ORA suivi d’un numéro à 5 chiffres pour le code d’erreur.

Le message nous dit que le mot-clé FROM est manquant, pourtant, les requêtes que vous faites semblent correctes.

Cela va vous surprendre, mais sous Oracle, il faut toujours utiliser le mot-clé SELECT avec le mot-clé FROM, sinon, vous obtenez l’erreur mentionnée ci-dessus.

Mais alors, comment faire pour ce genre de requête ? La réponse est l’utilisation d’une table fictive appelée dual. Dès que vous avez des requêtes ne faisant pas intervenir de table, de vue ou de CTE, alors ajoutez systématiquement FROM dual à votre requête.

Nous pouvons donc réécrire les requêtes précédentes ainsi :

SELECT 2 + 3 FROM dual;

Elle donne bien le bon résultat et sans erreur :

2+3|
---+
  5|

Et maintenant avec :

SELECT UPPER('coucou') FROM dual;

Elle fonctionne également :

UPPER('COUCOU')|
---------------+
COUCOU         |

Voyons maintenant un autre cas : une CTE récursive.

WITH cte_exemple(n) AS (
	SELECT 0
	UNION ALL
	SELECT n + 1 FROM cte_exemple WHERE  n < 9
)
SELECT * FROM cte_exemple;

Vous obtiendrez encore l’erreur du mot-clé FROM manquant.

Donc pour lancer une CTE récursive, il faut penser à spécifier le FROM dual !

En voici la version corrigée :

WITH cte_exemple(n) AS (
	SELECT 0 FROM dual
	UNION ALL
	SELECT n + 1 FROM cte_exemple WHERE  n < 9
)
SELECT * FROM cte_exemple;

Et cela donne le résultat escompté :

N|
-+
0|
1|
2|
3|
4|
5|
6|
7|
8|
9|

Donc retenez bien : pas de SELECT sans FROM !

Deuxième piège : CREATE TABLE DEFAULT en erreur

Vous voulez créer une table assez simple, avec certaines colonnes devant avoir une valeur par défaut, et ne devant pas valoir NULL.

Cela pourrait être l’exemple suivant :

CREATE TABLE THING (
	NAME VARCHAR2 ( 100 ) NOT NULL,
	CREATED_AT DATE NOT NULL DEFAULT SYSDATE  
);

Vous exécutez la requête de création de table, sûr⋅e de vous, et là vous obtenez l’étrange erreur suivante :

ORA-00907: parenthèse de droite absente

Pourtant, il ne manque pas de parenthèse. Alors, pourquoi cette erreur est-elle levée ?

L’explication est qu’Oracle est peu tolérant au changement d’ordre dans les déclarations. Pour une syntaxe qui serait valable avec un autre SGBD, on obtient une erreur avec Oracle, mais une erreur pas claire car comme il s’attend à un ordre différent des éléments, il soulève une erreur par rapport à quelque chose qu’il attend et qui n’a pas lieu d’être ici. Pour faire simple, il soulève une erreur innapropriée.

La solution est de faire attention à l’emplacement de la clause DEFAULT. Il faut ici la placer après le nom de la colonne, comme ceci par exemple :

CREATE TABLE THING (
	NAME VARCHAR2 ( 100 ) NOT NULL,
	CREATED_AT DATE DEFAULT SYSDATE NOT NULL  
);

La requête s’exécute sans erreur, la table est bien créée.

Insérons-y une ligne :

INSERT INTO THING (NAME) VALUES ('Foo');

Voyons si la valeur par défaut a bien été prise :

SELECT * FROM THING;

La sélection donne :

NAME|CREATED_AT             |
----+-----------------------+
Foo |2022-04-23 11:41:19.000|

Nous avons bien la valeur par défaut pour la date de création.

Donc retenez bien : attention à l’ordre de déclaration qui est assez rigide chez Oracle, contrairement à d’autres SGBD.

Troisième piège : les clés primaires auto-incrémentées

Oracle ne propose pas une, pas deux, mais trois façons différentes de déclarer une clé primaire auto-incrémentée. Selon ce que vous aurez choisi, vous obtiendrez ou non des erreurs lors des insertions de données.

Voyons cela avec 3 exemples.

Auto-incrémentation basique

Testons directement avec cette déclaration :

CREATE TABLE BAR (
	ID NUMBER GENERATED AS IDENTITY PRIMARY KEY,
	NAME VARCHAR2 ( 100 ) NOT NULL  
);

La table est bien créée. insérons maintenant des données :

INSERT INTO BAR (NAME) VALUES ('Foo');

Fonctionne et donne ceci :

ID|NAME|
--+----+
 1|Foo |

Insérons en spécifiant une valeur pour la clé primaire :

INSERT INTO BAR (ID, NAME) VALUES (2, 'Thing');

Oracle lève l’erreur suivante :

ORA-32795:  impossible d'insérer la valeur dans une colonne d'identité avec
            les mots-clés GENERATED ALWAYS

Voyons avec la valeur NULL pour la clé :

INSERT INTO BAR (ID, NAME) VALUES (NULL, 'Bar');

Oracle lève la même erreur :

ORA-32795:  impossible d'insérer la valeur dans une colonne d'identité avec
            les mots-clés GENERATED ALWAYS

Donc, pour un GENERATED AS IDENTITY il ne faut pas spécifier de valeur pour le champ de la clé primaire lors des insertions, Oracle se charge de tout.

Auto-incrémentation par défaut

Passons à la deuxième façon de délarer une clé primaire avec auto-incrémentation.

CREATE TABLE BAR2 (
	ID NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
	NAME VARCHAR2 ( 100 ) NOT NULL  
);

La table est créée, réalisons maintenant différentes insertions :

INSERT INTO BAR2 (NAME) VALUES ('Foo');

Cette première insertion sans spécifier de valeur pour la colonne auto-incrémenté passe sans problème.

INSERT INTO BAR2 (ID, NAME) VALUES (2, 'Thing');

La deuxième insertion, en donnant une valeur pour la colonne auto-incrémentée, passe également sans difficulté.

INSERT INTO BAR2 (ID, NAME) VALUES (NULL, 'Bar');

Cette troisième insertion, en passant NULL pour la colonne auto-incrémentée, lève l’erreur suivante :

ERREUR : ORA-01400: impossible d'insérer NULL dans ("TEST"."BAR2"."ID")

Nous avons donc au final pour cette table :

ID|NAME |
--+-----+
 1|Foo  |
 2|Thing|

Donc, pour un GENERATED BY DEFAULT AS IDENTITY il ne faut pas passer la valeur NULL pour le champ de la clé primaire lors des insertions.

Auto-incrémentation par défaut pour NULL

Voyons maintenant ce qu’il en est avec la troisième façon de définir un champ auto-incrémenté comme clé primaire.

CREATE TABLE BAR3 (
	ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
	NAME VARCHAR2 ( 100 ) NOT NULL  
);

La table étant créée, testons les différentes façons d’insérer les données :

INSERT INTO BAR3 (NAME) VALUES ('Foo');

Cette première insertion sans spécifier de valeur pour la colonne auto-incrémentée passe sans problème.

INSERT INTO BAR3 (ID, NAME) VALUES (2, 'Thing');

La deuxième insertion, en donnant une valeur pour la colonne auto-incrémentée, passe également sans difficulté.

INSERT INTO BAR3 (ID, NAME) VALUES (NULL, 'Bar');

La troisième insertion, en forçant à NULL pour la colonne auto-incrémentée, passe cette fois-ci sans difficulté.

Voyons maintenant les valeurs insérées :

SELECT * FROM BAR3;

Nous avons bien un ID à 3 pour l’insertion avec NULL :

ID|NAME |
--+-----+
 1|Foo  |
 2|Thing|
 3|Bar  |

Donc, pour un GENERATED BY DEFAULT ON NULL AS IDENTITY, il est possible d’utiliser tous les types d’insertion possible !

PK AI récapitulatif

Voici un tableau récapitulant les insertions possibles en fonction du type d’auto-incrémentation utilisé.

Déclaration insert sans ID PK insert ID PK insert ID PK NULL
GENERATED AS IDENTITY OK ERR ERR
GENERATED BY DEFAULT AS IDENTITY OK OK ERR
GENERATED BY DEFAULT ON NULL AS IDENTITY OK OK OK

Conclusion

J’espère vous avoir fait épargner du temps en recherche de résolution de problème pour ces quelques particularités d’Oracle qui peuvent surprendre de prime abord.

Photo de Tom Butler sur Unsplash