Saltar al contenido

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

Este artículo ha sido aprobado por especialistas así se asegura la veracidad de nuestra esta división.

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 valor 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 sin procesar 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 un diferente DELIMITER (uno que no ocurre en absoluto 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);

Ejecutar después de verificar la validez, o ejecutar 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$$;

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

El string se convierte en un literal de fila, se convierte en el tipo de fila de tabla recién creado 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

Tienes la posibilidad dar visibilidad a este tutorial si si solucionó tu problema.

¡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 *