Blog

Blog
Affichage des articles dont le libellé est Oracle. Afficher tous les articles
Affichage des articles dont le libellé est Oracle. Afficher tous les articles

Voir les rôles/droits assignés à un user

C'est une demande récurrente. Pouvoir voir les rôles/droits accordés à un user. Si la base de données est bien gérée, tous les droits devraient être accordés via des rôles correctement nommés.

La vue dba_role_privs

Cette vue permet de données tous les rôles accordés et à qui ils ont été accordés. Les colonnes intéressantes sont celles-ci:
  • grantee : Celui à qui on accorde le rôle
  • granted_role : Le rôle autorisé

La vue dba_tab_privs

Mais comme dans la plupart des bases de données on trouve des droits accordés en dehors des rôles, il faut aller sur la vue dba_tab_privs. Cette vue permet de récupérer tous les droits accordés à un user sur un objet de la base. Les colonnes intéressantes sont celles-ci :
  • grantee : Celui à qui on accorde des droits
  • owner : Le propriétaire de l'objet sur lequel des droits ont été accordés
  • table_name : Ce nom est ambigu, il s'agit bien de nom de l'objet (qui peut être une table ou pas)
  • grantor : Celui qui a donné le droit
  • privilege : Le droit accordé

Les vues role_*_privs

Si les rôles n'ont pas été correctement nommés et si la base n'est pas correctement documentée, il est possible d'oublier quels droits sont donnés par un rôle. C'est là que les vues role_role_privs, role_sys_privs et role_tab_privs peuvent vous aider. Malheureusement, le SQL ne permet pas de récursivité pour récupérer l'intégralité des rôles grantés à un autre rôle. Voir le script proposés après.

Tous les droits pour un user

La problématique est la même que pour les rôles. Il n'est pas possible de faire du récursif en SQL. Or, c'est nécessaire dans ce cas car on peut attribuer un rôle à un autre rôle un paquet de fois! Pour corriger ce problème, je vous conseille d'aller faire un tour sur le site de Pete Finnigan qui, même s'il a un sacré problème dans le choix des couleurs de son blog, a écrit une procédure stockée en PL/SQL qui peut déterminer l'ensemble des droits d'un rôle ou d'un user. http://www.petefinnigan.com/weblog/archives/00001243.htm

Pour en savoir plus

Vous pouvez regarder la définition des vues sur le livre Database Reference Oracle. En 11G : http://docs.oracle.com/cd/E11882_01/server.112/e40402/toc.htm

ORA-01654 sur SYSAUX

Je suis tombée sur une erreur ORA-01654 car le tablespace SYSAUX était plein. Je pense qu'il n'avait pas forcément été bien dimensionné au départ (il faut utiliser le script utlsyxsz pour estimer la taille nécessaire à SYSAUX).

Cependant, n'étant pas propriétaire de la base et n'ayant pas l'autorisation d'augmenter le-dit SYSAUX, j'ai du chercher à grappiller quelques Mo.

Utilisation de SYSAUX

Pour savoir ce qui utilise SYSAUX, il suffit de lancer cette requête :
SELECT  occupant_name "Coupable",
space_usage_kbytes/1048576 "Espace occupé (Go)",
schema_name "Schema"
FROM v$sysaux_occupants
ORDER BY 2 desc;
Dans mon cas, c'était AWR qui occupait plus de la moitié du SYSAUX.

AWR et droit d'utilisation

Pour ce client, AWR était d'autant plus inutile qu'il n'avait pas payé le tuning pack. Il n'avait donc pas le droit de générer et d'aller voir les rapports AWR. Il se trouve qu ele client ne savait même pas ce que c'était. j'ai donc eu l'autorisation de purger les snapshots.

Purge des snapshots

Pour purger les snapshots, il faut savoir quel est le plus ancien et quel est le plus récent. Cette requête donne ces infos.
SELECT snap_id, begin_interval_time, end_interval_time 
FROM SYS.WRM$_SNAPSHOT 
WHERE snap_id = ( SELECT MIN (snap_id) FROM SYS.WRM$_SNAPSHOT)
UNION 
SELECT snap_id, begin_interval_time, end_interval_time 
FROM SYS.WRM$_SNAPSHOT 
WHERE snap_id = ( SELECT MAX (snap_id) FROM SYS.WRM$_SNAPSHOT); 

Il faut ensuite réutiliser ces valeurs dans le bloc PL/SQL suivant :
BEGIN
  dbms_workload_repository.drop_snapshot_range(low_snap_id => 14241, high_snap_id=>14321);
END;

Récupérer la place libérée

Il faut ensuite shrinker la table pour pouvoir réellement récupérer la place (il faut être connecté en sys as sysdba).
alter table sys.WRH$_ACTIVE_SESSION_HISTORY shrink space cascade;

Et après ?

La procédure ci-dessus n'est qu'une procédure d'urgence. Il faudra prévoir rapidement d'envisager au moins une des actions suivantes :

  • Faire tourner le script utlsyxsz  pour évaluer le besoin réel d'espace de SYSAUX
  • Ajouter un datafile à SYSAUX (ou augmenter les datafiles existants)
  • Diminuer la fréquence et la rétention des snapshots d'AWR

ORA-19705 ou comment perdre le plus de temps possible

Je suis tombée par hasard sur une erreur ORA-19705 "tag value exceeds maximum length of 31 characters". J'ai donc appris à mes dépends qu'un tag de sauvegarde RMAN ne peut pas faire plus de 31 caractères.
Cela ne m'aurait pas posé de problème si RMAN me l'avait dit AU DEBUT de la sauvegarde, mais ce petit plaisantin a attendu d'avoir sauvegardé mes 100 Go de données pour planter tout à la fin!

La solution

Comme vous vous en doutez, la solution a été de réduire la taille de mon tag de sauvegarde, quitte à ce qu'il soit moins précis...
Tout ça pour ça!

D'un autre côté, si je n'avais pas perdu du temps aujourd'hui pour cette broutille, je n'aurai peut-être pas aussi bien retenu qu'un tag ne doit pas faire plus de 31 caractères!

