Uma mesa vazia não serve. Você precisa de uma tabela que corresponda à estrutura dos dados de entrada. Algo como:
CREATE TABLE raw_data (
col1 int
, col2 int
...
);
Você não precisa declarar
tab
como DELIMITER
já que esse é o padrão:COPY raw_data FROM '/home/Projects/TestData/raw_data.txt';
800 colunas você diz? Essa quantidade de colunas normalmente indicaria um problema com seu design. De qualquer forma, existem maneiras de automatizar parcialmente o
CREATE TABLE
roteiro. Automação
Presumindo dados 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
Defina um
DELIMITER
diferente (uma que não ocorre nos dados de importação) e importe para uma tabela de preparação temporária com um único text
coluna:CREATE TEMP TABLE tmp_data (raw text);
COPY tmp_data FROM '/home/Projects/TestData/raw_data.txt' WITH (DELIMITER '§');
Esta consulta cria o
CREATE TABLE
roteiro:SELECT 'CREATE TABLE tbl (col' || replace (raw, E'\t', ' bool, col') || ' bool)'
FROM (SELECT raw FROM tmp_data LIMIT 1) t;
Uma consulta mais genérica e 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);
Devoluções:
CREATE TABLE tbl (col1 bool, col2 bool, col3 bool, col4 bool);
Execute após verificar a validade - ou execute dinamicamente se você confiar no resultado:
DO
$$BEGIN
EXECUTE (
SELECT 'CREATE TABLE tbl (col' || replace(raw, ' ', ' bool, col') || ' bool)'
FROM (SELECT raw FROM tmp_data LIMIT 1) t
);
END$$;
Então
INSERIR
os dados com 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;
Ou mais simples com
translate()
:INSERT INTO tbl
SELECT (('(' || translate(raw, E'10\t', 'tf,') || ')')::tbl).*
FROM (SELECT raw FROM tmp_data OFFSET 1) t;
A string é convertida em um literal de linha, convertida para o tipo de linha da tabela recém-criada e decomposta com
(row).*
. Tudo feito.
Você poderia colocar tudo isso em uma função plpgsql, mas precisaria se proteger contra injeção de SQL. (Existem várias soluções relacionadas aqui no SO. Tente pesquisar.
db<>fiddle aqui
Velha violino SQL