Et tomt bord duer ikke. Du skal bruge en tabel, der matcher strukturen af inputdata. Noget som:
CREATE TABLE raw_data (
col1 int
, col2 int
...
);
Du behøver ikke at angive tab
som DELIMITER
da det er standard:
COPY raw_data FROM '/home/Projects/TestData/raw_data.txt';
800 kolonner siger du? At mange kolonner typisk vil indikere et problem med dit design. I hvert fald er der måder at halvautomatisere CREATE TABLE
på script.
Automatisering
Forudsat forenklede 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
Definer en anden DELIMITER
(en, der slet ikke forekommer i importdataene), og importer til en midlertidig staging-tabel med en enkelt tekst
kolonne:
CREATE TEMP TABLE tmp_data (raw text);
COPY tmp_data FROM '/home/Projects/TestData/raw_data.txt' WITH (DELIMITER '§');
Denne forespørgsel opretter CREATE TABLE
script:
SELECT 'CREATE TABLE tbl (col' || replace (raw, E'\t', ' bool, col') || ' bool)'
FROM (SELECT raw FROM tmp_data LIMIT 1) t;
En mere generisk og mere sikker forespørgsel:
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);
Returnerer:
CREATE TABLE tbl (col1 bool, col2 bool, col3 bool, col4 bool);
Udfør efter bekræftelse af gyldigheden - eller kør dynamisk, hvis du stoler 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$$;
Derefter INSERT
dataene med denne forespørgsel:
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 enklere med translate()
:
INSERT INTO tbl
SELECT (('(' || translate(raw, E'10\t', 'tf,') || ')')::tbl).*
FROM (SELECT raw FROM tmp_data OFFSET 1) t;
Strengen konverteres til en rækkeliteral, castes til den nyoprettede tabelrækketype og dekomponeres med (række).*
.
Alt færdigt.
Du kan lægge alt dette ind i en plpgsql-funktion, men du skal sikre dig mod SQL-injektion. (Der er en række relaterede løsninger her på SO. Prøv en søgning.
db<>fiddle her
Gammel SQL-violinator