Introduction
Pour se connecter à une base Oracle via le fichier TNSNAMES.ora, Oracle recommande d'utiliser le nom d'un service avec le paramètre SERVICE_NAME. On peut aussi utiliser le nom de l'instance avec INSTANCE_NAME mais Oracle met depuis plusieurs versions en avant cette notion de services. Entre le client et le serveur Oracle, il s'agit d'un maillon supplémentaire dans la chaîne de connexion et il est fondamental de pouvoir identifier les services disponibles dans une base pour s'y connecter et de savoir à quelle PDB ce service se rapporte dans le cas d'une base 12c multi-tenant.

Le but de cet article n'est pas de vous présenter les services Oracle mais de vous montrer où ils se trouvent dans le dictionnaire de données; et croyez-moi, c'est plus complexe que je ne le pensais à première vue



Points d'attention
N/A.



Base de tests
Une base Oracle 12c en mode multi-tenant.



Exemples

============================================================================================
Identifier ma base et fichier tnsnames.ora

============================================================================================
Ma base de test 12c s'appelle ORCL12C, l'instance se nomme orcl12c et j'ai deux PDB de nom ORCL et PDB$SEED et bien sur le CDB$ROOT. J'ai récupéré cette base sur le site d'Oracle et, comment dire, pourquoi ont-ils donné le même nom à l'instance, à la base, à des services, en jouant sur les majuscules et minuscules... ça complexifie l'appréhension de la base.
          SQL> select instance_name, name from v$instance, v$database;
          INSTANCE_NAME     NAME
          ----------------   ----------------------------
          orcl12c                     ORCL12C

          SQL> select PDB_NAME from dba_pdbs;
          PDB_NAME
          --------------------------------------------------------------------------------
          ORCL
          PDB$SEED

          SQL> select NAME from v$containers order by 1;
          NAME
          --------------------------------------------------------------------------------
          CDB$ROOT
          ORCL
          PDB$SEED

Voici le contenu du fichier tnsnames.ora pour ma base. Il y a deux entrées dans ce fichier : ORCL12C pointant vers le service orcl12c et ORCL pointant vers le service orcl.
          [oracle@vbgeneric ~]$ more $ORACLE_HOME/network/admin/tnsnames.ora
          # tnsnames.ora Network Configuration File: /u01/app/oracle/product/12.2/db_1/network/admin/tnsnames.ora
          # Generated by Oracle configuration tools.
          LISTENER_ORCL12C =
            (ADDRESS = (PROTOCOL = TCP)(HOST = 0.0.0.0)(PORT = 1521))
 
          ORCL12C =
            (DESCRIPTION =
              (ADDRESS = (PROTOCOL = TCP)(HOST = 0.0.0.0)(PORT = 1521))
              (CONNECT_DATA =
                (SERVER = DEDICATED)
                (SERVICE_NAME = orcl12c)
              )
            )

          ORCL =
            (DESCRIPTION =
              (ADDRESS = (PROTOCOL = TCP)(HOST = 0.0.0.0)(PORT = 1521))
              (CONNECT_DATA =
                (SERVER = DEDICATED)
                (SERVICE_NAME = orcl)
              )
            )


============================================================================================
Les vues sur les services

============================================================================================

J'ai récupéré les noms de ma base, de l'instance, des PDBs et il ne me manque plus que ceux des services; et c'est là que ça devient compliqué!

Le contenu du paramètre service_names n'est pas suffisant car il ne contient pas tous les services; attention, on ne parle pas du paramètre service_name du tnsnames.ora qui lui est au singulier. Pourtant Oracle précise que "SERVICE_NAMES spécifie le ou les noms des services par lesquels le client peut se connecter à la base à distance." Je pensais que tous les services étaient dans ce paramètre service_names mais il ne contient que les services auxquels on permet à des users de se connecter à la base via le listener, donc à distance. Il manque donc certains services utilisés en interne par Oracle.

