Lister toutes les séquences dans un Postgres db 8.1 avec SQL

147

Je convertis une base de données de postgres en mysql.

Comme je ne trouve pas d'outil qui fasse le truc lui-même, je vais convertir toutes les séquences postgres en identifiants d'auto-incrémentation dans mysql avec une valeur d'auto-incrémentation.

Alors, comment puis-je lister toutes les séquences dans une base de données Postgres ( version 8.1 ) avec des informations sur la table dans laquelle elle est utilisée, la valeur suivante, etc. avec une requête SQL?

Sachez que je ne peux pas utiliser la information_schema.sequencesvue dans la version 8.4.

apelliciari
la source
1
Il convient de noter que vous effectuez la conversion dans le mauvais sens. Depuis qu'Oracle a acheté Sun, ils ont lentement tué MySQL, donc à moins que vous ne méprisiez votre client (auquel cas vous devriez simplement quitter), vous devriez vous en tenir à PostgreSQL car aucune société (pro-monopole ou non) ne peut venir, engloutir PostgreSQL et éventuellement le remplacer par leur propre base de données.
John
@John Je dirais qu'il y a un milliard et une autre raison de s'en tenir à postgres, et un milliard de plus pour ne jamais toucher à mysql, mais oui - votre point est toujours très valable :)
Ruslan
@John à l'époque (2009) nous avons besoin d'une base de données plus simple - et mysql était mieux couplé à php
apelliciari

Réponses:

251

La requête suivante donne les noms de toutes les séquences.

SELECT c.relname FROM pg_class c WHERE c.relkind = 'S';

En général, une séquence est nommée ${table}_id_seq. Une simple correspondance de motif regex vous donnera le nom de la table.

Pour obtenir la dernière valeur d'une séquence, utilisez la requête suivante:

SELECT last_value FROM test_id_seq;
Anand Chitipothu
la source
5
L' ${table}_id_seqindice était utile
Pierre de LESPINAY
${table}_${column}_seqpour les séquences créées automatiquement
Evgeny Nozdrev
81

Notez qu'à partir de PostgreSQL 8.4, vous pouvez obtenir toutes les informations sur les séquences utilisées dans la base de données via:

SELECT * FROM information_schema.sequences;

Puisque j'utilise une version supérieure de PostgreSQL (9.1) et que je cherchais la même réponse haut et bas, j'ai ajouté cette réponse pour la postérité et pour les futurs chercheurs.

Raveren
la source
1
Protip: triez les réponses par "actif". La postérité devient de plus en plus pertinente à mesure que les questions deviennent de plus en plus anciennes ..
raveren
1
Cool. Et il semble que si je choisis la méthode de tri «active», le site se souvient tout de suite du paramètre (ici, je cherchais dans les préférences pour trouver un endroit pour le définir par défaut en vain). Hm, maintenant si seulement nous avions une option "réponse acceptée par le demandeur ne l'emportant pas automatiquement sur tout le reste", ce serait une très grande victoire pour la postérité.
SeldomNeedy
Notez bien que cette table a été introduite dans PG 8.4, je dirais plutôt PG 8.2 après la documentation officielle: postgresql.org/docs/8.2/infoschema-sequences.html
Guillaume Husta
Cette «toutes les informations» n'inclut pas la valeur actuelle.
bart
62

Exécuter:, psql -Epuis\ds


la source
1
je n'ai pas besoin seulement de la liste des séquences, j'ai besoin de la table dans laquelle elle est utilisée, de la valeur suivante etc. Et je dois le faire en SQL
apelliciari
Ensuite, sur chaque séquence, faites \ d <nom> (étant toujours dans psql -E)
encore une fois, ce n'est pas en SQL et ne montre pas à quelle table la séquence est attachée
apelliciari
@avastreg: l'avez-vous exécuté comme je vous l'ai dit? et pourquoi pas?
10
@avastreg: FAITES-LE UNE FOIS . Et il vous montrera les requêtes!
26

après un peu de douleur, je l'ai eu.

la meilleure façon d'y parvenir est de lister toutes les tables

select * from pg_tables where schemaname = '<schema_name>'

puis, pour chaque table, listez toutes les colonnes avec des attributs

select * from information_schema.columns where table_name = '<table_name>'

puis, pour chaque colonne, testez si elle a une séquence

select pg_get_serial_sequence('<table_name>', '<column_name>')

puis, obtenez les informations sur cette séquence

select * from <sequence_name>
apelliciari
la source
13

info de séquence: valeur maximale

SELECT * FROM information_schema.sequences;

info séquence: dernière valeur

