web-dev-qa-db-ja.com

プログラムで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.

Terraformコードは次のようになります。

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 CLIですが、aws athena [option]には、このオプションはありません。

私はもう試した:

  • create-named-queryCREATE OR REPLACE VIEWこれは、このコマンドの使用目的ではないようです。
  • start-query-execution これは、出力場所を要求します。これは、ステートフルな変更/作成を行うのではなく、データのクエリと結果の出力を目的としていることを示唆しています。 stop-query-execution とペアになっているようにも見えます。
8
tjheslin1

あなたが示唆したように、AWS CLIでstart-query-executionを使用してプログラムでAthenaビューを作成することは間違いなく可能です。ご指摘のとおり、ファイルをチェックする必要がない場合でも、結果のS3の場所を指定する必要があります(Athenaは何らかの理由で空のtxtファイルをその場所に配置します)。

次に例を示します。

$ 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"
    }
}

5
JD D

Athenaでプログラムによってビューを作成することは文書化されておらず、サポートもされていませんが、可能です。 StartQueryExecutionを使用してビューを作成すると、舞台裏で何が起きるかは、AthenaがPrestoにビューを作成させ、Prestoの内部表現を抽出してGlueカタログに配置することです。

古さの問題は通常、Prestoメタデータの列とGlueメタデータが同期していないことに起因します。 Athenaビューには、ビューの3つの説明が実際に含まれています。ビューSQL、Glue形式の列とその型、Presto形式の列と型です。これらのいずれかが同期しなくなった場合、「…は古くなっています。再作成する必要があります」と表示されます。エラー。

これらは、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:_を追加します(_:_の後にスペースを入れます)
  • ビューSQL、列とそのタイプ、およびいくつかのカタログメタデータを含むbase 64エンコードJSON文字列を追加します(以下を参照)
  • サフィックス_*/_を追加します(_*_の前にスペースあり)

ビューを記述するJSONは次のようになります。

  • catalogプロパティは、値awsdatacatalogを持つ必要があります。
  • ビューが作成されるデータベースの名前である必要があるschemaプロパティ(つまり、周囲の接着構造のDatabaseNameプロパティと一致する必要があります。
  • それぞれがnameおよびtypeの列のリスト
  • 実際のビューSQLを含むoriginalSqlプロパティ(_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"
}
_

ここでの注意点の1つは、列のタイプがGlueの名前とほぼ同じであるが完全ではないことです。 Athena/Glueにstringがある場合、このJSONの値はvarcharでなければなりません。 Athena/Glueが_array<string>_を使用する場合、このJSONの値はarray(varchar)である必要があり、_struct<foo:int>_はrow(foo int)になります。

これはかなり厄介で、すべてをまとめるには、いくらかいじってテストする必要があります。これを機能させる最も簡単な方法は、いくつかのビューを作成してデコードし、上記の手順を逆に実行してそれらがどのように見えるかを確認してから、自分で試してみることです。

9
Theo

上記の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という名前のビューが作成され、ファイルが削除されるとビューが破棄されます。

3
Joshua Samuel

Theoの回答への追加:base64エンコードされたJSONファイルでは、cloumn属性を定義するときに「string」タイプは無効です。この時点では常に "varchar"と記述してください。

編集:「int」も「integer」として宣言する必要があります!

私はTheoのソリューションを使用し、AWSクラウド形成テンプレートを使用して機能しました。

ちょっとしたヒントを追加したかったので、デバッグの時間を節約できます。まだコメントする権利がないので、コメントとしてこれを書いていません。これをTheoの回答のコメントセクションにコピーして貼り付けてください。

1
TailorDurden

JD DTheoによる回答に追加し、それらのソリューションを使用して、Teraformを介してAWS Cliを呼び出す方法を以下に示しました。

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 assume-role の出力から必要なフィールドを解析するために使用されます。

aws athena start-query-execution は、定義された環境変数で指定されたロールの下で実行できます。

--result-configuration "OutputLocation=s3://....の代わりに--work-groupを指定できます。これはstart-query-executionの別個のフラグであり、--result-configuration文字列の一部ではないことに注意してください。

1
tjheslin1

以前の回答に基づいて、ソースファイルが変更された場合にのみクエリを実行する例を次に示します。また、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
  }
}

Destroyを正しく機能させるには、filename-example.sqlはクエリに関連します:

CREATE OR REPLACE VIEW example AS ...
1
Maciej Majewski