Pour ma base, son contenu est orcl12c alors qu'on va voir que Oracle gère en réalité 7 services dans cette base.
          SQL> show con_name
          CON_NAME
          ------------------------------
          CDB$ROOT

          SQL> show parameter service
          NAME                     TYPE     VALUE
          ------------------------------------ ----------- ------------------------------
          service_names                 string     orcl12c

          SQL> alter session set container=ORCL;
          Session altered.

          SQL> show con_name
          CON_NAME
          ------------------------------
          ORCL

          SQL> show parameter service
          NAME                     TYPE     VALUE
          ------------------------------------ ----------- ------------------------------
          service_names                 string     orcl12c

La question est maintenant de savoir quels sont tous les services dans une base Oracle 12c en multi-tenant et à quelle PDB ou CDB$ROOT correspond chaque service. On peut effectivement avoir des noms de services assez hermétiques comme SERV01, SERV02 dans le tnsnames.ora et il est dur de savoir, a priori, à quelle base ils se rapportent. Pour cela, il faut interroger les vues du dictionnaire de données contenant SERVICE dans leur nom.

Par défaut on pense à la vue DBA_SERVICES car la doc Oracle dit qu'elle "affiche tous les services de la base" : en réalité c'est vrai pour la PDB mais si on veut TOUS les services de la base (PDBs et CDB inclues), il faut se connecter au CDB$ROOT et, problème classique avec Oracle, interroger plusieurs vues car aucune ne contient l'intégralité des données recherchées.

          SQL> show con_name
          CON_NAME
          ------------------------------
          CDB$ROOT


J'utilise mon script pour identifier les vues avec SERVICE dans leur nom (qui va remplacer la variable de substitution 1). En filtrant déjà sur plusieurs vues on en trouve 62!
          SELECT TABLE_NAME FROM DICT where UPPER(table_name) like UPPER('%&&1%')
          UNION
          SELECT TABLE_NAME FROM DBA_TABLES where UPPER(table_name) like UPPER('%&1%')
          UNION
          SELECT TABLE_NAME FROM DBA_ALL_TABLES where UPPER(table_name) like UPPER('%&1%')
          UNION
          SELECT VIEW_NAME FROM DBA_VIEWS where UPPER(view_name) like UPPER('%&1%') and VIEW_NAME not like 'GV_$%' and VIEW_NAME not like 'CDB%' and VIEW_NAME not like 'V_$%'
          UNION
          SELECT NAME FROM V$FIXED_TABLE where UPPER(name) like UPPER('%&1%') and NAME not like 'V_$%' and NAME not like 'GV%'
          UNION
          SELECT VIEW_NAME FROM V$FIXED_VIEW_DEFINITION where UPPER(view_name) like UPPER('%&1%') and VIEW_NAME not like 'GV%'
          ORDER BY 1;

          TABLE_NAME
          --------------------------------------------------------------------------------
          ALL_SDO_CSW_SERVICE_INFO
          ALL_SERVICES
          AQ$SYS$SERVICE_METRICS_TAB
          AQ$SYS$SERVICE_METRICS_TAB_R
          AQ$SYS$SERVICE_METRICS_TAB_S
          AQ$_SYS$SERVICE_METRICS_TAB_F
          AQ$_SYS$SERVICE_METRICS_TAB_G
          AQ$_SYS$SERVICE_METRICS_TAB_H
          AQ$_SYS$SERVICE_METRICS_TAB_I
          AQ$_SYS$SERVICE_METRICS_TAB_L
          AQ$_SYS$SERVICE_METRICS_TAB_S
          AQ$_SYS$SERVICE_METRICS_TAB_T
          AWR_PDB_SERVICE_NAME
          AWR_PDB_SERVICE_STAT
          AWR_PDB_SERVICE_WAIT_CLASS
          AWR_ROOT_SERVICE_NAME
          AWR_ROOT_SERVICE_STAT
          AWR_ROOT_SERVICE_WAIT_CLASS
          CDB_HIST_SERVICE_NAME
          CDB_HIST_SERVICE_STAT         
          CDB_HIST_SERVICE_WAIT_CLASS
          CDB_SERVICE$
          CDB_SERVICES
          CSW_SERVICE_INFO
          DBA_HIST_SERVICE_NAME
          DBA_HIST_SERVICE_STAT
          DBA_HIST_SERVICE_WAIT_CLASS
          DBA_SERVICES
          DBA_UMF_SERVICE
          DIR$SERVICE_ATTRIBUTES
          DIR$SERVICE_OPERATIONS
          EXP_SERVICE$
          GV$ACTIVE_SERVICES
          GV$SERVICEMETRIC
          GV$SERVICEMETRIC_HISTORY
          GV$SERVICES
          GV$SERVICE_EVENT
          GV$SERVICE_REGION_METRIC
          GV$SERVICE_STATS
          GV$SERVICE_WAIT_CLASS
          OPENLSSERVICES
          SERVICE
          SERVICE$
          SERVICE_PREFERRED_AVAILABLE
          SYS$SERVICE_METRICS_TAB
          UMF$_SERVICE
          UMF$_SERVICE_XML
          USER_SDO_CSW_SERVICE_INFO
          V$ACTIVE_SERVICES
          V$SERVICEMETRIC
          V$SERVICEMETRIC_HISTORY
          V$SERVICES
          V$SERVICE_EVENT
          V$SERVICE_REGION_METRIC
          V$SERVICE_STATS
          V$SERVICE_WAIT_CLASS
          WRH$_SERVICE_NAME
          WRH$_SERVICE_STAT
          WRH$_SERVICE_STAT_BL
          WRH$_SERVICE_WAIT_CLASS
          WRH$_SERVICE_WAIT_CLASS_BL
          WRHS$_SERVICE_NAME
          62 rows selected.