Comment ne pas perdre le listener sous Windows 2012

Windows ne fonctionne pas comme Linux. Ce n'est pas une nouveauté. par contre, en arrêtant et redémarrant une base sous Windows, j'ai eu la surprise de perdre mon listener qui me découvrait 2 instances pour mon service alors qu'il n'y en avait qu'une :
Le service "rachel" comporte 2 instance(s).
L'instance "rachel", statut READY, comporte 1 gestionnaire(s) pour ce service.
L'instance "rachel", statut UNKNOWN, comporte 1 gestionnaire(s) pour ce service.
J'ai donc arrêté ma base et le listener ne retrouvait plus qu'une seule instance :
Le service "rachel" comporte 1 instance(s).
L'instance "rachel", statut UNKNOWN, comporte 1 gestionnaire(s) pour ce service.

Démarrage propre d'une instance sous Windows

Bien entendu, si la base est arrêtée, il n'est pas possible d'ouvrir sqlplus avec un nom de service (@rachel).
Voici comment faire correctement sous Windows :
set oracle_sid=rachel
sqlplus sys/*** as sysdba
SQL>startup
...et mon listener était content!
Le service "rachel" comporte 1 instance(s).
L'instance "rachel", statut READY, comporte 1 gestionnaire(s) pour ce service.

Comment résoudre une erreur TNS-12560 sous Windows 2012 ?

Nous avons eu "la chance" avec un collègue de faire une installation sous Windows 2012... Ce n'est un secret pour personne, la plupart des DBAs préfèrent une installation sous Linux. Il y a certainement une part de résistance aux changements là-dedans (hé, oui, nous sommes humains, jetez-nous la première pierre!) et aussi une bonne dose de What The Fuck quand on tente d'administrer une base Oracle sous Windows.

Bref, nous avions installé Oracle et nous voulions configurer le listener pour pouvoir créer la base. netca se lance correctement, dit que tout va bien, mais le listener ne fonctionne pas... erreur TNS-12560.

Dieu sous Windows : Le mode administrateur

Après avoir cherché à droite à gauche, j'ai fini par comprendre que notre ami netca, même s'il disait que tout allait bien dans le meilleur des mondes, ne pouvait pas écrire dans le listener.ora... Parce qu'il n'avait pas les droits administrateurs. J'ai donc relancé netca en mode administrateur et tout baigne.

Dieu existe aussi en mode console

J'ai ensuite lancé un petit lsnrctl status et tout allait bien. Puis j'ai voulu arrêter le listener (pour le relancer et voir que tout allait bien) et là, le truc recommence à m'insulter... Pas moyen d'arrêter le listener! Après une petite pause relaxation, j'ai découvert que le menu Windows+X me donnait accès à "Invite de commandes en mode administrateur" après avoir lancé cette console, j'ai pu arrêter mon listener et même le redémarrer sans me faire insulter.

Donc voilà, même avec un compte admin, il faut lancer les différents utilitaires Oracle soit en mode admin soit depuis une console en mode admin. Il n'est pas possible de passer d'une console en mode à une console en mode admin avec une commande équivalente à su, ce serait bien trop simple!

To_number, ORA-01722 et NLS_NUMERIC_CHARACTERS

Une utilisatrice m'a appelée à l'aide car elle n'arrivait pas à corriger ses données en base. Elle avait une colonne de type VARCHAR2 dans laquelle étaient stockés des nombres (modèle de données fourni avec un progiciel). Pour une quarantaine de lignes de sa table, elle avait des nombres avec 28 décimales après la virgules, alors qu'elle ne souhaitait en conserver que 2, cette précision excessive ayant causé un incident de production (là encore, la qualité du code du progiciel ne semble pas extraordinaire). Elle me dit que la fonction ROUND en fonctionne pas et que pourtant sa syntaxe est bonne.

L'erreur ORA-01722

Tout d'abord, habitude que j'ai acquise rapidement en étant au contact avec les utilisateurs : vérifier leurs dire. Je lui demande donc un exemple précis de données avec lesquelles elle joue et je lance sa requête, qui revient à ça (son champ était de type texte) :
SELECT round('355.7885219720776879089176240276',2)
FROM dual;
Ce à quoi Oracle me répond :
ORA-01722: invalid number
01722. 00000 -  "invalid number"
*Cause: The specified number was invalid.
*Action: Specify a valid number.
Le message est pourtant assez clair : il n'a pas reconnu un nombre.

La conversion explicite

Qu'à cela ne tienne, je tente une conversion explicite de ma chaîne de caractères en nombre :
SELECT to_number('355.7885219720776879089176240276')
FROM dual;
A nouveau ORA-01722. Je tente avec une virgule à la place du point :
SELECT to_number('355,7885219720776879089176240276')
FROM dual;
J'obtiens une réponse correcte. Il s'agit donc bien d'un souci de séparateur décimal. Le séparateur décimal est défini au niveau de la base dans le paramètre nls_numeric_characters.

Le paramètre nls_numeric_characters

Pour vérifier la valeur du paramètre, il suffit d'aller sur la vue v$nls_parameters.
SELECT *
FROM v$nls_parameters
WHERE parameter='NLS_NUMERIC_CHARACTERS'
-> La base me répond alors ", ". Cela signifie que pour la base un nombre a pour séparateur décimal la virgule et pour séparateur de groupe l'espace. Pour utiliser le paramètre nls_parameter sous Oracle, je suis obligée d'utiliser le paramètre format.

Le format d'un nombre

Pour modifier la valeur de nls_numeric_characters, il y a deux solutions :
  • Soit on modifie la valeur au niveau de la sessions (ALTER session SET nls_numeric_characters=', ')
  • Soit on précise la valeur à prendre en compte dans la fonction to_number, mais cela impose de préciser le format du nombre

Format des nombres sous Oracle

Ne me voyant pas expliquer la modification du paramètre nls_numeric_characters dans la sessions à mon utilisatrice, j'opte pour la deuxième option. Etant donné que la conversion implicite se plante, il va falloir lui donner le format de nos nombres. Il est bon de savoir que le format peu comporter plus de chiffres que le nombre en lui même mais en aucun cas il ne pourra en comporter moins. Cela nous donne donc ce format :
999D99999999999999999999999999999
Chaque 9 correspond à un chiffre, le D correspond au symbole représentant le séparateur décimal.

La conversion

Il faut donc convertir la chaîne de caractères en nombre, arrondir et reconvertir en chaîne de caractères.
SELECT to_char(round(to_number('355.7885219720776879089176240276','999D9999999999999999999999999999', 'NLS_NUMERIC_CHARACTERS=''. '''),2),'999D99','NLS_NUMERIC_CHARACTERS=''. ''')
FROM dual
Après, c'est vrai que la solution peut sembler complexe, mais avec des nombres stockées dans une colonne de type varchar, je ne peux pas faire de miracle. je suis DBA pas magicienne!

Pour aller plus loin

Vous pouvez consulter la documentation Oracle :

Les surprises de CREATE TABLE AS

Pour certaines opérations, il n'est pas possible de faire un ALTER TABLE. Dans ce cas, on utilise généralement le mécanisme suivant :
  • Créer la nouvelle table à partir de la première
  • Supprimer l'ancienne table
  • Renommer la nouvelle
Malheureusement, le CREATE TABLE AS d'Oracle n'inclue pas les objets liés à la table: index, contraintes et triggers.

Le script pour générer le script

J'ai écrit les requêtes suivantes qui devraient vous permettre de récupérer le script à exécuter. Il faudra remplacer les paramètres par les vrais noms des tables dans la partie centrale (création/drop/renommage de table)
/*Drop des triggers*/
SELECT 'DROP TRIGGER "'||owner||'"."'||trigger_name||'";' from all_triggers WHERE owner='&mon_schema' AND table_name='&ma_table';
/*Drop des index*/
SELECT 'DROP INDEX "'||owner||'"."'||index_name||'";' from all_indexes WHERE owner='&mon_schema' AND table_name='&ma_table' AND NOT (index_name LIKE 'PK_%' OR uniqueness='UNIQUE');
/*Drop des contraintes*/
SELECT 'DROP CONSTRAINT "'||owner||'"."'||constraint_name||'";' from all_constraints WHERE owner='&mon_schema' AND table_name='&ma_table' AND constraint_type='C';
SELECT 'DROP CONSTRAINT "'||owner||'"."'||constraint_name||'";' from all_constraints WHERE owner='&mon_schema' AND table_name='&ma_table' AND constraint_type='R';
SELECT 'DROP CONSTRAINT "'||owner||'"."'||constraint_name||'";' from all_constraints WHERE owner='&mon_schema' AND table_name='&ma_table' AND
 constraint_type='P';
 
