PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

FEHLER:Zusätzliche Daten nach der letzten erwarteten Spalte bei Verwendung von PostgreSQL COPY

Ein leerer Tisch geht nicht. Sie benötigen eine Tabelle, die der Struktur der Eingabedaten entspricht. Etwas wie:

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

Sie müssen tab nicht deklarieren als DELIMITER da dies die Vorgabe ist:

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

800 Spalten sagst du? So viele Spalten weisen normalerweise auf ein Problem mit Ihrem Design hin. Wie auch immer, es gibt Möglichkeiten, das CREATE TABLE halb zu automatisieren Skript.

Automatisierung

Unter Annahme vereinfachter Rohdaten

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

Definieren Sie ein anderes DELIMITER (einer, der in den Importdaten überhaupt nicht vorkommt) und mit einem einzigen text in eine temporäre Staging-Tabelle importieren Spalte:

CREATE TEMP TABLE tmp_data (raw text);

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

Diese Abfrage erstellt die CREATE TABLE Skript:

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

Eine allgemeinere und sicherere Abfrage:

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

Rückgabe:

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

Nach Überprüfung der Gültigkeit ausführen - oder dynamisch ausführen, wenn Sie dem Ergebnis vertrauen:

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

Dann INSERT die Daten mit dieser Abfrage:

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

Oder einfacher mit translate() :

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

Der String wird in ein Zeilenliteral konvertiert, in den neu erstellten Tabellenzeilentyp umgewandelt und mit (row).* zerlegt .

Alles erledigt.

Sie könnten all das in eine plpgsql-Funktion packen, aber Sie müssten sich gegen SQL-Injection schützen. (Hier auf SO gibt es eine Reihe verwandter Lösungen. Versuchen Sie es mit einer Suche.

db<>fiddle hier
Alte SQL-Geige