Parmi toutes ces vues, certaines me semblent plus intéressantes que d'autres mais POURQUOI est-ce que Oracle multiplie les vues plutôt que d'en créer une ou deux qui seraient des points d'entrée? Et, en plus, avec l'architecture multi-tenant, si vous êtes connecté dans une PDB ou le CDB$ROOT, le contenu de ces vues n'est pas le même!

Les vues suivantes sont intéressantes MAIS, problème, aucune ne liste tous les services de toutes les bases de mon container :-(
          CDB_SERVICES
          DBA_SERVICES
          SERVICE (sans S à la fin)
          SERVICE$ (avec un $ à la fin)
          V$ACTIVE_SERVICES
          V$SERVICES


============================================================================================
Le contenu des vues dans le CDB$ROOT et la PDB

============================================================================================

On voit que pour la même vue, le contenu est différent suivant que je suis dans la PDB ou le CDB$ROOT. En revanche, pour la PDB applicative, seul le service orcl apparaît, quelle que soit la vue interrogée. Pour le rôle précis de chaque vue, je vous laisse consulter la doc Oracle.

DBA_SERVICES dans CDB$ROOT
         
SQL> select name, pdb from dba_services order by 1;
          NAME                              PDB
          ------------------------          --------------------
          SYS$BACKGROUND    CDB$ROOT
          SYS$USERS                CDB$ROOT
          orcl12c                         CDB$ROOT
          orcl12cXDB                   CDB$ROOT

DBA_SERVICES dans PDB
          SQL> select name, pdb from dba_services order by 1;
          NAME             PDB
          -------------------- ----------
          orcl             ORCL



V$ACTIVE_SERVICES dans CDB$ROOT
          SQL> select name, con_name from v$active_services order by 1;
          NAME                           CON_NAME
          -------------------- --------------------
          SYS$BACKGROUND     CDB$ROOT
          SYS$USERS                 CDB$ROOT
          orcl                               ORCL
          orcl12c                          CDB$ROOT
          orcl12cXDB                    CDB$ROOT

V$ACTIVE_SERVICES dans PDB
          SQL> select name, con_name from v$active_services order by 1;
          NAME             CON_NAME
          -------------------- ----------
          orcl             ORCL


V$SERVICES dans CDB$ROOT
          SQL> select name, pdb from v$services order by 1;
          NAME                             PDB
          --------------------               ----------------------
          SYS$BACKGROUND     CDB$ROOT
          SYS$USERS                 CDB$ROOT
          orcl                               ORCL
          orcl12c                          CDB$ROOT
          orcl12cXDB                    CDB$ROOT

V$SERVICES dans PDB
          SQL>  select name, pdb from v$services order by 1;
          NAME             PDB
          -------------------- ----------
          orcl             ORCL


CDB_SERVICES dans CDB$ROOT
          SQL> select name, pdb from CDB_SERVICES order by 1;
          NAME                                 PDB
          --------------------                --------------------------
          SYS$BACKGROUND           CDB$ROOT
          SYS$USERS                       CDB$ROOT
          orcl                                     ORCL
          orcl12c                                CDB$ROOT
          orcl12cXDB                          CDB$ROOT


CDB_SERVICES dans PDB
          SQL> select name, pdb from CDB_SERVICES order by 1;
          NAME      PDB
          ------------ ----------
          orcl          ORCL


SYS.SERVICE$ dans CDB$ROOT : seule table avec les services pour accéder à la PDB$SEED mais il manque les services de la PDB! Je pensais trouver dans la table service$ la liste complète de tous les service car c'est une table du dictionnaire de données (elle finit par $) alors que les autres sont des vues sur le dictionnaire mais non, même là il manque un service, celui pour accéder à la PDB de nom ORCL (pour cela il faut se connecter à la PDB).
          SQL> select name, pdb from sys.service$ order by 1;
          NAME                            PDB
          --------------------              -----------------------
          SYS$BACKGROUND     CDB$ROOT
          SYS$USERS                 CDB$ROOT
          orcl12c                          CDB$ROOT
          orcl12cXDB                    CDB$ROOT
          seeddata                       CDB$ROOT
          seeddataXDB                 CDB$ROOT

SYS.SERVICE$ dans PDB
          SQL> select name, pdb from sys.service$ order by 1;
          NAME      PDB
          ----------- -------------
          orcl          ORCL


L'ordre SQL que je vous propose pour afficher tous les services est le suivant, en sachant qu'il faut être connecté au CDB$ROOT pour cela.
          SQL> select name, pdb from v$services
          UNION
          select name, pdb from sys.service$
          ORDER BY 1, 2;

Dans le CDB$ROOT : nous avons bien les 7 services.
          NAME                                 PDB
          --------------------------------------     --------------------
          SYS$BACKGROUND            CDB$ROOT
          SYS$USERS                        CDB$ROOT
          orcl                                     ORCL
          orcl12c                                CDB$ROOT
          orcl12cXDB                         CDB$ROOT
          seeddata                             CDB$ROOT
          seeddataXDB                      CDB$ROOT
          7 rows selected.

Dans la PDB.
          NAME                                 PDB
          --------------------------------------     --------------------
          orcl                                     ORCL


============================================================================================
Les autres services Oracle

============================================================================================

Vous avez noté que certains services ne sont pas utilisés pour se connecter à la base. Mon fichier tnsnames.ora contient seulement les entrées SERVICE_NAME=orcl et SERVICE_NAME=orcl12c. A quoi servent les autres services?

SYS$BACKGROUND : service utilisé par les process d'arrière plan de l'instance pour se connecter à la base.
SYS$USERS : service par défaut pour les sessions utilisateurs qui ne sont pas associés à des services.
orcl12cXDB : service pour se connecter au CDB$ROOT en mode SHARED  SERVERS.
          SQL> show parameter dispatchers
          NAME                     TYPE     VALUE
          ------------------------------------ ----------- ------------------------------
          dispatchers                 string     (PROTOCOL=TCP) (SERVICE=orcl12cXDB)
          max_dispatchers              integer
seeddata : service pour se connecter à la PDB$SEED
seeddataXDB : service pour se connecter à la PDB$SEED en mode SHARED  SERVERS


Une fois de plus nous constatons que, dans le dictionnaire de données d'Oracle, il faut chasser l'information et ne pas se contenter de celles données par les vues qui nous semblent les plus évidentes, comme DBA_SERVICES dans notre cas, mais multiplier les recherches.