/*Création de la nouvelle table*/
CREATE TABLE &mon_schema.&ma_table_new AS SELECT * FROM &mon_schema.&ma_table;
/*Drop de l'ancienne table*/
DROP TABLE &mon_schema.&ma_table;
/*Renommage de la nouvelle table*/
ALTER TABLE &mon_schema.&ma_table_new RENAME TO &mon_schema.&ma_table;

/*Recréation des contraintes*/ SELECT DBMS_METADATA.GET_DDL('CONSTRAINT', constraint_name, owner) from all_constraints WHERE owner='&mon_schema' AND table_name='&ma_table' AND constraint_type='P'; SELECT DBMS_METADATA.GET_DDL('CONSTRAINT', constraint_name, owner) from all_constraints WHERE owner='&mon_schema' AND table_name='&ma_table' AND constraint_type='R'; SELECT DBMS_METADATA.GET_DDL('CONSTRAINT', constraint_name, owner) from all_constraints WHERE owner='&mon_schema' AND table_name='&ma_table' AND constraint_type='C'; /*Recréation des index*/ SELECT DBMS_METADATA.GET_DDL('INDEX', index_name, owner) from all_indexes WHERE owner='&mon_schema' AND table_name='&ma_table' AND NOT (index_name LIKE 'PK_%' OR uniqueness='UNIQUE'); /*Recréation des triggers*/ SELECT DBMS_METADATA.GET_DDL('TRIGGER', trigger_name, owner) from all_triggers WHERE owner='&mon_schema' AND table_name='&ma_table';

La démarche

La démarche permettant d'obtenir des objets vraiment corrects avec les bons noms est donc la suivante :
  • Générer le script
  • Dropper les triggers sur la table d'origine
  • Dropper les index sur la table d'origine
  • Dropper les contraintes sur la table d'origine
  • Créer la nouvelle table à partir de la table d'origine
  • Dropper l'ancienne table
  • Renommer la nouvelle table
  • Recréer les contraintes sur la nouvelle table
  • Recréer les index sur la nouvelle table
  • Recréer les triggers sur la nouvelle table

Pour aller plus loin

La doc Oracle sur le package dbms_metadata : http://docs.oracle.com/cd/E11882_01/appdev.112/e25788/d_metada.htm

Mon article sur comment dropper une table et ses contraintes associés sans faire un cascade : http://laetitia-avrot.blogspot.fr/2012/12/dropper-une-table-sans-cascade-ora-02449.html

Dropper une table sans cascade (ORA-02449)

J'ai un collègue développeur qui a décidé de coder tous les champs texte en clob das une seule table. Forcément, les performances ont commencé à en pâtir. Quand les données ont dues être intégrées à une base décisionnelle, ça a été un désastre.

Ebauche de solution

J'ai donc réfléchi avec lui pour transformer mes clobs en varchar2...
Malheureusement, Oracle ne permet pas de faire un ALTER TABLE pour modifier un champd de type clob en champ de type varchar2, même en mettant la fonction de conversion.
J'ai donc créé une nouvelle table (oui, l'ordre des colonnes était important car il y avait du SQL Loader impacté) et voulu dropper la table d'origine pour renommer la nouvelle...

ORA-02449: unique/primary keys in table referenced by foreign keys

