PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

ERRORE:dati extra dopo l'ultima colonna prevista quando si utilizza PostgreSQL COPY

Un tavolo vuoto non va bene. Hai bisogno di una tabella che corrisponda alla struttura dei dati di input. Qualcosa come:

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

Non è necessario dichiarare tab come DELIMITER poiché è l'impostazione predefinita:

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

800 colonne dici? Quelle colonne in genere indicherebbero un problema con il tuo design. Ad ogni modo, ci sono modi per automatizzare a metà il CREATE TABLE copione.

Automazione

Assumendo dati grezzi semplificati

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

Definisci un diverso DELIMITER (uno che non si trova affatto nei dati di importazione) e importare in una tabella di staging temporanea con un singolo text colonna:

CREATE TEMP TABLE tmp_data (raw text);

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

Questa query crea il CREATE TABLE sceneggiatura:

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

Una query più generica e più sicura:

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);

Resi:

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

Esegui dopo aver verificato la validità o esegui dinamicamente se ritieni attendibile il risultato:

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

Quindi INSERT i dati con questa query:

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

O più semplice con translate() :

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

La stringa viene convertita in una riga letterale, convertita nel tipo di riga della tabella appena creato e scomposta con (row).* .

Tutto fatto.

Potresti mettere tutto questo in una funzione plpgsql, ma dovresti proteggerti dall'iniezione SQL. (Ci sono una serie di soluzioni correlate qui su SO. Prova una ricerca.

db<>violino qui
Old SQL Fiddle