Ett tomt bord duger inte. Du behöver en tabell som matchar strukturen för indata. Något i stil med:
CREATE TABLE raw_data (
col1 int
, col2 int
...
);
Du behöver inte deklarera tab
som DELIMITER
eftersom det är standard:
COPY raw_data FROM '/home/Projects/TestData/raw_data.txt';
800 kolumner säger du? Att många kolumner vanligtvis skulle indikera ett problem med din design. Hur som helst, det finns sätt att halvautomatisera CREATE TABLE
manus.
Automatisering
Förutsatt förenklad rådata
1 2 3 4 -- first row contains "column names"
1 1 0 1 -- tab separated
1 0 0 1
1 0 1 1
Definiera en annan DELIMITER
(en som inte förekommer i importdata alls), och importera till en temporär staging-tabell med en enda text
kolumn:
CREATE TEMP TABLE tmp_data (raw text);
COPY tmp_data FROM '/home/Projects/TestData/raw_data.txt' WITH (DELIMITER '§');
Den här frågan skapar CREATE TABLE
skript:
SELECT 'CREATE TABLE tbl (col' || replace (raw, E'\t', ' bool, col') || ' bool)'
FROM (SELECT raw FROM tmp_data LIMIT 1) t;
En mer allmän och säkrare fråga:
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);
Returnerar:
CREATE TABLE tbl (col1 bool, col2 bool, col3 bool, col4 bool);
Kör efter att ha verifierat giltigheten - eller kör dynamiskt om du litar på resultatet:
DO
$$BEGIN
EXECUTE (
SELECT 'CREATE TABLE tbl (col' || replace(raw, ' ', ' bool, col') || ' bool)'
FROM (SELECT raw FROM tmp_data LIMIT 1) t
);
END$$;
Sedan INSERT
data med denna fråga:
INSERT INTO tbl
SELECT (('(' || replace(replace(replace(
raw
, '1', 't')
, '0', 'f')
, E'\t', ',')
|| ')')::tbl).*
FROM (SELECT raw FROM tmp_data OFFSET 1) t;
Eller enklare med translate()
:
INSERT INTO tbl
SELECT (('(' || translate(raw, E'10\t', 'tf,') || ')')::tbl).*
FROM (SELECT raw FROM tmp_data OFFSET 1) t;
Strängen konverteras till en radliteral, cast till den nyskapade tabellradtypen och dekomponeras med (rad).*
.
Allt klart.
Du kan lägga allt detta i en plpgsql-funktion, men du måste skydda dig mot SQL-injektion. (Det finns ett antal relaterade lösningar här på SO. Försök med en sökning.
db<>fiol här
Gammal SQL-fiol