sql >> Database >  >> RDS >> PostgreSQL

FOUT:extra gegevens na de laatst verwachte kolom bij gebruik van PostgreSQL COPY

Een lege tafel is niet voldoende. U hebt een tabel nodig die overeenkomt met de structuur van invoergegevens. Iets als:

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

U hoeft tab niet te declareren als DELIMITER aangezien dat de standaard is:

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

800 kolommen zegt u? Zoveel kolommen duiden doorgaans op een probleem met uw ontwerp. Hoe dan ook, er zijn manieren om de CREATE TABLE voor de helft te automatiseren schrift.

Automatisering

Uitgaande van vereenvoudigde onbewerkte gegevens

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

Definieer een andere DELIMITER (een die helemaal niet voorkomt in de importgegevens), en importeer naar een tijdelijke verzameltabel met een enkele text kolom:

CREATE TEMP TABLE tmp_data (raw text);

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

Deze query maakt de CREATE TABLE script:

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

Een meer algemene en veiligere vraag:

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

Retourneren:

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

Uitvoeren na verificatie van de geldigheid - of dynamisch uitvoeren als u het resultaat vertrouwt:

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

Dan INSERT de gegevens met deze vraag:

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

Of eenvoudiger met translate() :

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

De tekenreeks wordt geconverteerd naar een letterlijke rij, gecast naar het nieuw gemaakte tabelrijtype en ontleed met (row).* .

Alles klaar.

Je zou dat allemaal in een plpgsql-functie kunnen stoppen, maar je zou je moeten beschermen tegen SQL-injectie. (Er zijn een aantal gerelateerde oplossingen hier op SO. Probeer een zoekopdracht.

db<>fiddle hier
Oude SQL Fiddle



  1. PHP $stmt->num_rows werkt niet met voorbereide instructies

  2. MySQL Groeperen op datums tussen

  3. Oracle-tabel of weergave bestaat niet vanuit de opgeslagen procedure

  4. PhalconPHP-database sluit zich aan bij ORM