Oracle est sympa sur ce coup-là : il nous donne un sacré coup de main dans les précisions :
Cause: An attempt was made to drop a table with unique or primary keys referenced by foreign keys in another table.
Action:  Before performing the above operations the table, drop the foreign key constraints in other tables. You can see what constraints are referencing a table by issuing the following command: SELECT * FROM USER_CONSTRAINTS WHERE TABLE_NAME = "tabnam";
Bref, vous avez oublié de supprimer les contraintes liées à votre table.

La requête qui tue

J'ai donc concocté cette requête magique (oui, pas de user_constraints, mais dba_constraints car nous n'utilisons jamais les users applicatifs quand des humains se connectent) :
SELECT 'ALTER TABLE '||c.owner||'.'||c.table_name||' DROP CONSTRAINT '||c.constraint_name||';'
FROM dba_constraints p
JOIN dba_constraints c ON p.constraint_name=c.r_constraint_name
  AND p.owner=c.owner
WHERE (p.constraint_type = 'P' OR p.constraint_type = 'U')
AND c.constraint_type = 'R'
AND p.table_name = '&ma_table'
AND p.owner='&mon_schema';
J'ai donc pu tranquillement dropper mes contraintes une par une pour dropper ma table.

Pour aller plus loin

La doc Oracle sur l'ORA-02449 : http://docs.oracle.com/cd/E11882_01/server.112/e17766/e2100.htm#sthref1269

Setter $ORACLE_SID avant de lancer un script (ORA-12162)

Sous Unix, je suis tombée sur un cas assez bizarre : mon script shell devait se connecter à une base via SQL Loader pour faire un import de données mais me sortait une ORA-12162: TNS:net service name is incorrectly specified...

ORA-12162: TNS:net service name is incorrectly specified

La signification de cette erreur est très claire : la variable $ORACLE_SID est mal renseignée... Mais je l'avais bien fait pourtant!

Le phénomène bizarre

Je me suis alors rendue compte que sans export, le script plantait alors qu'avec un export, il ne plantait pas. Exemple : (Ma base s'appelle Matalina)
>lancesqlload.sh
ORACLE_SID=MATALINA
echo $ORACLE_SID : MATALINA
ORA-12162: TNS:net service name is incorrectly specified
>
>lancesqlload_avecExport.sh
export ORACLE_SID=MATALINA
echo $ORACLE_SID : MATALINA
Import Succeed!
>
Je suppose que ce fonctionnement qui me semble bizarre est du au fait qu'un shell s'exécute dans son propre environnement et qu'il a dont besoin de l'export pour prendre en compte les variables ?

Pour aller plus loin

La doc Oracle sur l'ORA-12162 : http://docs.oracle.com/cd/E11882_01/server.112/e17766/net12150.htm#sthref3401

Mais qui bloque ma table ? (ORA-00054)

Grrr rien de pire que de créer une nouvelle table et d'avoir une ORA-00054 au moment de créer les clés trangères...

Recherche des infos sur la session bloquante

Il suffit de jouer cette requête :
SELECT ao.object_id,
  ao.owner,
  ao.object_name,
  lo.session_id,
  s.osuser,
  s.machine,
  s.program
FROM dba_objects ao 
  INNER JOIN v$locked_object lo ON ao.object_id=lo.object_id
  INNER JOIN v$session s ON lo.session_id = s.sid