SELECT * FROM <sequence_name>

bbh
la source
11

La relation entre les séquences générées automatiquement (telles que celles créées pour les colonnes SERIAL) et la table parent est modélisée par l'attribut de propriétaire de séquence.

Vous pouvez modifier cette relation à l'aide de la clause OWNED BY du ALTER SEQUENCE

par exemple, ALTER SEQUENCE foo_id OWNED par foo_schema.foo_table

pour le définir comme lié à la table foo_table

ou ALTER SEQUENCE foo_id OWNED by NONE

pour rompre la connexion entre la séquence et n'importe quelle table

Les informations sur cette relation sont stockées dans la table de catalogue pg_depend .

la relation de jointure est le lien entre pg_depend.objid -> pg_class.oid WHERE relkind = 'S' - qui relie la séquence à l'enregistrement de jointure, puis pg_depend.refobjid -> pg_class.oid WHERE relkind = 'r', qui relie le joindre l'enregistrement à la relation propriétaire (table)

Cette requête renvoie toutes les dépendances séquence -> table dans une base de données. La clause where le filtre pour n'inclure que les relations générées automatiquement, ce qui le restreint à n'afficher que les séquences créées par les colonnes de type SERIAL.

WITH fq_objects AS (SELECT c.oid,n.nspname || '.' ||c.relname AS fqname , 
                           c.relkind, c.relname AS relation 
                    FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace ),

     sequences AS (SELECT oid,fqname FROM fq_objects WHERE relkind = 'S'),  
     tables    AS (SELECT oid, fqname FROM fq_objects WHERE relkind = 'r' )  
SELECT
       s.fqname AS sequence, 
       '->' as depends, 
       t.fqname AS table 
FROM 
     pg_depend d JOIN sequences s ON s.oid = d.objid  
                 JOIN tables t ON t.oid = d.refobjid  
WHERE 
     d.deptype = 'a' ;
cms
la source
Explication utile des dépendances entre les tables et les séquences. Mais votre requête n'a pas trouvé toutes les séquences pour moi. Il semble que certaines séquences existent sans aucune dépendance.
Evgeny Nozdrev
oui, cette requête ne montre explicitement que les séquences définies par les définitions de colonne série de la base de données. Ceci est expliqué dans la réponse.
cms
5

Je sais que cet article est assez ancien, mais j'ai trouvé la solution de CMS très utile car je cherchais un moyen automatisé de lier une séquence à la table ET à la colonne, et je voulais partager. L'utilisation de la table de catalogue pg_depend était la clé. J'ai développé ce qui a été fait pour:

WITH fq_objects AS (SELECT c.oid,n.nspname || '.' ||c.relname AS fqname ,
                           c.relkind, c.relname AS relation
                    FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace ),

     sequences AS (SELECT oid,fqname FROM fq_objects WHERE relkind = 'S'),
     tables    AS (SELECT oid, fqname FROM fq_objects WHERE relkind = 'r' )
SELECT
       s.fqname AS sequence,
       '->' as depends,
       t.fqname AS table,
       a.attname AS column
FROM
     pg_depend d JOIN sequences s ON s.oid = d.objid
                 JOIN tables t ON t.oid = d.refobjid
                 JOIN pg_attribute a ON a.attrelid = d.refobjid and a.attnum = d.refobjsubid
WHERE
     d.deptype = 'a' ;

Cette version ajoute une colonne à la liste des champs renvoyés. Avec le nom de la table et le nom de la colonne en main, un appel à pg_set_serial_sequence permet de s'assurer facilement que toutes les séquences de la base de données sont correctement définies. Par exemple:

CREATE OR REPLACE FUNCTION public.reset_sequence(tablename text, columnname text)
 RETURNS void
 LANGUAGE plpgsql
AS $function$
DECLARE
    _sql VARCHAR := '';
BEGIN
    _sql := $$SELECT setval( pg_get_serial_sequence('$$ || tablename || $$', '$$ || columnname || $$'), (SELECT COALESCE(MAX($$ || columnname || $$),1) FROM $$ || tablename || $$), true)$$;
    EXECUTE _sql;
END;
$function$;

J'espère que cela aide quelqu'un à réinitialiser les séquences!

DBAYoder
la source
quelques années plus tard, je remarque votre mise à jour et je passe pour donner un vote positif :-)
cms
3

Cette instruction répertorie la table et la colonne associées à chaque séquence:

