PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

ERREUR :données supplémentaires après la dernière colonne attendue lors de l'utilisation de PostgreSQL COPY

Une table vide ne fera pas l'affaire. Vous avez besoin d'une table qui correspond à la structure des données d'entrée. Quelque chose comme :

CREATE TABLE raw_data (
  col1 int
, col2 int
  ...
);

Vous n'avez pas besoin de déclarer tab comme DELIMITER puisque c'est la valeur par défaut :

COPY raw_data FROM '/home/Projects/TestData/raw_data.txt';

800 colonnes vous dites ? Ce nombre de colonnes indique généralement un problème avec votre conception. Quoi qu'il en soit, il existe des moyens de semi-automatiser le CREATE TABLE script.

Automatisation

En supposant des données brutes simplifiées

1   2   3   4  -- first row contains "column names"
1   1   0   1  -- tab separated
1   0   0   1
1   0   1   1

Définissez un autre DELIMITER (qui n'apparaît pas du tout dans les données d'importation) et importez dans une table intermédiaire temporaire avec un seul text colonne :

CREATE TEMP TABLE tmp_data (raw text);

COPY tmp_data FROM '/home/Projects/TestData/raw_data.txt' WITH (DELIMITER '§');

Cette requête crée le CREATE TABLE script :

SELECT 'CREATE TABLE tbl (col' || replace (raw, E'\t', ' bool, col') || ' bool)'
FROM   (SELECT raw FROM tmp_data LIMIT 1) t;

Une requête plus générique et plus sûre :

SELECT 'CREATE TABLE tbl('
    ||  string_agg(quote_ident('col' || col), ' bool, ' ORDER  BY ord)
    || ' bool);'
FROM  (SELECT raw FROM tmp_data LIMIT 1) t
     , unnest(string_to_array(t.raw, E'\t')) WITH ORDINALITY c(col, ord);

Renvoie :

CREATE TABLE tbl (col1 bool, col2 bool, col3 bool, col4 bool);

Exécuter après avoir vérifié la validité - ou exécuter dynamiquement si vous faites confiance au résultat :

DO
$$BEGIN
EXECUTE (
   SELECT 'CREATE TABLE tbl (col' || replace(raw, ' ', ' bool, col') || ' bool)'
   FROM  (SELECT raw FROM tmp_data LIMIT 1) t
   );
END$$;

Puis INSERT les données avec cette requête :

INSERT INTO tbl
SELECT (('(' || replace(replace(replace(
                  raw
                , '1',   't')
                , '0',   'f')
                , E'\t', ',')
             || ')')::tbl).*
FROM   (SELECT raw FROM tmp_data OFFSET 1) t;

Ou plus simple avec translate() :

INSERT INTO tbl
SELECT (('(' || translate(raw, E'10\t', 'tf,') || ')')::tbl).*
FROM   (SELECT raw FROM tmp_data OFFSET 1) t;

La chaîne est convertie en un littéral de ligne, convertie en type de ligne de tableau nouvellement créé et décomposée avec (row).* .

Tout est fait.

Vous pouvez mettre tout cela dans une fonction plpgsql, mais vous devez vous protéger contre l'injection SQL. (Il existe un certain nombre de solutions connexes ici sur SO. Essayez une recherche.

db<>violon ici
Ancien violon SQL