У меня есть большая таблица, строки которой периодически обновляются / вставляются / объединяются из нескольких разных запросов. Мне нужно запустить запланированный процесс (через API), чтобы периодически проверять, какие строки в этой таблице были обновлены с момента последней проверки. Итак, вот мои проблемы ...

  • Когда я запускаю запрос на слияние, я не вижу способа вернуть ему записи, которые были обновлены ... в противном случае я мог бы скопировать эти обновленные строки в специальную таблицу updated_records.
  • Триггеров нет, поэтому я не могу отслеживать мутации таким образом.
  • Я мог бы добавить столбец отметки времени last_updated, чтобы отслеживать этот путь, но затем многократно запрашивать всю таблицу в течение всего дня, это было бы огромным количеством выставленных счетов (дорого).

Мне интересно, упускаю ли я что-то очевидное или, может быть, есть какие-то особые метаданные BQ, которые могут помочь?

Причина, по которой я пытаюсь это сделать, заключается в том, что я хочу извлечь и синхронизировать меньшее подмножество этой таблицы в мой экземпляр PostgreSQL, потому что задержка для запроса BQ слишком велика для небольших запросов.

Любые идеи? Благодарность!

1
Jim Ott 19 Фев 2020 в 08:23

4 ответа

Лучший ответ

Некоторые хорошие идеи размещены здесь. Спасибо тем, кто откликнулся. По сути, есть несколько подходов к решению этой проблемы.

Но в любом случае, вот как я решил свою конкретную проблему ...

Предположим, что данные должны в конечном итоге оказаться в таблице с именем MyData . Я создал две дополнительные таблицы: MyDataStaging и MyDataUpdate . Эти две таблицы имеют структуру, идентичную MyData , за исключением того, что MyDataStaging имеет дополнительное поле Timestamp «batch_timestamp». Эта временная метка позволяет мне определить, какие строки являются последними версиями на случай, если я получу несколько версий до обработки таблицы.

DatFlow отправляет данные непосредственно в MyDataStaging вместе со значением отметки времени ("batch_timestamp"), указывающим, когда выполнялся процесс. Затем запланированный процесс добавляет / объединяет MyDataStaging с MyDataUpdate ( MyDataUpdate теперь всегда будет содержать только уникальный список строк / значений, которые были изменены) . Затем процесс загружается / объединяется из MyDataUpdate в MyData , а также экспортируется и загружается для загрузки в PostgreSQL. Затем таблицы промежуточного хранения / обновления очищаются соответствующим образом.

Теперь я не постоянно запрашиваю массивную таблицу для проверки изменений.

ПРИМЕЧАНИЕ. При слиянии с основной большой таблицей я фильтрую обновление по уникальным датам из исходной таблицы, чтобы ограничить количество обрабатываемых байтов.

0
Jim Ott 5 Мар 2020 в 18:54

Одним из способов является периодическое сохранение промежуточного состояния таблицы с использованием функции перемещения во времени. Или хранить только различия. Я просто хочу оставить эту опцию здесь:

FOR SYSTEM_TIME AS OF ссылается на исторические версии определения таблицы и строки, которые были текущими в timestamp_expression.

Значение timestamp_expression должно быть в пределах последних 7 дней .

Следующий запрос возвращает хронологическую версию таблицы за час до этого.

SELECT * FROM table
  FOR SYSTEM_TIME AS OF TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR);

Следующий запрос возвращает хронологическую версию таблицы в абсолютный момент времени.

SELECT * FROM table
  FOR SYSTEM_TIME AS OF '2017-01-01 10:00:00-07:00';
1
Pentium10 19 Фев 2020 в 07:41