WHERE object_name='mon_objet';
Il suffit ensuite de décrocher son téléphone et de demander des comptes à la personne qui bloque le truc (si c'est un script, je vous laisse galérer tout seul dans votre coin :-)).

Killer une session quel que soit l'OS

Hé oui, ça peut paraître bête, mais j'ai tellement l'habitude de killer les sessions qui m'embêtent sous Unix que je me suis retrouvée comme une conne sous MS-DOS!

Recherche de la session à killer

Cette partie est la même quel que soit l'OS.
Il suffit de jouer cette requête :
SET LINESIZE 100
COLUMN spid FORMAT A10
COLUMN username FORMAT A10
COLUMN program FORMAT A45

SELECT s.inst_id,
       s.sid,
       s.serial#,
       p.spid,
       s.username,
       s.program
FROM   gv$session s
       JOIN gv$process p ON p.addr = s.paddr 
                        AND p.inst_id = s.inst_id
WHERE  s.type != 'BACKGROUND';
Le résultat sera de ce genre :

   INST_ID        SID    SERIAL# SPID       USERNAME   PROGRAM 
---------- ---------- ---------- ---------- ---------- --------------------------------------------- 
         1        150         17 6116       DBSNMP     emagent.exe 
         1         92        120 1384       XXX        SQL Developer 
         1         16         68 6104       DBSNMP     emagent.exe 
         1         32        105 4616       SYS        sqlplus.exe 
         1        124         22 5684       DBSNMP     emagent.exe 

Killer la session

Toujours sous sqlplus, vous pouvez alors killer cette session. La syntaxe est :
ALTER SYSTEM KILL SESSION 'sid,serial#';
Ce qui dans mon exemple donne :
ALTER SYSTEM KILL SESSION '92,120';

SystÞme modifiÚ.
Oui, la console sous MS-DOS affiche toujours des caractères bizarres, mais Oracle n'a visiblement pas l'intention de le corriger.

Pour aller plus loin

Voici le livre Oracle avec la syntaxe du ALTER SESSION : http://docs.oracle.com/cd/E11882_01/server.112/e26088/statements_2013.htm#i2231814

Résoudre l'ORA-00020

Lors d'un import datapump, je me suis retrouvée face à l'ORA-00020.

L'erreur

Voici les insultes proférées par Oracle pour avoir tenté un import datapump sans avoir les ressources processeur nécessaire :
ORA-31626: la tÔche n'existe pas
ORA-31687: erreur lors de la crÚation du processus esclave  avec l'ID de processus esclave 1
ORA-31687: erreur lors de la crÚation du processus esclave DW01 avec l'ID de processus esclave 1
ORA-39106: Le processus esclave DW01 a ÚchouÚ lors du dÚmarrage. Erreur du processus esclave :
ORA-00020: nombre maximum de processus () atteint
(On remarquera au passage les jolis caractères bizarres liés à une mauvaise utilisation par Oracle du jeu de caractères MS-DOS, encore une raison de préférer les bases en anglais : mieux vaut du bon anglais que du mauvais français...)

Ce que ça veut dire

Voilà l'explication Oracle :

ORA-00020: maximum number of processes (string) exceeded
Cause: All process state objects are in use.
Action: Increase the value of the PROCESSES initialization parameter.

Oracle est tout bêtement en train de dire qu'on lui a alloué un certain nombre de processus et qu'il les a tous mangés!

Première solution

Mon import correspondant à une "restore" (oui, je suis obtue, mais pour moi seul rman fait une vraie restore) totale des données, j'ai demandé à mon intégrateur préféré de couper le serveur d'application. ça a permis de libérer des processus pour faire passer mon import.

Deuxième solution

Si vous ne pouvez pas couper de connexion, vous pouvez tout aussi bien augmenter le nombre de processus disponibles pour cette instance :
SQL> select name, value from v$parameter where name = 'processes';

NAME 
-------------------------------------------------------------------------------- 
VALUE 
-------------------------------------------------------------------------------- 
processes 
150 
SQL> alter system set processes=300 scope=spfile; 
 
SystÞme modifiÚ. 
 
SQL> shutdown immediate 
Base de donnÚes fermÚe. 
Base de donnÚes dÚmontÚe. 
Instance ORACLE arrÛtÚe. 
SQL> startup 
Instance ORACLE lancÚe. 
 
Total System Global Area 1073741824 bytes 
Fixed Size                  1300756 bytes 
Variable Size             427820780 bytes 
Database Buffers          629145600 bytes 
Redo Buffers               15474688 bytes 
Base de donnÚes montÚe. 
Base de donnÚes ouverte. 
On remarquera encore la bonne gestion part Oracle des caractères accentués sous Windows

Pour aller plus loin

Le livre des erreurs Oracle : http://docs.oracle.com/cd/E11882_01/server.112/e17766/e0.htm#sthref15

Plan d'exécution

Il existe plusieurs manières de récupérer un plan d'exécution sous Oracle.

Autotrace only

Cette méthode est ma préférée. Elle aurait cependant l'inconvénient de nécessiter un exécution complète de la requête pour afficher le plan d'exécution. Je n'ai jamais rencontré de problème de lenteur avec cette méthode.
Il suffit d'ajouter la commande autotrace only pour qu'au lieu d'exécuter les requêtes, Oracle nous affiche le plan d'exécution. Je vous laisse faire les set linesize qui vont bien avant.
SET AUTOTRACE TRACEONLY EXPLAIN

Plan_table

Avec cette méthode, on demande la création du plan d'exécution (en lui donnant un id, tant qu'à faire). Le plan d'exécution est ensuite stocké dans la table plan_table. On peut donc l'afficher avec du SQL:
EXPLAIN PLAN SET statement_id='MonId' FOR SELECT * FROM maTable;
SELECT * FROM TABLE(dbms_xplan.display('PLAN_TABLE','MonId',null));
Et voilà vous avez votre plan d'exécution!

Migration 10g->11G avec changement d'OS User

Le client pour lequel j'ai du faire cette migration a changé de stratégie pour la gestion des users OS et j'ai donc du faire une migration de 10gR2 vers 11gR2 sans dbua... Hé oui, c'est la surprise du chef, il n'est pas possible d'utiliser dbua dans ce cas... (cf note metalink 984511.1). D'accord, le plus dur dans un upgrade, c'est de convaincre son chef que c'est important, utile et que ça ne lui coûtera pas trop cher, mais une fois que c'est fait, on n'est pas arrivés pour autant! Voici donc la liste des étapes à effectuer (je passerai rapidement sur certaines choses triviales) :

Installer les binaires 11gR2

Je vous laisse faire cette action tout seul. C'est pas le plus compliqué (quoi que l'install des prérequis quand on n'a pas yum n'est pas ce que je préfère), mais la documentation Oracle est très complète sur ce point.

Lancer l'outil de pré-upgrade

Là encore, c'est assez simple, connecté en sqlplus as sysdba sur l'ancien user oracle, lancer les requêtes suivantes :
SPOOL log_file.log
@new_oracle_home/rdbms/admin/utlu112i.sql
SPOOL OFF

Vérifier que les vues matérialisées sont à jour

Requête toute simple : s'il n'y a pas de résultat, il n'y a rien à faire. Sinon il faudra rafraîchir les vues sorties :
SELECT * FROM sys.obj$ o, sys.user$ u, sys.sum$ s WHERE o.type# = 42 AND bitand(s.mflags, 8) = 8;

S'assurer qu'aucun fichier n'a besoin d'un recovery

Si la requête suivante ramène des résultats, lancer le recovery jusqu'à ce qu'elle n'en ramène plus.
SELECT * FROM v$recover_file;

Vérifier qu'aucun fichier n'est en mode Backup

Remettre les fichiers résultats de cette requête ne mode normal :
SELECT * FROM v$backup WHERE status != 'NOT ACTIVE';

Résoudre les transactions en attente

Si la première requête ramène des résultats, lancer la procedure dbms_transaction.purge_lost_db_entry('id_trans') et commiter.
SELECT * FROM dba_2pc_pending;

Purger la corbeille

Il suffit de faire la commande suivante sous sqlplus :
PURGE dba_recyclebin;

Faire une sauvegarde (;-))

Je préfère faire une sauvegarde à froid pour ce genre de chose, mais il est parfaitement possible de faire une sauvegarde à chaud si on est en mode archive log.
rman target / nocatalog
RUN
{ 
    ALLOCATE CHANNEL c1 TYPE DISK; 
    SHUTDOWN IMMEDIATE; 
    STARTUP MOUNT; 
    BACKUP DATABASE FORMAT '/u01/app/backup_before_upgrade/%U' TAG before_upgrade; 
    BACKUP CURRENT CONTROLFILE FORMAT '/u01/app/backup_before_upgrade/control01.ctl'; 
}

Préparation du nouveau pfile

