Программное создание представления AWS Athena

Можно ли создавать представления в Amazon Athena? описывает, как создать представление с помощью пользовательского интерфейса.

Я хотел бы создать AWS Athena View программно, в идеале с помощью Terraform (который вызывает CloudFormation).

Я выполнил шаги, описанные здесь: https://ujjwalbhardwaj.me/post/create-virtual-views-with-aws-glue-and-query-them-using-athena, однако столкнулся с проблемой, заключающейся в том, что представление быстро устаревает.

...._view' is stale; it must be re-created.

Код терраформирования выглядит так:

resource "aws_glue_catalog_table" "adobe_session_view" {

  database_name = "${var.database_name}"
  name = "session_view"

  table_type = "VIRTUAL_VIEW"
  view_original_text = "/* Presto View: ${base64encode(data.template_file.query_file.rendered)} */"
  view_expanded_text = "/* Presto View */"

  parameters = {
    presto_view = "true"
    comment = "Presto View"
  }

  storage_descriptor {
    ser_de_info {
      name = "ParquetHiveSerDe"
      serialization_library = "org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe"
    }

    columns { name = "first_column" type = "string" }
    columns { name = "second_column" type = "int" }
    ...
    columns { name = "nth_column" type = "string" }
}

Альтернативой, которую я бы с удовольствием использовал, является интерфейс командной строки AWS, однако aws athena [option] не предоставляет для этого возможности.

Я пробовал:

  • создать именованный запрос, который мне не удалось заставить работать для оператора, такого как СОЗДАТЬ ИЛИ ЗАМЕНИТЬ ВИД, поскольку это не похоже на предполагаемый вариант использования этой команды.
  • запуск-выполнение-запроса, который запрашивает местоположение вывода, что предполагает, что это предназначено для запроса данных и вывода результатов, а не для внесения изменений/созданий с отслеживанием состояния. Также кажется, что он связан с остановить выполнение запроса.
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
21
0
13 092
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Ответ принят как подходящий

Как вы предположили, определенно возможно программно создать представление Athena через интерфейс командной строки AWS с помощью start-query-execution. Как вы указали, это требует от вас указать местоположение S3 для результатов, даже если вам не нужно будет проверять файл (по какой-то причине Athena поместит в это место пустой текстовый файл).

Вот пример:

$ aws athena start-query-execution --query-string "create view my_view as select * from my_table" --result-configuration "OutputLocation=s3://my-bucket/tmp" --query-execution-context "Database=my_database"

{
    "QueryExecutionId": "1744ed2b-e111-4a91-80ea-bcb1eb1c9c25"
}

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

Вы можете проверить, успешно ли создано ваше представление, с помощью команды get-query-execution.

$ aws --region athena get-query-execution --query-execution-id bedf3eba-55b0-42de-9a7f-7c0ba71c6d9b
{
    "QueryExecution": {
        "QueryExecutionId": "1744ed2b-e111-4a91-80ea-bcb1eb1c9c25",
        "Query": "create view my_view as select * from my_table",
        "StatementType": "DDL",
        "ResultConfiguration": {
            "OutputLocation": "s3://my-bucket/tmp/1744ed2b-e111-4a91-80ea-bcb1eb1c9c25.txt"
        },
        "Status": {
            "State": "SUCCEEDED",
            "SubmissionDateTime": 1558744806.679,
            "CompletionDateTime": 1558744807.312
        },
        "Statistics": {
            "EngineExecutionTimeInMillis": 548,
            "DataScannedInBytes": 0
        },
        "WorkGroup": "primary"
    }
}

Спасибо, ДжейДи! Это потрясающе! Работал как шарм, отличная документация: D

tjheslin1 28.05.2019 11:43

С Местоположение результата запроса, установленным на моем рабочая группа, я смог заменить --result-configuration на --work-group.

tjheslin1 28.05.2019 11:54

Можно ли это сделать через Athena API?

Jagrut Sharma 04.10.2021 23:59

Программное создание представлений в Athena не документировано и не поддерживается, но возможно. Что происходит за кулисами, когда вы создаете представление с помощью StartQueryExecution, так это то, что Athena позволяет Presto создать представление, а затем извлекает внутреннее представление Presto и помещает его в каталог Glue.

Проблема устаревания обычно возникает из-за того, что столбцы в метаданных Presto и метаданные Glue не синхронизированы. Представление Athena действительно содержит три описания представления: представление SQL, столбцы и их типы в формате Glue, а также столбцы и типы в формате Presto. Если какой-либо из них выйдет из строя, вы получите сообщение «… устарело; его необходимо создать заново». ошибка.

Вот требования к таблице Glue для работы в качестве представления Athena:

  • TableType должно быть VIRTUAL_VIEW
  • Parameters должен содержать presto_view: true
  • TableInput.ViewOriginalText должен содержать закодированный вид Presto (см. ниже)
  • StorageDescriptor.SerdeInfo должна быть пустая карта
  • StorageDescriptor.Columns должен содержать все столбцы, которые определяет представление, с их типами

Сложная часть — это закодированное представление Presto. Эта структура создается с помощью этого кода: https://github.com/prestosql/presto/blob/27a1b0e304be841055b461e2c00490dae4e30a4e/presto-hive/src/main/java/io/prestosql/plugin/hive/HiveUtil.java#L597-L600, и примерно вот что он делает:

  • Добавляет префикс /* Presto View: (с пробелом после :)
  • Добавляет строку JSON в кодировке Base 64, содержащую SQL представления, столбцы и их типы, а также некоторые метаданные каталога (см. ниже).
  • Добавляет суффикс */ (с пробелом перед *)

JSON, описывающий представление, выглядит так:

  • Свойство catalog, которое должно иметь значение awsdatacatalog.
  • Свойство schema, которое должно быть именем базы данных, в которой создается представление (т. е. оно должно соответствовать свойству DatabaseName окружающей структуры Glue.
  • Список столбцов, в каждом из которых есть name и type
  • Свойство originalSql с фактическим представлением SQL (не включая CREATE VIEW …, оно должно начинаться с SELECT … или WITH …)

Вот пример:

{
  "catalog": "awsdatacatalog",
  "schema": "some_database",
  "columns": [
    {"name": "col1", "type": "varchar"},
    {"name": "col2", "type": "bigint"}
  ],
  "originalSql": "SELECT col1, col2 FROM some_other_table"
}

Одно предостережение здесь заключается в том, что типы столбцов почти, но не совсем, совпадают с именами в Glue. Если бы у Athena/Glue было бы string, значение в этом JSON должно быть varchar. Если Athena/Glue использует array<string>, значение в этом JSON должно быть array(varchar), а struct<foo:int> становится row(foo int).

Это довольно запутанно, и для того, чтобы собрать все это воедино, нужно немного повозиться и протестировать. Самый простой способ заставить его работать — создать несколько представлений и декодировать приведенные выше инструкции в обратном порядке, чтобы увидеть, как они выглядят, а затем попробовать сделать это самостоятельно.

Чтобы добавить к этому: struct типы данных столбца в Athena должны быть сопоставлены с row в определении Presto JSON, например. type = "struct<col1:string>" в определении Terraform/Glue отображается на "type": "row(col1 varchar)" в определении представления Presto.

Nathan Griffiths 13.09.2019 01:17

@NathanGriffiths, вы правы, я написал struct вместо row, теперь исправлено в моем ответе.

Theo 13.09.2019 10:30

Так же, как пара дополнительных заметок из моих знаний при реализации этого ответа, которые, надеюсь, помогут другим. Столбцы во всех трех представлениях таблицы должны быть в одном порядке (в противном случае вид будет устаревшим). Столбцы должны быть отлиты в originalSQL, чтобы соответствовать обозначениям в предварительных столбцах. (устаревший вид). Я также неправильно прочитал ответ и подумал, что Presto добавит префикс, а base64 закодирует мой JSON для меня, но это не так. originalText = addPrefixSuffix(base64(JSON.stringify(exampleObjectabove)))

Ben Swinburne 16.04.2020 23:50

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

trademark 10.02.2021 17:38

Я упаковал этот ответ как модуль Terraform, который вы можете найти здесь: github.com/iconara/terraform-aws-athena-view

Theo 26.06.2021 13:40

Чтобы добавить ответы JD D и Theo, работая с их решениями, мы выяснили, как вызывать AWS Cli через terraform следующим образом:

resource "null_resource" "athena_view" {

  provisioner "local-exec" {
    command = <<EOF
aws sts assume-role \
  --output json \
  --region my_region \
  --role-arn arn:aws:iam::${var.account_number}:role/my_role \
  --role-session-name create_my_view > /tmp/credentials.json

export AWS_SESSION_TOKEN=$(jq -r '.Credentials.SessionToken' /tmp/credentials.json)
export AWS_ACCESS_KEY_ID=$(jq -r '.Credentials.AccessKeyId' /tmp/credentials.json)
export AWS_SECRET_ACCESS_KEY=$(jq -r '.Credentials.SecretAccessKey' /tmp/credentials.json)

aws athena start-query-execution \
  --output json \
  --region my_region \
  --query-string "CREATE OR REPLACE VIEW my_view AS SELECT * FROM my_table \
  --query-execution-context "Database=${var.database_name}" \
  --result-configuration "OutputLocation=s3://${aws_s3_bucket.my-bucket.bucket}"
EOF
  }
}

Мы используем null_resource... для запуска поставщиков, которые не связаны напрямую с конкретным ресурсом.

Результат aws sts assume-role выводится в формате JSON в /tmp/credentials.json.

jq используется для анализа необходимых полей из вывода aws sts взять на себя роль.

Затем AWS Athena запуск-запрос-выполнение может выполняться под ролью, указанной определенными переменными среды.

Вместо --result-configuration "OutputLocation=s3://.... можно указать --work-group. ОБРАТИТЕ ВНИМАНИЕ, что это отдельный флаг для start-query-execution, а не часть строки --result-configuration.

Дополнение к ответу Тео: в файле JSON с кодировкой base64 тип «строка» недействителен при определении атрибутов cloumn! Всегда пишите «varchar» в этот момент.

редактировать: также "int" должен быть объявлен как "целое число"!

Я выбрал решение Тео, и оно работало с использованием шаблонов AWS Cloud Formation.

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

Без проблем! Рад, что очень подробный ответ Тео помог!

tjheslin1 06.08.2019 12:00

Я исправил свой ответ, так что он говорит varchar в соответствующем месте.

Theo 13.09.2019 10:32

Обновление приведенных выше примеров для синтаксиса Terraform 0.12+, и добавление при чтении запросов просмотра из файловой системы:

resource "null_resource" "athena_views" {
  for_each = {
    for filename in fileset("${path.module}/athenaviews/", "**"):
           replace(filename,"/","_") => file("${path.module}/athenaviews/${filename}")
  }

  provisioner "local-exec" {
    command = <<EOF
    aws athena start-query-execution \
      --output json \
      --query-string CREATE OR REPLACE VIEW ${each.key} AS ${each.value} \
      --query-execution-context "Database=${var.athena_database}" \
      --result-configuration "OutputLocation=s3://${aws_s3_bucket.my-bucket.bucket}"
EOF
  }

  provisioner "local-exec" {
    when    = "destroy"
    command = <<EOF
    aws athena start-query-execution \
      --output json \
      --query-string DROP VIEW IF EXISTS ${each.key} \
      --query-execution-context "Database=${var.athena_database}" \
      --result-configuration "OutputLocation=s3://${aws_s3_bucket.my-bucket.bucket}"
EOF
  }
}

Также обратите внимание на блок when= "destroy", чтобы убедиться, что просмотры удаляются, когда ваш стек разрывается.

Поместите текстовые файлы с запросом SELECT ниже пути к вашему модулю в каталоге (athenaview/ в этом примере), и он подберет их и создаст представления. Это создаст представления с именем subfolder_filename и уничтожит их, если файлы будут удалены.

Спасибо @Joshua Samuel Отличное дополнение! Я считаю, что мы добавили немало документации в этой области.

tjheslin1 14.10.2019 10:47

Мне нравится этот подход, но: Ошибка: ошибка при выполнении команды 'aws athena start-query-execution --query-string "СОЗДАТЬ ИЛИ ЗАМЕНИТЬ ПРОСМОТР Query1 AS SELECT ac, region FROM meta.getresources" --output json --query-execution -context "Database=meta_resources" --result-configuration "OutputLocation=s3://query-log" ': статус выхода 255. Вывод: использование: aws [опции] <команда> <подкоманда> [<подкоманда> ... ] [параметры] aws help Неизвестные параметры: REPLACE, VIEW, Query1, AS, SELECT, ac,, region, FROM, meta.getresources", ИЛИ ... однако, если я скопирую SQL из вывода CMD, он запустится в моем SQL-клиент

SimonB 27.01.2020 19:55

@SimonB вы должны заключить значение параметра --query-string в кавычки, например. --query-string 'CREATE OR REPLACE VIEW...' но еще лучше заставить AWS CLI загружать исходные файлы, а не загружать их в Terraform: --query-string file://${each.value}

Maciej Majewski 24.02.2020 14:48

@ MaciejMajewski Да, я завернул его, сделал с двойными и одинарными кавычками, та же ошибка. Также загружен весь оператор «Создать» из файла. На какой ты версии? Терраформ v0.12.20

SimonB 02.03.2020 13:56

@SimonB Я использую Terraform v0.12.21. Трудно сказать, с file:// у нас это работает хорошо

Maciej Majewski 04.03.2020 12:12

Работает на меня, но только в первый раз, когда он создан. когда я удалил вид из Афины и попытался воссоздать его, этого не произошло. есть проблема с null_resource? это работает только один раз?

user2768132 23.03.2020 20:56

Основываясь на предыдущих ответах, вот пример, который будет выполнять запросы, только если исходный файл изменился. Кроме того, вместо того, чтобы вставлять SQL-запрос в команду, он использует адаптер file:// для передачи его в команду AWS CLI.

resource "null_resource" "views" {
  for_each = {
    for filename in fileset("${var.sql_files_dir}/", "**/*.sql") :
    replace(replace(filename, "/", "_"), ".sql", "") => "${var.sql_files_dir}/${filename}"
  }

  triggers = {
    md5 = filemd5(each.value)

    # External references from destroy provisioners are not allowed -
    # they may only reference attributes of the related resource.
    database_name = var.database_name
    s3_bucket_query_output = var.s3_bucket_query_output
  }

  provisioner "local-exec" {
    command = <<EOF
      aws athena start-query-execution \
        --output json \
        --query-string file://${each.value} \
        --query-execution-context "Database=${var.database_name}" \
        --result-configuration "OutputLocation=s3://${var.s3_bucket_query_output}"
EOF
  }

  provisioner "local-exec" {
    when    = destroy
    command = <<EOF
      aws athena start-query-execution \
        --output json \
        --query-string 'DROP VIEW IF EXISTS ${each.key}' \
        --query-execution-context "Database=${self.triggers.database_name}" \
        --result-configuration "OutputLocation=s3://${self.triggers.s3_bucket_query_output}"
EOF
  }
}

Чтобы уничтожение работало правильно, называйте файлы точно так же, как имя файла - example.sql относится к запросу:

CREATE OR REPLACE VIEW example AS ...

Другие вопросы по теме