web-dev-qa-db-fra.com

Rechercher si une colonne dans Oracle a une séquence

J'essaie de déterminer si une colonne d'Oracle est renseignée à partir d'une séquence. Mon impression de la façon dont Oracle gère le séquençage est que la séquence et la colonne sont des entités distinctes et que vous devez insérer manuellement la valeur de séquence suivante, telle que:

insert into tbl1 values(someseq.nextval, 'test')

ou le mettre dans un déclencheur de table. Cela signifie qu'il est non trivial de dire si une colonne est remplie à partir d'une séquence. Est-ce exact? Avez-vous des idées sur la manière de déterminer si une colonne est renseignée à partir d'une séquence?

25
stimms

Vous avez raison; la séquence est distincte de la table et une seule séquence peut être utilisée pour renseigner n'importe quelle table et les valeurs d'une colonne dans une table peuvent généralement provenir d'une séquence (ou d'un ensemble de séquences), à l'exception des valeurs générées manuellement.

En d'autres termes, il n'y a pas de connexion obligatoire entre une colonne et une séquence - et donc aucun moyen de découvrir une telle relation à partir du schéma.

En fin de compte, l'analyse portera sur le code source de toutes les applications qui insèrent ou mettent à jour des données dans la table. Rien d'autre n'est garanti. Vous pouvez réduire l'étendue de la recherche si une procédure stockée est le seul moyen d'apporter des modifications à la table, ou s'il existe un déclencheur qui définit la valeur, ou autre. Mais la solution générale est la "non-solution" de "analyser la source".

19
Jonathan Leffler

Si la séquence est utilisée dans un déclencheur, il est possible de trouver les tables qu’elle remplit:

SQL> select t.table_name, d.referenced_name as sequence_name
  2  from   user_triggers t
  3         join user_dependencies d
  4         on d.name = t.trigger_name
  5  where  d.referenced_type = 'SEQUENCE'
  6  and    d.type = 'TRIGGER'
  7  /

TABLE_NAME                     SEQUENCE_NAME
------------------------------ ------------------------------
EMP                            EMPNO_SEQ

SQL>

Vous pouvez modifier cette requête pour rechercher des procédures stockées, etc. qui utilisent la séquence. 

11
APC

Il n'y a pas de liens de métadonnées directs entre les séquences Oracle et aucune utilisation dans la base de données. Vous pouvez deviner intelligemment si les valeurs d'une colonne sont liées à une séquence en interrogeant les métadonnées USER_SEQUENCES et en comparant la colonne LAST_NUMBER aux données de la colonne.

2
dpbradley

Comme Jonathan l'a souligné: il n'y a pas de moyen direct de relier les deux objets. Toutefois, si vous "conservez un standard" pour les clés primaires et les séquences/déclencheurs, vous pouvez le découvrir en recherchant la clé primaire, puis en associant la contrainte à la séquence de la table.

J'avais besoin de quelque chose de similaire car nous construisions un produit multi-db et j'ai essayé de répliquer certaines classes avec des propriétés trouvées dans un objet DataTable de .Net qui a AutoIncrement, IncrementSeed et IncrementStep, qui ne peuvent être trouvées que dans les séquences.

Ainsi, comme je l'ai dit, si vous utilisez pour vos tables une PK et que vous avez toujours une séquence associée à un déclencheur d'insertions sur une table, cela peut s'avérer utile:

select tc.table_name,
  case tc.nullable 
    when 'Y' then 1
    else 0
  end as is_nullable,
  case ac.constraint_type 
    when 'P' then 1
    else 0
  end as is_identity,
  ac.constraint_type,
  seq.increment_by as auto_increment_seed,
  seq.min_value as auto_increment_step,
  com.comments as caption,
  tc.column_name,
  tc.data_type,
  tc.data_default as default_value,
  tc.data_length as max_length,
  tc.column_id,
  tc.data_precision as precision,
  tc.data_scale as scale
from SYS.all_tab_columns tc
left outer join SYS.all_col_comments com
  on (tc.column_name = com.column_name and tc.table_name = com.table_name)
