Празна маса няма да свърши работа. Нуждаете се от таблица, която отговаря на структурата на входните данни. Нещо като:
CREATE TABLE raw_data (
col1 int
, col2 int
...
);
Не е необходимо да декларирате tab
като DELIMITER
тъй като това е по подразбиране:
COPY raw_data FROM '/home/Projects/TestData/raw_data.txt';
800 колони казвате? Толкова много колони обикновено показват проблем с вашия дизайн. Както и да е, има начини за полуавтоматизиране на CREATE TABLE
скрипт.
Автоматизация
Приемайки опростени необработени данни
1 2 3 4 -- first row contains "column names"
1 1 0 1 -- tab separated
1 0 0 1
1 0 1 1
Дефинирайте различен DELIMITER
(това, което изобщо не се среща в данните за импортиране) и импортиране към временна междинна таблица с един текст
колона:
CREATE TEMP TABLE tmp_data (raw text);
COPY tmp_data FROM '/home/Projects/TestData/raw_data.txt' WITH (DELIMITER '§');
Тази заявка създава CREATE TABLE
скрипт:
SELECT 'CREATE TABLE tbl (col' || replace (raw, E'\t', ' bool, col') || ' bool)'
FROM (SELECT raw FROM tmp_data LIMIT 1) t;
По-генерична и по-безопасна заявка:
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);
Връща:
CREATE TABLE tbl (col1 bool, col2 bool, col3 bool, col4 bool);
Изпълнете след проверка на валидността - или изпълнете динамично, ако имате доверие на резултата:
DO
$$BEGIN
EXECUTE (
SELECT 'CREATE TABLE tbl (col' || replace(raw, ' ', ' bool, col') || ' bool)'
FROM (SELECT raw FROM tmp_data LIMIT 1) t
);
END$$;
След това INSERT
данните с тази заявка:
INSERT INTO tbl
SELECT (('(' || replace(replace(replace(
raw
, '1', 't')
, '0', 'f')
, E'\t', ',')
|| ')')::tbl).*
FROM (SELECT raw FROM tmp_data OFFSET 1) t;
Или по-просто с translate()код>
:
INSERT INTO tbl
SELECT (('(' || translate(raw, E'10\t', 'tf,') || ')')::tbl).*
FROM (SELECT raw FROM tmp_data OFFSET 1) t;
Низът се преобразува в литерал на ред, преобразува се в новосъздадения тип ред на таблицата и се разлага с (row).*
.
Готово.
Можете да поставите всичко това във функция plpgsql, но ще трябва да се предпазите срещу SQL инжектиране. (Тук в SO има редица свързани решения. Опитайте с търсене.
db<>fiddle тук
Стар SQL Fiddle