On commence par se connecter à la base pour créer le pfile.
CREATE PFILE='your_path_and_file_name' FROM SPFILE;
Puis, on regarde le pfile et on supprime les paramètres dépréciés (background_dump_dest, user_dump_dest) et on modifie également les paramètres permettant de bénéficier des nouvelles fonctionnalités (memory_max_target, memory_target vs sga_target, sga_max_target, pga_aggregate_target). On nn'oublie pas de mettre le paramètre de compatibilité (*.compatible='10.2.0.3.0').

Modifier /etc/oratab

Il faut penser à modifier le fichier /etc/oratab pour faire pointer le ORACLE_HOME de la base à migrer vers notre tout nouveau ORACLE_HOME 11G.

Modifier les droits sur les fichiers de la base

On commence par arrêter la base avant de manipuler les droits OS sur les fichiers.
shutdown immediate
Il faut ensuite mettre comme owner des répertoires de la base le nouveau user Oracle. Voici la liste des répertoires à modifier :
  • Répertoires des fichiers de données
  • Répertoires des fichiers de contrôles
  • Répertoires des pfile et spfile
  • Répertoires des archivelogs
  • Répertoires des redos
Commande système à passer sous Linux:
chown -R newUser:oinstall your_rep

Lancer l'outil d'upgrade

Avant de lancer l'outil, bien s'assurer que les variables suivantes sont settées correctement et si nécessaire sur l'environnement 11G :
  • ORACLE_HOME
  • PATH
  • ORACLE_SID
Aller dans le répertoire $ORACLE_HOME/rdbms/admin et démarrer l'instance via sqlplus (connecté as sysdba):
STARTUP MIGRATE PFILE='your_pfile_destination'
Lancer l'outil d'upgrade en mettant un fichier de spool :
SPOOL 'your_log_file'
@catupgrd.sql
SPOOL OFF
Cette exécution est TRES longue... L'outil arrête lorsqu'il a finit et vous déconnecte d'sqlplus. Il faut ensuite redémarrer la base et lancer les outils de post-upgrade :
STARTUP PFILE='your_pfile'
@utlu112s.sql
-> Tous les éléments doivent être valides Puis, on peut lancer la dernière phase de l'upgrade :
@catuppst.sql
Et on recompile les objets invalides :
@utlrp.sql
Et on peut regarder les objets invalides :
SELECT count(*) FROM dba_invalid_objects;
Si tout est bon, on peut créer le spfile :
CREATE SPFILE='your_spfile' FROM PFILE='your_pfile';
Il ne reste plus qu'à modifier les différents scripts qui tournent sur la base pour pointer sur le bon ORACLE_HOME et à s'assurer que les connecteurs JDBC ou autres sont compatibles Oracle 11gR2!

Statspack ou l'ancêtre d'AWR

Hé oui, statspack a été remplacé par AWR (Automatic Workload Repository), mais cette fonctionnalité fait partie du diagnostic pack qui est une option même pour la version Enterprise. (Il faut d'ailleurs faire un peu attention, car cette fonctionnalité est installée même si elle n'a pas été payée...)
Pour ne pas payer ou risquer d'être en infraction, il reste le statspack. je n'ai aps trouvé de vraie doc sur ce statspck en 11g, mais sachez que la doc 9i est toujours d'actualité.

Installer Statspack

Hé oui, contrairement à AWR, il faut l'installer avant de l'utiliser. Autant vous dire que vous ne pourrez jamais sortir un rapport sur ce qu'il s'est passé hier sur la base de production...
Pour installer statspack, il suffit de lancer le script spcreate.sql qui se trouve sous $ORACLE_HOME/rdbms/admin.
sqlplus / as sysdba @?/rdbms/admin/spcreate
Le script devrait vous demander certaines informations comme le mot de passe du user perfstat, le tablespace d'espace temporaire et le tablespace par défaut du user perfstat.

Et c'est tout!

Faire un snapshot

Pour faire un snapshot, il suffit de lancer le script suivant (après s'être connecté avec le user perfstat) :
EXECUTE statspack.snap;

Faire un rapport

Pour faire un rapport, il faut lancer le script suivant (toujours connecté avec le user perfstat) :
@?/rdbms/admin/spreport
Ensuite, on peut analyser plus avant un ordre SQL en fournissant le hash value de la requête SQL au script suivant
@?/rdbms/admin/sprepsql

Programmer des snapshots réguliers

Comme on ne prévient pas quand on va en avoir besoin, il vaut mieux passer régulièrement des snapshots. Pour ça, on peut soit créer un script qui génère les snapshots et le lancer via un scheduler quelconque, soit utiliser Oracle pour les lancer. je préfère utiliser Oracle :-). En 9i, on utilisera DBMS_JOB, à partir de la 10g, on préfèrera DBMS_SCHEDULER.

Modifier les fenêtres de gather stat job en 11g

Je m'occupe d'une certaine plateforme de vote électronique. Comme chacun sait, un vote électronique est disponible H24 7j/7. Il a donc fallu que je me penche sur le recalcul automatique des stats Oracle pour m'assurer qu'ils n'allaient pas intervenir à un moment important du scrutin.

Oracle autotasks

