web-dev-qa-db-fra.com

Exporter des lignes spécifiques d'une table PostgreSQL en tant que script INSERT SQL

J'ai un schéma de base de données nommé: nyummy et une table nommée cimory:

create table nyummy.cimory (
  id numeric(10,0) not null,
  name character varying(60) not null,
  city character varying(50) not null,
  CONSTRAINT cimory_pkey PRIMARY KEY (id)
);

Je souhaite exporter les données de la table cimory en tant que fichier de script SQL d'insertion. Cependant, je souhaite uniquement exporter les enregistrements/données où la ville est égale à 'tokyo' (en supposant que les données de la ville sont toutes en minuscules).

Comment faire? 

Peu importe que la solution soit dans des outils d'interface graphique gratuits ou en ligne de commande (bien que la solution d'outils d'interface graphique soit meilleure). J'ai essayé pgAdmin III, mais je ne trouve pas d'option pour le faire.

137
null

Créez une table avec l'ensemble que vous souhaitez exporter, puis utilisez l'utilitaire de ligne de commande pg_dump pour exporter dans un fichier:

create table export_table as 
select id, name, city
from nyummy.cimory
where city = 'tokyo'
$ pg_dump --table=export_table --data-only --column-inserts my_database > data.sql

--column-inserts affichera des commandes d’insertion avec des noms de colonnes.

--data-only ne pas vider le schéma.

Comme indiqué ci-dessous, la création d'une vue dans une table évite la création d'une table chaque fois qu'une nouvelle exportation est nécessaire.

226
Clodoaldo Neto

Pour une exportation data-only, utilisez COPY .
Vous obtenez un fichier avec une ligne de tableau par ligne sous forme de texte brut (pas de commandes INSERT), il est plus petit et plus rapide:

COPY (SELECT * FROM nyummy.cimory WHERE city = 'tokio') TO '/path/to/file.csv';

Import identique dans une autre table de la même structure n'importe où avec:

COPY other_tbl FROM '/path/to/file.csv';

COPY écrit et lit les fichiers local du serveur, contrairement aux programmes clients tels que pg_dump ou psql qui lisent et écrivent des fichiers local du client. Si les deux fonctionnent sur le même ordinateur, peu importe, mais c'est le cas pour les connexions à distance.

Il y a aussi la commande \copy de psql that:

Effectue une copie frontale (client). C'est une opération qui exécute un Commande SQL COPY, mais au lieu que le serveur lise ou écrit le fichier Le fichier spécifié, psql lit ou écrit le fichier et achemine les données entre le serveur et le système de fichiers local. Cela signifie que le fichier l'accessibilité et les privilèges sont ceux de l'utilisateur local, pas le serveur, et aucun privilège de superutilisateur SQL n'est requis.

142
Erwin Brandstetter

Ceci est un simple et rapide moyen de exporter une table dans un script avec pgAdmin manuellement sans installations supplémentaires:

  1. Faites un clic droit sur la table cible et sélectionnez "Sauvegarder".
  2. Sélectionnez un chemin de fichier pour stocker la sauvegarde. En tant que format, choisissez "Plaine".
  3. Ouvrez l'onglet "Options de vidage # 2" en bas et cochez la case "Utiliser les inserts de colonne".
  4. Cliquez sur le bouton Sauvegarde. 
  5. Si vous ouvrez le fichier résultant avec un lecteur de texte (par exemple, le bloc-notes ++), vous obtenez un script pour créer la table entière. De là, vous pouvez simplement copier les instructions INSERT générées. 

Cette méthode fonctionne également avec la technique de création d'une table export_ comme indiqué dans la réponse de @Clodoaldo Neto.

 Click right on target table and choose "Backup"

 Choose a destination path and change the format to "Plain"

 Open the tab "Dump Options #2" at the bottom and check "Use Column Inserts"

 You can copy the INSERT Statements from there.

20
Andi R

SQL Workbench a une telle fonctionnalité.

Après avoir exécuté une requête, faites un clic droit sur les résultats de la requête et choisissez "Copier les données en tant que SQL> Insertion SQL".

9
machinery

Pour mon cas d'utilisation, je pouvais simplement me connecter à grep.

pg_dump -U user_name --data-only --column-inserts -t nyummy.cimory | grep "tokyo" > tokyo.sql
6
M.Vanderlee

Vous pouvez afficher la table avec des enregistrements spécifiques, puis vider le fichier SQL

CREATE VIEW foo AS
SELECT id,name,city FROM nyummy.cimory WHERE city = 'tokyo'
2

