sql - 如何从 .t​​sv 文件的特定列中获取数据到 Postgres 数组列中?正则表达式是正确的工具,还是我应该寻找另一种方法?

标签 sql arrays regex postgresql csv

概述 :

1) 我有一个 .TSV [制表符分隔的值,平面文件] 转储,我需要将它分流到现有的 Postgres 表 [表不是我的设计,但我确实有直接的数据库访问权限] 我打算使用 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
);

附加说明:数据库是 Postgres 11;所有涉及的系统都是基于 RHEL 的;我知道一点 JavaScript 和 SQL,但到目前为止我的尝试都是在 bash 中,过去我已经成功地将\copy 用于许多 .csv 类型的转储 [但从未用于非规范化表],我认为以下特别SO问题会让我大部分时间到达那里,但是当我尝试使解决方案适应我的情况时,我得到了错误“substitution failed”:
How can i capture all data from a certain column?

澄清一下:我渴望使用任何有效的方法,所以如果有比正则表达式更好的方法来解决这个问题,我会全力以赴!非常感谢所有指导。

编辑:

非常感谢@James Brown 和@jjanes——我还没有足够的“声誉点”来支持你的答案,但我会尽快这样做。

下面来自@James Brown 的 awk 解决方案对我有用——处理后的文件包含一个尾随换行符,我必须使用这个 unix.SE 问题中的 awk 脚本删除该换行符:
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;正如以下 SF 问题中所建议的那样,但没有骰子
https://serverfault.com/questions/488669/postgres-insert-error-permission-denied-for-schema-public

我看不出这不应该起作用的任何原因,真是令人头疼。

最佳答案

使用 awk 。您的数据在第五个字段中有一个扭曲(我为您留下了 timestamp_[123] ,但修复了丢失的选项卡):

$ 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

关于sql - 如何从 .t​​sv 文件的特定列中获取数据到 Postgres 数组列中?正则表达式是正确的工具,还是我应该寻找另一种方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58278536/

相关文章:

sql - Postgres 架构问题

c# - 如何在c#中保留锯齿状数组的一些列和行并删除不需要的列和行

regex - 我如何在 vim 中翻译正则表达式以与 sed 一起工作?

ruby-on-rails - 使用带突出显示的正则表达式

javascript - 如何修改时间正则表达式?

mysql查询将列拆分为m

mysql - 组合查询

mysql - MySQL 中的 ROW_NUMBER()

javascript - Array.prototype.find 在数组中搜索对象

arrays - 如何将数字数组从 bash 传递到 csh