Saltar al contenido

“ERROR: datos adicionales después de la última columna esperada” cuando se usa PostgreSQL COPY

Solución:

Una mesa vacía no servirá. Necesita una tabla que coincida con la estructura de los datos de entrada. Algo como:

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

No necesitas declarar tab como DELIMITER ya que ese es el predeterminado:

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

800 columnas dices? Esa cantidad de columnas normalmente indicaría un problema con su diseño. De todos modos, hay formas de automatizar a medias el CREATE TABLE texto.

Automatización

Suponiendo datos brutos simplificados

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

Definir una diferente DELIMITER (uno que no ocurre en los datos de importación), e importar a una tabla de preparación temporal con un solo text columna:

CREATE TEMP TABLE tmp_data (raw text);

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

Esta consulta crea el CREATE TABLE texto:

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

Una consulta más genérica y segura:

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

Devoluciones:

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

Ejecute después de verificar la validez, o ejecute dinámicamente si confía en el resultado:

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

Luego INSERT los datos con esta consulta:

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 más simple con translate():

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

La cadena se convierte en un literal de fila, se convierte en el tipo de fila de la tabla recién creada y se descompone con (row).*.

Todo listo.

Podría poner todo eso en una función plpgsql, pero necesitaría protegerse contra la inyección de SQL. (Hay una serie de soluciones relacionadas aquí en SO. Intente una búsqueda.

db <> violín aquí

Viejo violín SQL

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *