Google bigquery 附加具有不同模式的文件-BigQuery

Google bigquery 附加具有不同模式的文件-BigQuery,google-bigquery,Google Bigquery,我正在尝试将多个CSV文件附加到BigQuery中的一个表中。问题是它们没有相同的模式。它们有键,但字段不同。以下是一个例子: file1.csv ID A B C 1 T T T 2 F T F 3 F F F 文件2.csv ID A B D 1 T T T 4 F

我正在尝试将多个CSV文件附加到BigQuery中的一个表中。问题是它们没有相同的模式。它们有键,但字段不同。以下是一个例子:

file1.csv

ID     A       B       C
1       T       T       T
2       F       T       F
3       F       F       F
文件2.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

当将JSON文件加载到BigQuery中时,它会很高兴地识别并将其加载到正确的列中,正如JSON在每个记录中清楚地指出它希望加载数据的列一样。同时,对于CSV,您不能拥有相同的功能:当您将CSV表加载到BigQuery中时,BigQuery只会按照表和CSV的相同顺序将列映射到表


因此,如果您有不同的CSV模式,您将需要将它们加载到不同的BigQuery表中,并在稍后使用insert等将它们映射。

最有可能的情况是,您需要最后一个表,然后将其用作其他表的联接-在这种情况下,使用类似枢轴的模式并不是最有效的选择-因此我建议考虑以下选项,将原始矩阵展平为ID-列-值模式

在下面的示例中-我假设您拥有具有真/假值的功能集,因此我可以使用逻辑AND或or轻松协调“冲突”值-但如果您实际具有“T”、“F”等字符串,则同样的方法也会起作用(当然,在这种情况下,下面的代码需要稍微调整)

下面是BigQuery标准SQL,在应用此代码之前,只需将所有文件加载到单独的表中(file1>table1、file2>table2等)

您可以根据需要在下面添加任意多的行

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    

根据我在大多数情况下的经验,使用上述扁平模式比您最初期望的模式(在您的问题中)更简单、更容易。

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    |
+----+---+---+------+------+

你到底想做什么?最终的目标是有效地合并这两个文件,其中D列被视为实际名为C?或者你想在加载后把D作为一个单独的列来处理吗?是的,我想把D作为一个新的列来处理。刚刚添加了结果表。您如何决定ID=1使用哪些值?从文件1还是文件2?还是其他逻辑?
$ 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    |
+----+---+---+------+------+