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

FEJL:ekstra data efter sidste forventede kolonne ved brug af PostgreSQL COPY

Et tomt bord duer ikke. Du skal bruge en tabel, der matcher strukturen af ​​inputdata. Noget som:

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

Du behøver ikke at angive tab som DELIMITER da det er standard:

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

800 kolonner siger du? At mange kolonner typisk vil indikere et problem med dit design. I hvert fald er der måder at halvautomatisere CREATE TABLE på script.

Automatisering

Forudsat forenklede rådata

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

Definer en anden DELIMITER (en, der slet ikke forekommer i importdataene), og importer til en midlertidig staging-tabel med en enkelt tekst kolonne:

CREATE TEMP TABLE tmp_data (raw text);

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

Denne forespørgsel opretter CREATE TABLE script:

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

En mere generisk og mere sikker forespørgsel:

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

Returnerer:

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

Udfør efter bekræftelse af gyldigheden - eller kør dynamisk, hvis du stoler på resultatet:

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

Derefter INSERT dataene med denne forespørgsel:

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

Eller enklere med translate() :

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

Strengen konverteres til en rækkeliteral, castes til den nyoprettede tabelrækketype og dekomponeres med (række).* .

Alt færdigt.

Du kan lægge alt dette ind i en plpgsql-funktion, men du skal sikre dig mod SQL-injektion. (Der er en række relaterede løsninger her på SO. Prøv en søgning.

db<>fiddle her
Gammel SQL-violinator



  1. Sådan komprimeres og repareres en database automatisk i Access 2016

  2. Brug af arraynøgler og værdier til at oprette sql select-sætning

  3. Oracle-indstilling pr. brugers standardskema (ændrer ikke en session)

  4. PostgreSQL newline karakter