Code:

    SELECT t.relname as related_table, 
           a.attname as related_column,
           s.relname as sequence_name
    FROM pg_class s 
      JOIN pg_depend d ON d.objid = s.oid 
      JOIN pg_class t ON d.objid = s.oid AND d.refobjid = t.oid 
      JOIN pg_attribute a ON (d.refobjid, d.refobjsubid) = (a.attrelid, a.attnum)
      JOIN pg_namespace n ON n.oid = s.relnamespace 
    WHERE s.relkind     = 'S' 

  AND n.nspname     = 'public'

plus voir ici lien pour répondre


la source
2

Amélioration de la réponse précédente:

select string_agg('select sequence_name, last_value from ' || relname, chr(13) || 'union' || chr(13) order by relname) 
from pg_class where relkind ='S'
Alexandre Ryabov
la source
3
Veuillez ne pas simplement mettre votre code sans aucune explication. De plus, puisque vous avez déclaré que votre code est une "Amélioration de la réponse précédente", vous devez également nous dire POURQUOI il s'agit d'une amélioration. Oh, n'abandonnez pas, et bienvenue dans SO!
Joel
Dois-je écrire une page de texte insensé au lieu d'un code précis (quelques lignes)?
Alexander Ryabov
2
Jamais dit ça. J'aime le code simple et précis. Mais lorsque vous déclarez que votre code est une amélioration, une ou deux lignes expliquant POURQUOI c'est une amélioration (meilleure lisibilité, amélioration des performances, etc.) ne ferait pas de mal. Et vous obtiendrez probablement aussi un +1 de ma part.
Joel
1

Partiellement testé mais semble presque complet.

select *
  from (select n.nspname,c.relname,
               (select substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128)
                  from pg_catalog.pg_attrdef d
                 where d.adrelid=a.attrelid
                   and d.adnum=a.attnum
                   and a.atthasdef) as def
          from pg_class c, pg_attribute a, pg_namespace n
         where c.relkind='r'
           and c.oid=a.attrelid
           and n.oid=c.relnamespace
           and a.atthasdef
           and a.atttypid=20) x
 where x.def ~ '^nextval'
 order by nspname,relname;

Crédit là où le crédit est dû ... il est en partie rétro-ingénierie à partir du SQL enregistré à partir d'un \ d sur une table connue qui avait une séquence. Je suis sûr que cela pourrait aussi être plus propre, mais bon, les performances n'étaient pas un problème.


la source
1

Une sorte de hack, mais essayez ceci:

sélectionnez 'select' '' || relname || '' 'comme séquence, last_value de' || relname || 'union' FROM pg_catalog.pg_class c WHERE c.relkind IN ('S', '');

Supprimer la dernière UNION et exécuter le résultat

jimbob
la source
1

Obtenez des séquences pour chaque colonne de chaque table via l'analyse de la clause DEFAULT. Cette méthode fournit des informations sur les séquences de colonnes liées et n'utilise pas de dépendances qui peuvent ne pas exister pour certaines séquences. Même la pg_get_serial_sequence(sch.nspname||'.'||tbl.relname, col.attname)fonction ne trouve pas toutes les séquences pour moi!

Solution:

SELECT
    seq_sch.nspname  AS sequence_schema
  , seq.relname      AS sequence_name
  , seq_use."schema" AS used_in_schema
  , seq_use."table"  AS used_in_table
  , seq_use."column" AS used_in_column