J'ai essayé d'écrire une procédure faisant cela, basée sur les codes @PhilHibbs, d'une manière différente… .. Veuillez regarder et tester.

 CREATE OR REPLACE FUNCTION dump(IN p_schema text, IN p_table text, IN p_where text)
   RETURNS setof text AS
 $BODY$
 DECLARE
     dumpquery_0 text;
     dumpquery_1 text;
     selquery text;
     selvalue text;
     valrec record;
     colrec record;
 BEGIN

     -- ------ --
     -- GLOBAL --
     --   build base INSERT
     --   build SELECT array[ ... ]
     dumpquery_0 := 'INSERT INTO ' ||  quote_ident(p_schema) || '.' || quote_ident(p_table) || '(';
     selquery    := 'SELECT array[';

     <<label0>>
     FOR colrec IN SELECT table_schema, table_name, column_name, data_type
                   FROM information_schema.columns
                   WHERE table_name = p_table and table_schema = p_schema
                   ORDER BY ordinal_position
     LOOP
         dumpquery_0 := dumpquery_0 || quote_ident(colrec.column_name) || ',';
         selquery    := selquery    || 'CAST(' || quote_ident(colrec.column_name) || ' AS TEXT),';
     END LOOP label0;

     dumpquery_0 := substring(dumpquery_0 ,1,length(dumpquery_0)-1) || ')';
     dumpquery_0 := dumpquery_0 || ' VALUES (';
     selquery    := substring(selquery    ,1,length(selquery)-1)    || '] AS MYARRAY';
     selquery    := selquery    || ' FROM ' ||quote_ident(p_schema)||'.'||quote_ident(p_table);
     selquery    := selquery    || ' WHERE '||p_where;
     -- GLOBAL --
     -- ------ --

     -- ----------- --
     -- SELECT LOOP --
     --   execute SELECT built and loop on each row
     <<label1>>
     FOR valrec IN  EXECUTE  selquery
     LOOP
         dumpquery_1 := '';
         IF not found THEN
             EXIT ;
         END IF;

         -- ----------- --
         -- LOOP ARRAY (EACH FIELDS) --
         <<label2>>
         FOREACH selvalue in ARRAY valrec.MYARRAY
         LOOP
             IF selvalue IS NULL
             THEN selvalue := 'NULL';
             ELSE selvalue := quote_literal(selvalue);
             END IF;
             dumpquery_1 := dumpquery_1 || selvalue || ',';
         END LOOP label2;
         dumpquery_1 := substring(dumpquery_1 ,1,length(dumpquery_1)-1) || ');';
         -- LOOP ARRAY (EACH FIELD) --
         -- ----------- --

         -- debug: RETURN NEXT dumpquery_0 || dumpquery_1 || ' --' || selquery;
         -- debug: RETURN NEXT selquery;
         RETURN NEXT dumpquery_0 || dumpquery_1;

     END LOOP label1 ;
     -- SELECT LOOP --
     -- ----------- --

 RETURN ;
 END
 $BODY$
   LANGUAGE plpgsql VOLATILE;

Et alors :

-- for a range
SELECT dump('public', 'my_table','my_id between 123456 and 123459'); 
-- for the entire table
SELECT dump('public', 'my_table','true');

testé sur mon postgres 9.1, avec une table avec un type de données de champ mixte (texte, double, int, horodatage sans fuseau horaire, etc.).

C’est pourquoi le type CAST in TEXT est nécessaire . Mon test s’exécute correctement pendant environ 9 millions de lignes et semble échouer juste avant 18 minutes d’exécution.

ps: J'ai trouvé un équivalent pour MySQL sur le WEB.

0
Vi Shen

Je viens de mettre au point une procédure rapide pour le faire. Cela ne fonctionne que pour une seule ligne, je crée donc une vue temporaire qui sélectionne simplement la ligne que je veux, puis remplace la page pg_temp.temp_view par la table dans laquelle je veux insérer.

CREATE OR REPLACE FUNCTION dv_util.gen_insert_statement(IN p_schema text, IN p_table text)
  RETURNS text AS
$BODY$
DECLARE
    selquery text; 
    valquery text; 
    selvalue text; 
    colvalue text; 
    colrec record;
BEGIN

    selquery := 'INSERT INTO ' ||  quote_ident(p_schema) || '.' || quote_ident(p_table);

    selquery := selquery || '(';

    valquery := ' VALUES (';
    FOR colrec IN SELECT table_schema, table_name, column_name, data_type
                  FROM information_schema.columns 
                  WHERE table_name = p_table and table_schema = p_schema 
                  ORDER BY ordinal_position 
    LOOP
      selquery := selquery || quote_ident(colrec.column_name) || ',';

      selvalue := 
        'SELECT CASE WHEN ' || quote_ident(colrec.column_name) || ' IS NULL' || 
                   ' THEN ''NULL''' || 
                   ' ELSE '''' || quote_literal('|| quote_ident(colrec.column_name) || ')::text || ''''' || 
                   ' END' || 
        ' FROM '||quote_ident(p_schema)||'.'||quote_ident(p_table);
      EXECUTE selvalue INTO colvalue;
      valquery := valquery || colvalue || ',';
    END LOOP;
    -- Replace the last , with a )
    selquery := substring(selquery,1,length(selquery)-1) || ')';
    valquery := substring(valquery,1,length(valquery)-1) || ')';

    selquery := selquery || valquery;

RETURN selquery;
END
$BODY$
  LANGUAGE plpgsql VOLATILE;

Invoqué ainsi:

SELECT distinct dv_util.gen_insert_statement('pg_temp_' || sess_id::text,'my_data') 
from pg_stat_activity 
where procpid = pg_backend_pid()

Je n'ai pas testé cela contre les attaques par injection, merci de me prévenir si l'appel quote_literal n'est pas suffisant pour cela.

En outre, cela ne fonctionne que pour les colonnes qui peuvent être simplement converties en :: text et inversement.

C'est aussi pour Greenplum, mais je ne vois pas pourquoi cela ne fonctionnerait pas avec Postgres, CMIIW.

0
PhilHibbs