En 11g, les tâches de maintenance Oracle sont lancées dans un scheduler. Ces tâches sont nommées "autotask" (le terme est important car sans lui, il est difficile de s'y retrouver dans la doc Oracle). Ces jobs de maintenance se lancent suivant des fenêtres de maintenance.

Gather stats

Le gather_stats_jobs d'Oracle 10g a été remplacé par une "tâche" que vous pouvez retrouver dans la vue dba_autotask_client. Pour savoir quand il se lance, il vous reste la requête suivante :
SELECT client_name, window_group
FROM dba_autotask_client
WHERE client_name='auto optimizer stats collection';
Vous récupérez donc un "window_group" (normalement, ça devrait s'appeler ORA$AT_WGRP_OS), vous pouvez requéter la table dba_scheduler_window_groups mais elle ne vous apprendra pas grand chose sur les heures de lancement de votre job En fait un "groupe de fenêtres" comporte différentes fenêtres (sic!). Bref, il faut requêter chaque fene^tre ensuite pour savoir quand votre job va se lancer :
SELECT window_group_name, window_name
FROM dba_scheduler_wingroup_members
WHERE window_group_name='ORA$AT_WGRP_OS';
Et voilà votre liste de fenêtres :

WINDOW_GROUP_NAME WINDOW_NAME
------------------------------ ------------------------------
ORA$AT_WGRP_OS MONDAY_WINDOW
ORA$AT_WGRP_OS TUESDAY_WINDOW
ORA$AT_WGRP_OS WEDNESDAY_WINDOW
ORA$AT_WGRP_OS THURSDAY_WINDOW
ORA$AT_WGRP_OS FRIDAY_WINDOW
ORA$AT_WGRP_OS SATURDAY_WINDOW
ORA$AT_WGRP_OS SUNDAY_WINDOW

Visualisation de mes fenêtres

A ce moment-là, vous devrez avoir une petite idée de quelle fenêtre vous voulez modifier. Si vous avez un doute, vous pouvez toujours demander le détail en requêtant dba_scheduler_windows :
SELECT window_name, repeat_interval, duration
FROM dba_scheduler_windows 
WHERE window_name IN ('SATURDAY_WINDOW','SUNDAY_WINDOW');

WINDOW_NAME
------------------------------
REPEAT_INTERVAL
--------------------------------------------------------------------------------
DURATION
---------------------------------------------------------------------------
SATURDAY_WINDOW
freq=daily;byday=SAT;byhour=6;byminute=0; bysecond=0
+000 20:00:00

SUNDAY_WINDOW
freq=daily;byday=SUN;byhour=6;byminute=0; bysecond=0
+000 20:00:00
On voit donc que la maintenance se lance à 6h00 et dure 20h00 les week-ends. Je vais donc pouvoir modifier mes fenêtres du samedi et du dimanche pour que la fenêtre de maintenance commence à 22h00 et dure 4h00. (C'est par défaut le comportant des jours ouvrés.)

Modification de mes fenêtres

Je fais donc ceci (et la même chose pour SUNDAY) :
BEGIN
  dbms_scheduler.disable(
    name  => 'SATURDAY_WINDOW');
  dbms_scheduler.set_attribute(
    name      => 'SATURDAY_WINDOW',
    attribute => 'DURATION',
    value     => numtodsinterval(4, 'hour'));
  dbms_scheduler.set_attribute(
    name      => 'SATURDAY_WINDOW',
    attribute => 'REPEAT_INTERVAL',
    value     => 'freq=daily;byday=SAT;byhour=22;byminute=0; bysecond=0');
  dbms_scheduler.enable(
    name => 'SATURDAY_WINDOW');
END;
/
Et voilà!

Pour aller un peu plus loin avec les stats

Il faut savoir que ce n'est pas parce que ces fenêtres de maintenance sont correctement settées que les statistiques seront forcément recalculées. En effet, Oracle est intelligent et ne recalcule les stats sur une table que s'il considère que c'est nécessaire (plus de 20% de modification sur la table par défaut).

Duplicate : RMAN-06217 et RMAN-04006

Vous avez une nouvelle machine qui doit héberger une standby et vous voulez qu'rman s'en occupe tout seul comme il sait si bien le faire. Vous avez donc créé un pfile minimal et avez démarré l'instance. la base est en nomount. (Ben oui, elle peut pas aller plus loin, y a pas de control file.) Les fichiers tnsnames et de mots de passe sont OK. Le listener tourne, bref tout marche, sauf que non!

Arrivée de la RMAN-06217

Vous tentez donc une connexion rman comme ça : (Dans mes exemples la primaire s'appelle rachel et la standby trenton)
rman target sys/xxx@rachel auxiliary /
et ça semble marcher :
Recovery Manager: Release 11.2.0.1.0 - Production on Mon Oct 3 10:25:57 2011
Copyright (c) 1982, 2009, Oracle and/or its affiliates.  All rights reserved.
connected to target database: RACHEL (DBID=YYY)
connected to auxiliary database: TRENTON (not mounted)
RMAN
puis, bous lancez votre duplicate et arrive l'erreur fatidique (sans avoir été invitée):

run 
{
allocate auxiliary channel t1 type disk;
allocate channel t2 type disk;
duplicate target database for standby from active database password file spfile;
}

using target database control file instead of recovery catalog
allocated channel: t1
channel t1: SID=95 device type=DISK

allocated channel: t2
channel t2: SID=13 device type=DISK

Starting Duplicate Db at 03-OCT-11
released channel: t1
released channel: t2
RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-03002: failure of Duplicate Db command at 10/03/2011 10:26:15
RMAN-06217: not connected to auxiliary database with a net service name

Arrivée de la RMAN-04006

Vous remplissez donc consciencieusement votre tnsnames.ora. Vous faites même un petit tnsping pour être sûr :
tnsping trenton

TNS Ping Utility for Linux: Version 11.2.0.1.0 - Production on 03-OCT-2011 10:36:03

Copyright (c) 1997, 2009, Oracle.  All rights reserved.

Used parameter files:
/admin/oracle/network/admin/sqlnet.ora

Used TNSNAMES adapter to resolve the alias
Attempting to contact (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = yyy)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = TRENTON)))
OK (0 msec)
Fiers de vous, vous lancez donc la commande qui doit marcher :
rman target sys/xxx@rachel auxiliary sys/xxx@trenton

Recovery Manager: Release 11.2.0.1.0 - Production on Mon Oct 3 10:27:25 2011

Copyright (c) 1982, 2009, Oracle and/or its affiliates.  All rights reserved.

connected to target database: RACHEL (DBID=YYY)
RMAN-00571: ===========================================================
RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============
RMAN-00571: ===========================================================
RMAN-00554: initialization of internal recovery manager package failed
RMAN-04006: error from auxiliary database: ORA-12528: TNS:listener: all appropriate instances are blocking new connections
Et le pire, c'est que c'est logique! La BDD secondaire n'a pas pu s'enregistrer auprès du listener (ben oui, elle est toujours à l'état nomount), on ne peut donc pas se connecter en utilisant un service name!

La solution

D'après la note metalink Connection to Auxilary using connect string failed with ORA-12528 (Doc ID 419440.1) (Attention, authentification nécessaire), il faut modifier le tnsnames comme ça :

TRENTON =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = yyy)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = TRENTON)
      (UR=A)
    )
  )

