附加具有不同架构的文件-BigQuery

时间:2019-04-13 00:18:52

标签: google-bigquery

我正在尝试将多个CSV文件附加到BigQuery的一个表中。问题在于它们没有相同的架构。它们有键,但字段不相同。这是一个示例:

file1.csv

ID     A       B       C
1       T       T       T
2       F       T       F
3       F       F       F

file2.csv

ID     A       B       D
1       T       T       T
4       F       T       F
5       F       F       F

合并这些文件的最佳方法是什么。

ID     A       B       C       D
1      T       T       T       T
2      F       T       F
3      F       F       F
4      F       T               F
5      F       F               F

3 个答案:

答案 0 :(得分:2)

您极有可能需要此最终表才能用作其他表的联接-在这种情况下,无论如何,这种类似枢轴的模式都不是最有效的选择-因此,我建议考虑以下选项:您将把原始矩阵展平为ID-列-值模式

在下面的示例中-我假设您具有带有True / False值的特征集,因此我可以使用逻辑AND或OR轻松调和“冲突”值-但如果您实际上具有诸如“ T”, F”(当然,在这种情况下,下面的代码将需要稍作调整)

因此,以下是适用于BigQuery Standard SQL的代码,在应用此代码之前,您只需将所有文件加载到单独的表中(文件1>表1,文件2>表2等)

#standardSQL
CREATE TEMP FUNCTION x(t STRING) AS ((
  ARRAY(SELECT AS STRUCT col, val = 'true' val FROM
  UNNEST(REGEXP_EXTRACT_ALL(t, r',"(.+?)":(?:true|false)')) col WITH OFFSET
  JOIN UNNEST(REGEXP_EXTRACT_ALL(t, r',".+?":(true|false)')) val WITH OFFSET
  USING(OFFSET))  
));
SELECT id, col, LOGICAL_OR(val) val
FROM (
  SELECT ID, col, val FROM `project.dataset.table1` t, UNNEST(x(TO_JSON_STRING(t))) 
  UNION ALL
  SELECT ID, col, val FROM `project.dataset.table2` t, UNNEST(x(TO_JSON_STRING(t))) 
)
GROUP BY id, col

您可以根据需要添加以下行

UNION ALL
SELECT ID, col, val FROM `project.dataset.tableX` t, UNNEST(x(TO_JSON_STRING(t))) 

您可以使用问题中的示例数据来测试,玩游戏,如下例所示

#standardSQL
CREATE TEMP FUNCTION x(t STRING) AS ((
  ARRAY(SELECT AS STRUCT col, val = 'true' val FROM
  UNNEST(REGEXP_EXTRACT_ALL(t, r',"(.+?)":(?:true|false)')) col WITH OFFSET
  JOIN UNNEST(REGEXP_EXTRACT_ALL(t, r',".+?":(true|false)')) val WITH OFFSET
  USING(OFFSET))  
));
WITH `project.dataset.table1` AS (
  SELECT 1 ID, TRUE A, TRUE B, TRUE C UNION ALL
  SELECT 2, FALSE, TRUE, FALSE UNION ALL
  SELECT 3, FALSE, FALSE, FALSE 
), `project.dataset.table2` AS (
  SELECT 1 ID, TRUE A, TRUE B, TRUE D UNION ALL
  SELECT 4, FALSE, TRUE, FALSE UNION ALL
  SELECT 5, FALSE, FALSE, FALSE 
)
SELECT id, col, LOGICAL_OR(val) val
FROM (
  SELECT ID, col, val FROM `project.dataset.table1` t, UNNEST(x(TO_JSON_STRING(t))) 
  UNION ALL
  SELECT ID, col, val FROM `project.dataset.table2` t, UNNEST(x(TO_JSON_STRING(t))) 
)
GROUP BY id, col
-- ORDER BY id, col

有结果

Row id  col val  
1   1   A   true     
2   1   B   true     
3   1   C   true     
4   1   D   true     
5   2   A   false    
6   2   B   true     
7   2   C   false    
8   3   A   false    
9   3   B   false    
10  3   C   false    
11  4   A   false    
12  4   B   true     
13  4   D   false    
14  5   A   false    
15  5   B   false    
16  5   D   false    

根据我的经验,在大多数情况下,使用上述扁平化模式比您最初预期的模式要简单和容易

答案 1 :(得分:2)

BigQuery没有键的概念,因此,如果您将两个具有相同ID列的文件“追加”在一起,则它们不会合并。但是,如果您要加载的只是两个具有不同模式的文件,然后将数据追加到其中,则可以轻松完成此操作。您可以使用表示允许架构更改的架构更新选项来告诉bigquery进行加载。您还应该传递要添加的文件的显式架构。因此,在您的情况下:

如果您有源文件:

$ cat one.csv
ID,A,B,C
1,T,T,T
2,F,T,F
3,F,F,F

$ cat two.csv
ID,A,B,D
1,T,T,T
4,F,T,F
5,F,F,F

然后您可以做

$ bq load --source_format=CSV --schema=id,a,b,c --skip_leading_rows=1 temp.test one.csv
Current status: DONE   
$ bq load --source_format=CSV --schema=id,a,b,d --schema_update_option=ALLOW_FIELD_ADDITION --skip_leading_rows=1 temp.test two.csv
Current status: DONE   
$ bq head temp.test
+----+---+---+------+------+
| id | a | b |  d   |  c   |
+----+---+---+------+------+
| 1  | T | T | NULL | T    |
| 2  | F | T | NULL | F    |
| 3  | F | F | NULL | F    |
| 1  | T | T | T    | NULL |
| 4  | F | T | F    | NULL |
| 5  | F | F | F    | NULL |
+----+---+---+------+------+

但是,这与您所说的不完全相同。似乎您想合并ID为1的行,以便它包含两个文件中的数据。

执行此操作的最佳方法是加载到两个单独的表,然后进行联接。如果将表temp.t1和temp.t2加载到表中,则只需对两个表进行联接。如

$ bq load --source_format=CSV --schema=id,a,b,c --skip_leading_rows=1 temp.t1 one.csv
Current status: DONE   
$ bq load --source_format=CSV --schema=id,a,b,d --skip_leading_rows=1 temp.t2 two.csv
Current status: DONE   
$ bq query --nouse_legacy_sql "SELECT IFNULL(t2.id, t1.id) as id, IFNULL(t2.a,  t1.a) as a, IFNULL(t2.b, t1.b) as b, t1.c as c, t2.d as d   FROM temp.t1 as t1 FULL OUTER JOIN temp.t2 as t2 ON t1.id = t2.id ORDER BY id"
Current status: DONE   
+----+---+---+------+------+
| id | a | b |  c   |  d   |
+----+---+---+------+------+
| 1  | T | T | T    | T    |
| 2  | F | T | F    | NULL |
| 3  | F | F | F    | NULL |
| 4  | F | T | NULL | F    |
| 5  | F | F | NULL | F    |
+----+---+---+------+------+

答案 2 :(得分:1)

将JSON文件加载到BigQuery中时,它将很高兴地识别并加载到正确的列中,因为JSON在每个记录中都清楚地表明了要加载数据的列。同时,使用CSV时您不能拥有相同的功能:将CSV表加载到BigQuery中时,BigQuery只会按表和CSV的相同顺序将列映射到表。

因此,如果您具有不同的CSV模式,则需要将它们加载到不同的BigQuery表中,并在以后使用插入内容将其映射。