使用非标准分隔符将csv加载到BQ

时间:2019-02-11 20:52:43

标签: csv google-cloud-platform google-bigquery google-cloud-dataflow

假设我在csv文件中包含以下数据:

'"tom","jones","hello,\nMy name is tom"\x01\n"sarah","smith","hello"\x01\n'

行终止符为\x01\n。是否可以将其直接加载到GCS中(而无需先进行预格式化)?我的思考过程是:

  • 使用非标准分隔符(例如\x00ff)将其加载到CSV中,以将所有数据收集到一行。
  • 然后执行基本的DML以“清理”数据并重新格式化。

但是,当我们有连续的线路时,我们会遇到一个问题,因为BQ不“支持”(如果您要称呼它)行排序。这是我现在在BQ中的数据:

enter image description here

我们可以看到行顺序不起作用,因此,例如,使用UDF来获取所需的正确csv数据是不可能的。

这里还有其他可能的方法吗?只是为了澄清一下,我希望通过BigQuery转换GCS上已经存在的CSV文件,而不必将该文件下载到单独的服务器上进行处理,然后再加载到BQ中。


作为参考,这是我当前正在使用的代码:

# /tmp/schema_external_nonstandard_csv.json
{
  "schema": {
    "fields": [
      {
        "name": "data",
        "type": "STRING"
      }
    ]
  },
  "sourceFormat": "CSV",
  "sourceUris": [
    "gs://XY-bq/ns.csv"
  ],
  "csvOptions": {
    "fieldDelimiter": "\u00ff",
    "quote": ""
  },
  "maxBadRecords": 1000000
}

$ bq mk --external_table_definition=/tmp/schema_external_nonstandard_csv.json datadocs-163219:bqtesting.ns
$ bq query --nouse_legacy_sql 'CREATE TABLE `XY-163219.bqtesting.ns1` AS select * from `XY-163219.bqtesting.ns`'

1 个答案:

答案 0 :(得分:2)

我想到了一些纯粹的BigQuery解决方案:

  1. 用bq指定分隔符。如here
  2. 所述,此方法不适用于该用例
  

“分隔符可以是任何ISO-8859-1单字节字符。”

  1. 使用REGEXP_REPLACE最好的方法是单行内部有换行符:
CREATE OR REPLACE TABLE test.separator_final AS
SELECT
  REGEXP_REPLACE(data, r"\\x01\\n", "\n") AS data
FROM
  test.separator_external

enter image description here

  1. 从上一点开始,可以使用“ hack”将行扩展为不同的行(请参见答案here)。但是,需要注意的是,您需要先验地了解拆分的数量,在这种情况下,不一致的数量。

  2. 您已经在使用的,但是添加了一个行号,以便可以将数据合并回去。这可能有用,但确保保留行顺序也会很复杂。

如果我们考虑将其他GCP产品用作GCS和BigQuery之间的中间人,我们可以找到其他有趣的解决方案:

  1. 使用Dataprep,它在后台运行Dataflow。有一个替换转换(docs),并且可以以编程方式生成和调用数据流模板。

  2. 使用数据流。我实际上使用此gist测试了该解决方案,它的工作原理是: 我认为可以通过创建模板(自定义分隔符可以是输入参数)很好地扩展该模板,并在每次使用Cloud Functions将文件上传到GCS时(NoOps解决方案)触发该模板。

简而言之,我们使用TextIO.read().from(file)从文件中读取记录,其中file是GCS路径(启动作业时提供inputoutput参数)。我们还可以使用withDelimiter()来使用虚拟定界符来避免冲突(此处再次限制为单个字节,因此我们不能直接传递真实的定界符)。然后,对于每一行,我们用c.element().split("\\\\x01\\\\n")除以实际的定界符。请注意,我们需要对已经转义的字符进行转义(您可以在JSON查询结果中以正常负载验证该字符),因此需要四倍的反斜杠。

p
    .apply("GetMessages", TextIO.read().from(file))
        .apply("ExtractRows", ParDo.of(new DoFn<String, String>() {
        @ProcessElement
        public void processElement(ProcessContext c) {
          for (String line : c.element().split("\\\\x01\\\\n")) {
            if (!line.isEmpty()) {
              c.output(line);
            }
          }
        }
    }))

结果:

enter image description here

请记住,正如@hlagos指出的那样,由于BigQuery中的行数限制或分配给Dataflow中单个工作人员的不可拆分步骤,您可能会遇到很大的单行CSV问题。