Подход состоит в том, чтобы иметь 3 таблицы:

  1. один basetable в режиме «только добавление», добавляются только вставки и обновления в виде полной строки, в этой таблице каждая запись будет похожа на систему управления версиями.
  2. таблица для хранения deletes (или это можно включить как мягкое удаление, если в первой таблице хранится специальный столбец)
  3. livetable где вы храните текущие данные (в этой таблице вы, скорее всего, сделаете свои MERGE операторы из первой базовой таблицы.

Если вы выберете секционирование и кластеризацию, вы можете в конечном итоге получить много пользы от цены со скидкой на долгое хранение и сканировать меньше данных с помощью секционирования и кластеризации.

1
Pentium10 19 Фев 2020 в 15:31

Если таблица большая, но объем данных, обновляемых за день, невелик, вы можете разбить и / или разбить таблицу на столбцы last_updated_date. Существуют некоторые крайние случаи, например, первая сегодняшняя проверка должна фильтровать для last_updated_date либо сегодня, либо вчера.

В зависимости от того, насколько скромен этот объем данных, обновляемых в течение дня, даже многократные запросы ко всей таблице в течение всего дня могут быть доступными, потому что механизм BQ будет сканировать только один ежедневный раздел.

< Сильный > P.S .
Детальное объяснение

Я мог бы добавить столбец отметки времени last_updated, чтобы отслеживать этот путь

Из этого я сделал вывод, что столбца last_updated еще нет (поэтому оператор проверки обновлений в настоящее время не может различить обновленные строки и не обновленные), но вы можете изменить операторы таблицы UPDATE, чтобы этот столбец был добавлены в новые измененные строки.

Поэтому я предположил, что вы можете изменить обновления дальше, чтобы установить дополнительный столбец last_updated_date, который будет содержать часть даты временной отметки, хранящейся в столбце last_updated.

но затем многократно запрашивая весь стол весь день

Отсюда я сделал вывод, что в течение дня проводится несколько проверок.

но обновляемые данные могут быть за любой период времени

Конечно, но как только строка будет обновлена, независимо от ее возраста, она получит два новых столбца last_updated и last_updated_date - если оба столбца не были добавлены предыдущим обновлением, в котором В этих случаях два столбца будут обновляться, а не добавляться. Если между проверками обновлений имеется несколько обновлений для одной и той же строки, то при последнем обновлении строка будет обнаруживаться проверками, использующими логику, описанную ниже.

Оператор проверки обновлений будет (концептуально, а не буквально):

  • фильтровать строки, чтобы обеспечить last_updated_date=today AND last_updated>last_checked. Дата и время предыдущей проверки обновления будут храниться в last_checked, и место хранения этого фрагмента данных (таблица, долговременная конфигурация) зависит от реализации.

  • узнайте, является ли текущая проверка первой сегодняшней проверкой. Если так, тогда дополнительно ищите last_updated_date=yesterday AND last_updated>last_checked.

Примечание 1
Если таблица разделена и / или кластеризована в столбце last_updated_date, то вышеупомянутые проверки обновлений не вызовут сканирование таблицы. И с учетом «скромного» предположения, сделанного в самом начале моего ответа, проверки удовлетворят ваш третий пункт.

Примечание 2
Недостатком этого подхода является то, что при проверках обновлений не будут найдены строки, которые были обновлены до того, как в операторы UPDATE таблицы были внесены два дополнительных столбца. (Такие строки будут в разделе __NULL__ со строками, которые никогда не обновлялись.) Но я предполагаю, что до внесения изменений в операторы UPDATE будет невозможно отличить обновленные строки от не обновленных.

Примечание 3
Это пояснительная концепция. В реальной реализации вам может понадобиться один дополнительный столбец вместо двух. И вам нужно будет проверить, какой подход работает лучше: разделение или кластеризация (с разделением на поддельный столбец) или оба.

Подробное объяснение первоначального (например, выше P.S. ) ответа заканчивается здесь.

Примечание 4

кластеризация только помогает производительности

С точки зрения предотвращения сканирования таблиц и достижения снижения использования / стоимости данных, одна кластеризация (с поддельным разделением) может быть такой же мощной, как и разделение.

Примечание 5
В упомянутом вами комментарии уже есть разделение. Я бы посоветовал проверить, является ли существующее разделение обязательным, можно ли его заменить кластеризацией.

1
winwiz1 20 Фев 2020 в 07:30