Как получить данные из определенного столбца файла .tsv в столбец массива Postgres? Является ли регулярное выражение правильным инструментом, или я должен искать другой подход? - PullRequest
2 голосов
/ 08 октября 2019

Обзор:

1) У меня есть дамп .TSV [значения, разделенные табуляцией, плоский файл], который мне нужно шунтировать в существующую таблицу Postgres [таблица не моего дизайна, но у меня есть прямойdb access] Я намерен использовать psql \ copy для этого;

2) В рассматриваемой таблице один столбец определен как одномерный массив [исторические причины, которые я не контролирую;я знаю, что денормализованные столбцы в большинстве случаев далеки от оптимальных.]

3) В TSV пятый столбец содержит значения, предназначенные для столбца массива Postgres;эти значения разделены запятыми

4) Чтобы получить эти значения в столбце массива postgres, я считаю, что мне нужно обернуть их следующим шаблоном: ‘{}’ - таким, что значения: foo,barстать ‘{foo,bar}’

Я предполагаю, что лучший способ сделать это с помощью регулярных выражений, но мои навыки в этой области в настоящее время очень слабы [работаю над этим, имейте книгу совы и поваренную книгу регулярных выражений! ]. Я прочитал несколько связанных SO / вопросов / ответов, просмотрел rexegg и regex101, но не могу найти здесь или в другом месте информацию, которая описывает достаточно похожую ситуацию, чтобы я мог использовать в ней решения / подходы.

Есть несколько конкретных аспектов этой проблемы, на которых я застрял:

a) Поскольку данные в каждом столбце имеют переменную длину [то есть каждое значение может быть любой длины],Я не могу понять, как определить пятую колонку [чтобы действовать в соответствии с ней];

b) Точно так же мне нужно добавить '{к началу столбца и}' к его концу, но, поскольку данные в столбце изменчивы по содержанию и длине, я не могу понять,как с этим справиться - например, я не могу сказать регулярному выражению искать какой-либо конкретный символ или длину, чтобы вызвать добавление скобок / кавычек в правильной позиции

Ниже приведен пример строки изфайл .tsv;пятый столбец с запятой, разделяющей два значения, - это то, над чем я пытаюсь действовать.

1234    e@mail.addy 43210   0123456789  foo_value,bar_value 107.00 0.00 timestamp_1 timestamp_2 54321   string_2    string_3    timestamp_3 98765   12345   US  Hawaii  string_4    string_5    string_6    string_7    string_8    false true  false

когда регулярное выражение [или любое другое преобразование работает] выполнено с ним, оно должно [я думаю] выглядеть следующим образом:

1234    e@mail.addy 43210   0123456789  ’{foo_value,bar_value}’ 107.00 0.00 timestamp_1 timestamp_2 54321   string_2    string_3    timestamp_3 98765   12345   US  Hawaii  string_4    string_5    string_6    string_7    string_8    false true  false

, чтобы столбец массива Postgres распозналвход как действительный массив.

Вот определение таблицы Postgres:

CREATE TABLE postgres_table (
  col1 SERIAL PRIMARY KEY,
  col2 TEXT,
  col3 TEXT, 
  col4 TEXT,
  col5 TEXT[], /*this is the array column*/
  col6 NUMERIC(19,2) NOT NULL,
  col7 NUMERIC(19,2),
  col8 TIMESTAMP WITHOUT TIME ZONE,
  col9 TIMESTAMP WITHOUT TIME ZONE,
  col10 TEXT,
  col11 TEXT,
  col12 TEXT,
  col13 TIMESTAMP WITHOUT TIME ZONE DEFAULT now(),
  col14 TEXT,
  col15 TEXT,
  col16 TEXT,
  col17 TEXT,
  col18 TEXT,
  col19 TEXT,
  col20 TEXT,
  col21 TEXT,
  col22 TEXT,
  col23 BOOLEAN,
  col24 BOOLEAN,
  col25 BOOLEAN
);

дополнительные примечания: db - это Postgres 11;все задействованные системы основаны на RHEL;Я немного знаю JavaScript и SQL, но мои попытки до сих пор были в bash, я успешно использовал \ copy для многих дампов типа .csv в прошлом [но никогда в денормализованную таблицу], и я подумал, что нижеТаким образом, вопрос мог бы получить большую часть пути, но я получаю ошибку “substitution failed”, когда пытаюсь адаптировать решение к моей ситуации: Как я могу получить все данные из определенного столбца?

Чтобы уточнить: я стремлюсь использовать любой подход, который работает, поэтому, если есть лучший способ сделать это, чем регулярное выражение, я весь слух! Все рекомендации очень ценятся.


РЕДАКТИРОВАТЬ:

