sql >> Databáze >  >> RDS >> PostgreSQL

CHYBA:další data za posledním očekávaným sloupcem při použití PostgreSQL COPY

Prázdný stůl nebude stačit. Potřebujete tabulku, která odpovídá struktuře vstupních dat. Něco jako:

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

tab nemusíte deklarovat jako DELIMITER protože to je výchozí:

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

800 sloupců, říkáte? Tolik sloupců by obvykle znamenalo problém s vaším návrhem. Každopádně existují způsoby, jak napůl automatizovat CREATE TABLE skript.

Automatizace

Za předpokladu zjednodušených nezpracovaných dat

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

Definujte jiný DELIMITER (takový, který se v importovaných datech vůbec nevyskytuje) a import do dočasné pracovní tabulky s jediným textem sloupec:

CREATE TEMP TABLE tmp_data (raw text);

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

Tento dotaz vytvoří CREATE TABLE skript:

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

Obecnější a bezpečnější dotaz:

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

Vrátí:

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

Spustit po ověření platnosti – nebo provést dynamicky, pokud výsledku důvěřujete:

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

Poté INSERT data s tímto dotazem:

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

Nebo jednodušší pomocí translate() :

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

Řetězec je převeden na řádkový literál, přetypován na nově vytvořený typ řádku tabulky a rozložen pomocí (řádek).* .

Vše hotovo.

To vše byste mohli vložit do funkce plpgsql, ale museli byste se chránit před injekcí SQL. (Zde na SO je řada souvisejících řešení. Zkuste hledat.

db<>fiddle zde
Old SQL Fiddle



  1. Jak uložit data na jedné stránce thymeleaf ve více řádcích SQL

  2. Webinář:Nové funkce v Postgres 12 [Následovat]

  3. Jak mohu procházet sadu výsledků MySQL více než jednou pomocí funkcí mysql_*?

  4. Oracle ignoruje chybu neplatného identifikátoru v dílčím dotazu