#google-bigquery
#google-bigquery
Вопрос:
Я пытаюсь добавить несколько файлов CSV в одну таблицу в BigQuery. Проблема в том, что у них нет одинаковой схемы. У них есть ключи, но не одинаковые поля. Вот пример:
файл1.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
Комментарии:
1. Что именно вы хотите сделать? Является ли цель в конечном итоге эффективным объединением этих двух файлов, где столбец D обрабатывается так, как если бы он на самом деле назывался C? Или вы хотите, чтобы D обрабатывался как отдельный столбец после загрузки?
2. да, я хочу, чтобы D был разделен новым столбцом. Только что добавлена таблица результатов.
3. и как вы решаете, какие значения использовать для ID = 1? из file1 или file2? или какая-то другая логика?
Ответ №1:
В 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 |
---- --- --- ------ ------
Однако это не совсем то, что вы сказали, что хотели; похоже, вы хотите объединить строку с идентификатором 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:
Скорее всего, вам нужна эта итоговая таблица для последующего использования в качестве соединения с некоторыми другими таблицами — в этом случае наличие этой сводной схемы в любом случае не является наиболее эффективным вариантом, поэтому я бы рекомендовал рассмотреть вариант ниже, в котором вы сгладите исходную матрицу в схему ID — column — value
В примере ниже — я предполагаю, что у вас есть наборы функций со значениями True / False, поэтому я могу легко согласовать «конфликтующие» значения, используя логические И или ИЛИ — но тот же подход будет работать, если у вас действительно есть строки типа «T», «F» (конечно, в таком случае приведенный ниже код должен будетнемного скорректировать)
Итак, ниже приведен стандартный SQL BigQuery, и перед применением этого кода вы просто загружаете все свои файлы в отдельные таблицы (file1> table1, file2> table2 и т. Д.)
#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
По моему опыту, в большинстве случаев использование приведенной выше схемы сглаживания проще и проще, чем схема, которую вы изначально ожидали (в вашем вопросе)
Ответ №3:
При загрузке файлов JSON в BigQuery он с радостью идентифицирует и загружает их в правильные столбцы, поскольку JSON четко указывает в каждой записи столбцы, в которые он хочет загрузить данные. Между тем, с CSV у вас не может быть того же самого: когда вы загружаете таблицы CSV в BigQuery, BigQuery просто сопоставит столбцы с таблицей в том же порядке для таблицы и CSV.
Следовательно, если у вас разные схемы CSV, вам нужно будет загрузить их в разные таблицы BigQuery и сопоставить их позже с помощью вставки или около того.