Сначала обратите внимание на оптимизацию затрат: взимается плата BigQuery за один столбец, и этот запрос будет проходить через 72GB. Таблица gg GDELT хранит всю историю в одной таблице - мы можем оптимизировать затраты, создавая годовые таблицы вместо одного.
Теперь, как мы можем исправить этот запрос, чтобы он пробежал целый год? «Ресурсы, превышаемые во время выполнения запроса», обычно исходят из немасштабируемых функций. Например:
RATIO_TO_REPORT(COUNT) OVER()
не будет масштабироваться: OVER() функция запуска по всему набору результатов, что позволяет нам вычислить итоги и сколько из общего числа в каждой строке способствует - но для этого бежать нам нужно полный набор результатов, чтобы он входил в одну виртуальную машину. Хорошей новостью является то, что OVER() может масштабироваться при разделении данных, например, имея OVER (PARTITION BY month) - тогда нам понадобится только каждый раздел для размещения в виртуальной машине. Для этого запроса мы просто удалим этот столбец результатов для простоты.
ORDER BY не будет масштабироваться: для сортировки результатов нам нужны все результаты, чтобы поместиться на одной виртуальной машине тоже. Вот почему «-allow-large-results» не позволит запустить шаг ORDER BY, так как каждая VM будет обрабатывать и выводить результаты параллельно.
В этом запросе мы имеем простой способ справиться с ORDER BY масштабируемость - мы будем двигаться позже фильтр «WHERE COUNT> 50» ранее в этот процесс. Вместо того чтобы сортировать все результаты и фильтрации те, которые имели COUNT> 50, мы будем двигаться и изменить его к HAVING, так он работает до ORDER BY:
SELECT Source, Target, count
FROM (
SELECT a.name Source, b.name Target, COUNT(*) AS COUNT
FROM (FLATTEN(
SELECT
GKGRECORDID, CONCAT(STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)), '#', STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3))) AS name
FROM [gdelt-bq:gdeltv2.gkg]
WHERE DATE>20150100000000 and DATE<20151299999999,name)) a
JOIN EACH (
SELECT
GKGRECORDID, CONCAT(STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)), '#', STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3))) AS name
FROM [gdelt-bq:gdeltv2.gkg]
WHERE DATE>20150100000000 and DATE<20151299999999) b
ON a.GKGRECORDID=b.GKGRECORDID
WHERE a.name<b.name
AND a.name != '0.000000#0.000000'
AND b.name != '0.000000#0.000000'
GROUP EACH BY 1, 2
HAVING count>50
ORDER BY 3 DESC)
LIMIT 500000
А теперь запрос пробегает полный год данных!
Давайте посмотрим на статистику объяснения:
Мы можем видеть, что таблица 188000000 строка была прочитана дважды: первый подзапрос произвел 1,5 млрд строк (учитывая «Свести»), и второй отфильтровал строки не в 2015 году (обратите внимание, что эта таблица начала хранить данные в начале 2015 года).
Этап 3 интересен: объединение обоих подзапросов позволило создать 3 миллиарда строк! Те получили уменьшенный до 500 миллионов с фильтром и агрегатные шагов:
Можем ли мы сделать лучше?
Да! Давайте перейдем к 2 WHERE a.name != '....'
на более раннюю «ИМЕТЬ»:
SELECT Source, Target, count
FROM (
SELECT a.name Source, b.name Target, COUNT(*) AS COUNT
FROM (FLATTEN(
SELECT
GKGRECORDID, CONCAT(STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)), '#', STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3))) AS name
FROM [gdelt-bq:gdeltv2.gkg]
WHERE DATE>20150100000000 and DATE<20151299999999
HAVING name != '0.000000#0.000000',name)) a
JOIN EACH (
SELECT
GKGRECORDID, CONCAT(STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)), '#', STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3))) AS name
FROM [gdelt-bq:gdeltv2.gkg]
WHERE DATE>20150100000000 and DATE<20151299999999
HAVING name != '0.000000#0.000000') b
ON a.GKGRECORDID=b.GKGRECORDID
WHERE a.name<b.name
GROUP EACH BY 1, 2
HAVING count>50
ORDER BY 3 DESC)
LIMIT 500000
Это работает еще быстрее!
Давайте посмотрим на статистику объяснения:
См? Перемещая фильтрацию на шаг до присоединения, этап 3 должен пройти только 1 миллиард строк, а не 3 миллиарда строк. Гораздо быстрее (даже для BigQuery, который, как вы можете проверить самостоятельно, способен в течение короткого промежутка времени собирать более 3 миллиардов строк, созданных JOIN).
И для чего был этот запрос?
Посмотрите на прекрасные результаты здесь: http://blog.gdeltproject.org/a-city-level-network-diagram-of-2015-in-one-line-of-sql/
делает 'ORDER BY' внутри подзапроса гарантий соответствующий заказ на внешний/окончательный ВЫБРАТЬ? –