Большое спасибо @James Brown и @jjanes - мне еще не хватает «очков репутации», чтобы поднять ваши ответы, но я обязательно сделаю этокак только смогу.

Решение awk от @James Brown ниже работало для меня - обработанный файл содержал завершающий перевод строки, который мне пришлось запрограммировать на сценарий awk из этого unix.SE вопрос: https://unix.stackexchange.com/questions/140727/how-can-i-delete-a-trailing-newline-in-bash

Я рад, что у меня появился стимул, чтобы выучить еще несколько awk;какой отличный инструмент.

ОБНОВЛЕНИЕ: Я все еще работаю над тем, чтобы решение @jjanes работало для моего собственного назидания - все еще сталкиваюсь с проблемами разрешений после подхода GRANT TEMP, но я подозреваю, что это как-то связано с тем, как RDS [гдебаза данных размещена] обрабатывает удаленные запросы psql - обновится снова, когда он у меня будет работать // - я попробовал решение @jjanes ниже, но столкнулся со следующей проблемой на шаге \copy: ERROR: permission denied for schema pg_temp_5

вот вывод \z pg_temp_5.*: Access privileges Schema | Name | Type | Access privileges | Column access privileges -----------+--------+-------+-------------------+-------------------------- pg_temp_5 | foobar | table | user1=arwdDxt/user1 |

Я пробовал: GRANT USAGE ON SCHEMA pg_temp_5 TO user1; GRANT ALL ON SCHEMA pg_temp_5 TO user1; ALTER TABLE pg_temp_5.foobar OWNER TO user1;, как предложено в следующемg SF вопрос, но без игры в кости https://serverfault.com/questions/488669/postgres-insert-error-permission-denied-for-schema-public

Я не вижу причин, почему это не должно сработать, настоящий скребок головы.

Ответы [ 2 ]

0 голосов
/ 08 октября 2019

Использование для этого awk. Ваши данные с завихрением в пятом поле (я оставил для вас timestamp_[123] s, но исправил пропущенные вкладки):

$ cat data
1234    e@mail.addy 43210   0123456789  foo_value,bar_value 107.00  0.00    timestamp_1 timestamp_2 54321   string_2    string_3    timestamp_3 98765   12345   US  Hawaii  string_4    string_5    string_6    string_7    string_8    false   true    false
12345   e@mail.addy 43210   0123456789  foo_value}bar_value 107.00  0.00    timestamp_1 timestamp_2 54321   string_2    string_3    timestamp_3 98765   12345   US  Hawaii  string_4    string_5    string_6    string_7    string_8    false   true    false

Добавьте фигурные скобки с помощью awk и удалите ранее существовавшие, если таковые имеются (выше, вторая запись, пятое поле):

$ awk '
BEGIN {
    FS=OFS="\t"              # set input and output delimiters to a tab
}
NR==1 {                      # first record in file
    nf=NF                    # store field count
}
NF==nf {                     # process only records with the same field count as the first record
    gsub(/\{/,"\\{",$5)      # escape left curly brackets with a \
    gsub(/\}/,"\\}",$5)      # escape right curly brackets with a \
    $5="{" $5 "}"            # surround the fifth with curly brackets
    print                    # output
}' data > processed_data     # redirect output to another file

Если вы не избежите их, вы получите:

psql:bar.sql:1: ERROR:  malformed array literal: "{foo_value,bar}value}"
DETAIL:  Junk after closing right brace.
CONTEXT:  COPY postgres_table, line 2, column col5: "{foo_value,bar}value}"

Вывод:

$ cat processed_data
1234    e@mail.addy 43210   0123456789  {foo_value,bar_value}...
12345   e@mail.addy 43210   0123456789  {foo_value,bar\}value}...

\COPY скрипт:

$ cat copy.sql
\COPY postgres_table(col1,col2,col3,col4,col5,col6,col7,col8,col9,col10,col11,col12,col13,col14,col15,col16,col17,col18,col19,col20,col21,col22,col23,col24,col25) FROM 'processed_data' CSV DELIMITER E'\t';

Выполнить:

$ psql -h host -U user -f copy.sql database
Timing is on.
COPY 2
Time: 5.966 ms

Запрос col5:

database=# select col5 from postgres_table;
          col5           
-------------------------
 {foo_value,bar_value}
 {foo_value,"bar}value"}
(2 rows)

Time: 3.388 ms
0 голосов
/ 08 октября 2019

Загрузить во временную таблицу как текст, а затем преобразовать ее.

create temp table foobar (like postgres_table);
alter table foobar alter col5 type text;
\copy foobar from whatever.txt
insert into postgres_table select col1, col2, col3, col4, ('{'||col5||'}')::text[], col6.....from foobar;

Это не будет работать, если в "col5" встречаются знаки препинания.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...