postgresql: INSERT INTO… (SELECT *…)

125

Je ne sais pas si son SQL standard:

 INSERT INTO tblA 
 (SELECT id, time 
    FROM tblB 
   WHERE time > 1000)  

Ce que je recherche, c'est: que se passe-t-il si tblA et tblB sont dans des serveurs DB différents .

PostgreSql donne-t-il des utilitaires ou des fonctionnalités qui aideront à utiliser INSERT query with PGresult struct

Je veux dire que je SELECT id, time FROM tblB ...reviendrai PGresult*sur l'utilisation PQexec. Est-il possible d'utiliser cette structure dans une autre PQexecpour exécuter une commande INSERT.

EDIT:
Si ce n'est pas possible, j'irais pour extraire les valeurs de PQresult * et créer une syntaxe d'instruction INSERT multiple comme:

INSERT INTO films (code, title, did, date_prod, kind) VALUES
    ('B6717', 'Tampopo', 110, '1985-02-10', 'Comedy'),
    ('HG120', 'The Dinner Game', 140, DEFAULT, 'Comedy'); 

Est-il possible de créer une déclaration préparée à partir de cela !! :(

Mayank
la source
Je ne sais pas si la syntaxe INSERT que vous avez postée est ANSI, mais elle est largement supportée (Oracle, MySQL, SQL Server, SQLite ...). Mais les crochets ne sont pas nécessaires.
OMG Ponies

Réponses:

152

Comme Henrik l'a écrit, vous pouvez utiliser dblink pour connecter la base de données distante et récupérer le résultat. Par exemple:

psql dbtest
CREATE TABLE tblB (id serial, time integer);
INSERT INTO tblB (time) VALUES (5000), (2000);

psql postgres
CREATE TABLE tblA (id serial, time integer);

INSERT INTO tblA
    SELECT id, time 
    FROM dblink('dbname=dbtest', 'SELECT id, time FROM tblB')
    AS t(id integer, time integer)
    WHERE time > 1000;

TABLE tblA;
 id | time 
----+------
  1 | 5000
  2 | 2000
(2 rows)

PostgreSQL a un pseudo-type d' enregistrement (uniquement pour l'argument de la fonction ou le type de résultat), qui vous permet d'interroger les données d'une autre table (inconnue).

Éditer:

Vous pouvez en faire une déclaration préparée si vous le souhaitez et cela fonctionne également:

PREPARE migrate_data (integer) AS
INSERT INTO tblA
    SELECT id, time
    FROM dblink('dbname=dbtest', 'SELECT id, time FROM tblB')
    AS t(id integer, time integer)
    WHERE time > $1;

EXECUTE migrate_data(1000);
-- DEALLOCATE migrate_data;

Edit (ouais, un autre):

Je viens de voir votre question révisée (fermée en double ou tout simplement très similaire à celle-ci).

Si ma compréhension est correcte (postgres a tbla et dbtest a tblb et vous voulez une insertion à distance avec une sélection locale , pas une sélection à distance avec une insertion locale comme ci-dessus):

psql dbtest

SELECT dblink_exec
(
    'dbname=postgres',
    'INSERT INTO tbla
        SELECT id, time
        FROM dblink
        (
            ''dbname=dbtest'',
            ''SELECT id, time FROM tblb''
        )
        AS t(id integer, time integer)
        WHERE time > 1000;'
);

Je n'aime pas ce dblink imbriqué, mais AFAIK je ne peux pas faire référence à tblB dans le corps de dblink_exec . Utilisez LIMIT pour spécifier les 20 premières lignes, mais je pense que vous devez d'abord les trier en utilisant la clause ORDER BY.

Grzegorz Szpetkowski
la source
1
Merci pour votre réponse. Eh bien, une dernière question rapide ... INSERT INTO tblA SELECT id, time FROM dblink('dbname=dbtest', 'SELECT id, time FROM tblB') AS t(id integer, time integer) WHERE time > 1000; Puis-je faire une déclaration préparée à partir de cela?
Mayank
Salut @ grzegorz-szpetkowski, Cette logique donne une erreur: ERREUR: le mot de passe est requis DÉTAIL: Les non-super-utilisateurs doivent fournir un mot de passe dans la chaîne de connexion.
Neel Darji
34

Si vous voulez insérer dans la colonne spécifiée:

INSERT INTO table (time)
(SELECT time FROM 
    dblink('dbname=dbtest', 'SELECT time FROM tblB') AS t(time integer) 
    WHERE time > 1000
);
Piotr Olaszewski
la source
9

Vous pouvez utiliser dblink pour créer une vue résolue dans une autre base de données. Cette base de données peut être sur un autre serveur.

Hendrik Brummermann
la source
Merci pour la réponse. Mais je n'ai pas compris comment INSERT INTO ... (SELECT FROM ...)fonctionnera avec dblink. Ce dont j'ai besoin est INSERT INTO ...d'être exécuté en session dblink vers un autre serveur DB, mais (SELECT FROM ...)dans ma session actuelle.
Mayank
Vous définissez simplement tblA comme une vue soutenue par dblink. Ainsi, les insertions, mises à jour, suppressions se feront dans l'autre base de données. dblink n'est pas en lecture seule.
Hendrik Brummermann
9

Cette notation (vue pour la première fois ici ) semble également utile:

insert into postagem (
  resumopostagem,
  textopostagem,
  dtliberacaopostagem,
  idmediaimgpostagem,
  idcatolico,
  idminisermao,
  idtipopostagem
) select
  resumominisermao,
  textominisermao,
  diaminisermao,
  idmediaimgminisermao,
  idcatolico ,
  idminisermao,
  1
from
  minisermao    
Sombriks
la source
2
Cela ne fonctionne que lorsque les tables sont dans la même base de données. La question concerne la copie de données à partir d'une table dans une autre base de données .
Nitin Nain
2
insert into TABLENAMEA (A,B,C,D) 
select A::integer,B,C,D from TABLENAMEB
mahesh ingale
la source
1

Voici une solution alternative, sans utiliser dblink.

Supposons que B représente la base de données source et A représente la base de données cible: Ensuite,

  1. Copier la table du DB source vers le DB cible:

    pg_dump -t <source_table> <source_db> | psql <target_db>
  2. Ouvrez l'invite psql, connectez-vous à target_db et utilisez un simple insert:

    psql
    # \c <target_db>;
    # INSERT INTO <target_table>(id, x, y) SELECT id, x, y FROM <source_table>;
  3. À la fin, supprimez la copie de source_table que vous avez créée dans target_table .

    # DROP TABLE <source_table>;
Nitin Nain
la source