FROM pg_class seq
  INNER JOIN pg_namespace seq_sch ON seq_sch.oid = seq.relnamespace
  LEFT JOIN (
              SELECT
                  sch.nspname AS "schema"
                , tbl.relname AS "table"
                , col.attname AS "column"
                , regexp_split_to_array(
                      TRIM(LEADING 'nextval(''' FROM
                           TRIM(TRAILING '''::regclass)' FROM
                                pg_get_expr(def.adbin, tbl.oid, TRUE)
                           )
                      )
                      , '\.'
                  )           AS column_sequence
              FROM pg_class tbl --the table
                INNER JOIN pg_namespace sch ON sch.oid = tbl.relnamespace
                --schema
                INNER JOIN pg_attribute col ON col.attrelid = tbl.oid
                --columns
                INNER JOIN pg_attrdef def ON (def.adrelid = tbl.oid AND def.adnum = col.attnum) --default values for columns
              WHERE tbl.relkind = 'r' --regular relations (tables) only
                    AND col.attnum > 0 --regular columns only
                    AND def.adsrc LIKE 'nextval(%)' --sequences only
            ) seq_use ON (seq_use.column_sequence [1] = seq_sch.nspname AND seq_use.column_sequence [2] = seq.relname)
WHERE seq.relkind = 'S' --sequences only
ORDER BY sequence_schema, sequence_name;

Notez qu'une séquence peut être utilisée dans plusieurs tables, elle peut donc être répertoriée sur plusieurs lignes ici.

Evgeny Nozdrev
la source
0

Merci de votre aide.

Voici la fonction pl / pgsql qui met à jour chaque séquence d'une base de données.

---------------------------------------------------------------------------------------------------------
--- Nom : reset_sequence
--- Description : Générique - met à jour les séquences au max de l'identifiant
---------------------------------------------------------------------------------------------------------

CREATE OR REPLACE FUNCTION reset_sequence() RETURNS void AS 
$BODY$
DECLARE _sql VARCHAR := '';
DECLARE result threecol%rowtype; 
BEGIN
FOR result IN 
WITH fq_objects AS (SELECT c.oid,n.nspname || '.' ||c.relname AS fqname ,c.relkind, c.relname AS relation FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace ),
    sequences AS (SELECT oid,fqname FROM fq_objects WHERE relkind = 'S'),
    tables    AS (SELECT oid, fqname FROM fq_objects WHERE relkind = 'r' )
SELECT
       s.fqname AS sequence,
       t.fqname AS table,
       a.attname AS column
FROM
     pg_depend d JOIN sequences s ON s.oid = d.objid
                 JOIN tables t ON t.oid = d.refobjid
                 JOIN pg_attribute a ON a.attrelid = d.refobjid and a.attnum = d.refobjsubid
WHERE
     d.deptype = 'a' 
LOOP
     EXECUTE 'SELECT setval('''||result.col1||''', COALESCE((SELECT MAX('||result.col3||')+1 FROM '||result.col2||'), 1), false);';
END LOOP;
END;$BODY$ LANGUAGE plpgsql;

SELECT * FROM reset_sequence();
Tom Milon
la source
0

En voici un autre qui a le nom du schéma à côté du nom de la séquence

select nspname,relname from pg_class c join pg_namespace n on c.relnamespace=n.oid where relkind = 'S' order by nspname
Robin
la source
0

Cette fonction affiche la dernière_valeur de chaque séquence.

Il génère un tableau à 2 colonnes qui indique le nom de la séquence plus sa dernière valeur générée.

drop function if exists public.show_sequence_stats();
CREATE OR REPLACE FUNCTION public.show_sequence_stats()
    RETURNS TABLE(tablename text, last_value bigint) 
    LANGUAGE 'plpgsql'
    COST 100
    VOLATILE 
    ROWS 1000
AS $BODY$
declare r refcursor; rec record; dynamic_query varchar;
        BEGIN
            dynamic_query='select tablename,last_value from (';
            open r for execute 'select nspname,relname from pg_class c join pg_namespace n on c.relnamespace=n.oid where relkind = ''S'' order by nspname'; 
            fetch next from r into rec;
            while found 
            loop
                dynamic_query=dynamic_query || 'select '''|| rec.nspname || '.' || rec.relname ||''' "tablename",last_value from ' || rec.nspname || '.' || rec.relname || ' union all ';
                fetch next from r into rec; 
            end loop;
            close r; 
            dynamic_query=rtrim(dynamic_query,'union all') || ') x order by last_value desc;';
            return query execute dynamic_query;
        END;
$BODY$;

select * from show_sequence_stats();
UN V
la source
0

En supposant que la exec()fonction déclarée dans cet article https://stackoverflow.com/a/46721603/653539 , les séquences avec leurs dernières valeurs peuvent être récupérées à l'aide d'une seule requête:

select s.sequence_schema, s.sequence_name,
  (select * from exec('select last_value from ' || s.sequence_schema || '.' || s.sequence_name) as e(lv bigint)) last_value
from information_schema.sequences s
Tomáš Záluský
la source
0
select sequence_name, (xpath('/row/last_value/text()', xml_count))[1]::text::int as last_value
from (
    select sequence_schema,
            sequence_name,         
            query_to_xml(format('select last_value from %I.%I', sequence_schema, sequence_name), false, true, '') as xml_count
    from information_schema.sequences
    where sequence_schema = 'public'
) new_table order by last_value desc;
Manuel
la source
0

Voici un exemple comment utiliser psqlpour obtenir une liste de toutes les séquences avec leur last_value:

psql -U <username> -d <database> -t -c "SELECT 'SELECT ''' || c.relname || ''' as sequence_name, last_value FROM ' || c.relname || ';' FROM pg_class c WHERE (c.relkind = 'S')" | psql -U <username> -d <database> -t

éclaboussure
la source