Délocker un user

Ben oui, ça arrive... Le client a tapé plusieurs fois un mauvais mot de passe et le compte est locké...

Comportement du lock

Tout est paramétrable lorsque vous créez le profile. Si vous n'avez rien spécifié, le compte sera locké pendant une journée au bout de 10 tentatives de login avec un faux mot de passe.
Pour retrouver ces infos :
SELECT resource_name, limit
FROM dba_profiles INNER JOIN dba_users ON dba_profiles.profile=dba_users.profile
WHERE username='user_name' 
  AND  resource_name IN ('PASSWORD_LOCK_TIME','FAILED_LOGIN_ATTEMPTS');

Voir le statut

Voilà la requête :
SELECT account_status 
FROM dba_users 
WHERE username='user_name'; 
Si le résultat est OPEN, votre compte n'est pas locké, si vous trouvé LOCKED(TIME), c'est locké parce le user s'est trompé trop de fois de mot de passe.

Délocker

C'est tout simple :
ALTER USER user ACCOUNT UNLOCK;
J'espère que vous n'aurez pas à vous en servir trop souvent!

Les commandes Rman

Je ne sais pas de quelle planète vient le gars qui a inventé les commandes rman, mais c'est pas la même que la mienne! Je galère pour retrouver la syntaxe des commandes à chaque fois.

En passant, c'est pas génial qu'Oracle n'ai pas prévu une certaine cohérence entre ses outils, mais bon, peut-être en 13c ?

Bref, voilà le lien vers la doc Oracle 11g pour les commandes rman : http://download.oracle.com/docs/cd/E11882_01/backup.112/e10643/rcmcomma005.htm

Mettre en place un observer

Bon, admettons que vous ayiez un dataguard qui fonctionne bien. C'est bien. Mais si ça plante à 2h00 du matin, faut vous appeler pour faire la bascule ? Hé non, y a observer pour ça, mais faut le configurer d'abord...

Standby OK

Commencez par vérifier que votre standby applique bien les redos de la primaire... (cf le billet Vérifier qu'une standby applique bien les logs)

LogXptMode

Il faut que la propriété LogXptMode soit settée à SYNC pour chaque base de données. La requête se lance sous dgmgrl.
SHOW DATABASE 'DB' 'LogXptMode';
Si la propriété n'est pas à SYNC, mettez-la à cette valeur :
EDIT DATABASE 'DB' SET PROPERTY 'LogXptMode'='SYNC';
Atention cette manipulation est à faire sur le maître et sur l'esclave!

FastStartFailoverTarget

Oui, c'est une succession de variable à setter... Je suis désolée. Cette option n'est à modifier que s'il y a ambiguïté sur la standby sur laquelle basculer.
Pour connaître la valeur de FastStartFailoverTarget, il faut faire ça:
SHOW DATABASE 'DB' 'FastStartFailoverTarget';
Pour modifier la valeur (sur la primaire uniquement) :
EDIT DATABASE 'DB' SET PROPERTY FastStartFailoverTarget='your_value';

Mode de protection

Il est temps de passer de 'Maximum performance' à 'Maximum availability'.
EDIT CONFIGURATION SET PROTECTION MODE AS MAXAVAILABILITY;

Flashback

C'est le moment d'activer le flashback sur vos bases. Pour savoir si c'est déjà fait (sous sqlplus sur chacune des bases) :
SELECT flashback_on FROM v$database;
Profitez-en pour vérifier aussi les valeurs des paramètres suivantes:
SHOW PARAMETER undo_retention;
SHOW PARAMETER undo_management;
SHOW PARAMETER db_flashback_retention_target;
SHOW PARAMETER db_recovery_file_dest_size;
SHOW PARAMETER db_recovery_file_dest;
Si c'est pas fait, utilisez les ordres ci-dessous (pour le maître):
ALTER SYSTEM SET undo_retention=3600 SCOPE=SPFILE;
ALTER SYSTEM SET undo_management='AUTO' SCOPE=SPFILE;
SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
SHOW PARAMETER undo;
ALTER SYSTEM SET db_flashback_retention_target=4320 SCOPE=BOTH;
ALTER DATABASE ARCHIVELOG;
ALTER SYSTEM SET db_recovery_file_dest_size=size;
ALTER SYSTEM SET db_recovery_file_dest=directory-specification;
ALTER DATABASE FLASHBACK ON;
ALTER DATABASE OPEN;
Pour l'esclave, il faut arrêter l'apply (puisque la base est déjà montée) via dgmgrl comme ça :
EDIT DATABASE db SET STATE='APPLY-OFF';
Puis on le remet en place :
EDIT DATABASE db SET STATE='APPLY-ON';

Démarrer l'observer

Il vaut mieux spécifier un fichier de log:
dgmgrl -logfile Your_logfile
Pour démarrer l'observer, il faut faire
start observer;
Dgmgrl ne vous rendra pas la main. Pour cette raison, il est préférable d'encapsuler tout ça dans un script lancé ensuite avec un nohup.

Activer le Fast-start failover

C'est ce qui permet de basculer rapidement, comme son nom l'indique. Il faut faire cette commande sous dgmgrl.
ENABLE FAST_START FAILOVER;
Vous pouvez vérifier que tout va bien en faisant ça sous dgmgrl:
SHOW FAST_START FAILOVER;
Ou ça sous sqlplus (sur chacune des bases) :
SELECT fs_failover_status, fs_failover_current_target, fs_failover_threshold, fs_failover_observer_present,fs_failover_observer_host FROM v$database;

Pour en savoir plus

la doc Oracle dispo ici : http://download.oracle.com/docs/cd/E11882_01/server.112/e17023/cli.htm#DGBKR3430