LEFT OUTER JOIN  SYS.ALL_CONS_COLUMNS CC 
  on (tc.table_name = cc.table_name and tc.column_name = cc.column_name and tc.owner = cc.owner)
LEFT OUTER JOIN SYS.ALL_CONSTRAINTS AC
  ON (ac.constraint_name = cc.constraint_name and ac.owner = cc.owner)
LEFT outer join user_triggers trg
  on (ac.table_name = trg.table_name and ac.owner = trg.table_owner)
LEFT outer join user_dependencies dep
  on (trg.trigger_name = dep.name and dep.referenced_type='SEQUENCE' and dep.type='TRIGGER')
LEFT outer join user_sequences seq
  on (seq.sequence_name = dep.referenced_name)  
where tc.table_name = 'TABLE_NAME'
and tc.owner = 'SCHEMA_NAME'
AND AC.CONSTRAINT_TYPE = 'P'
union all
select tc.table_name,
  case tc.nullable 
    when 'Y' then 1
    else 0
  end as is_nullable,
  case ac.constraint_type 
    when 'P' then 1
    else 0
  end as is_identity,
  ac.constraint_type,
  seq.increment_by as auto_increment_seed,
  seq.min_value as auto_increment_step,
  com.comments as caption,
  tc.column_name,
  tc.data_type,
  tc.data_default as default_value,
  tc.data_length as max_length,
  tc.column_id,
  tc.data_precision as precision,
  tc.data_scale as scale
from SYS.all_tab_columns tc
left outer join SYS.all_col_comments com
  on (tc.column_name = com.column_name and tc.table_name = com.table_name)
LEFT OUTER JOIN  SYS.ALL_CONS_COLUMNS CC 
  on (tc.table_name = cc.table_name and tc.column_name = cc.column_name and tc.owner = cc.owner)
LEFT OUTER JOIN SYS.ALL_CONSTRAINTS AC
  ON (ac.constraint_name = cc.constraint_name and ac.owner = cc.owner)
LEFT outer join user_triggers trg
  on (ac.table_name = trg.table_name and ac.owner = trg.table_owner)
LEFT outer join user_dependencies dep
  on (trg.trigger_name = dep.name and dep.referenced_type='SEQUENCE' and dep.type='TRIGGER')
LEFT outer join user_sequences seq
  on (seq.sequence_name = dep.referenced_name)  
where tc.table_name = 'TABLE_NAME'
and tc.owner = 'SCHEMA_NAME'
AND AC.CONSTRAINT_TYPE is null;

Cela vous donnerait la liste des colonnes pour un schéma/table avec:

  • Nom de la table
  • Si la colonne est nullable
  • Type de contrainte (uniquement pour les PK)
  • Incrémenter la graine (de la séquence)
  • Incrément pas (à partir de la séquence)
  • Commentaires de colonne
  • Nom de la colonne, bien sûr :)
  • Type de données
  • Valeur par défaut, le cas échéant
  • Longueur de la colonne
  • Index (identifiant de colonne)
  • Précision (pour les nombres)
  • Échelle (pour les nombres)

Je suis presque sûr que le code peut être optimisé, mais cela fonctionne pour moi. Je l'utilise pour "charger des métadonnées" dans les tables, puis représenter ces métadonnées en tant qu'entités sur mon interface.

Notez que je ne filtre que les clés primaires et ne récupère pas les contraintes de clés composées car je ne me soucie pas de celles-ci. Si vous le faites, vous devrez modifier le code pour le faire et vous assurer de filtrer les doublons, car vous pourriez obtenir une colonne deux fois (une pour la contrainte PK, une autre pour la clé composée).

1
Gustavo Rubio
select t.table_name,
   d.referenced_name  as sequence_name,
   d.REFERENCED_OWNER as "OWNER",
   c.COLUMN_NAME
  from user_trigger_cols t, user_dependencies d, user_tab_cols c
 where d.name = t.trigger_name
   and t.TABLE_NAME = c.TABLE_NAME
   and t.COLUMN_NAME = c.COLUMN_NAME
   and d.referenced_type = 'SEQUENCE'
   and d.type = 'TRIGGER'
1
Ali