Skip to content

確認問題|Professional ⑨ データモデリング(配点 6%)

教材 09-data-modeling.md の内容に基づく確認問題です(全10問)。 まず問題を解き、下の「解答・解説」で答え合わせをしてください。

問題

Q1. SCD Type 1 と Type 2 の違いについて、正しい説明はどれか。

  • A. Type 1 は変更のたびに古い値を新しい値で上書きし、各レコードの最新バージョンのみを保持して履歴を残さない。Type 2 は各バージョンを別行として保持して完全な履歴を残し、__START_AT / __END_AT が各バージョンの有効期間を定義し、アクティブ行は __END_AT = NULL。ターゲットが SCD Type 1 のときはこれらの列は付かない
  • B. Type 1 が履歴を保持し、Type 2 が最新バージョンのみを保持する
  • C. Type 2 では __START_AT / __END_AT にレコードの取り込み時刻が必ず入り、シーケンス値は入らない
  • D. Type 2 のアクティブ行は __END_AT に将来の日付(例 9999-12-31)が入る

Q2. ソース形式に応じた API の使い分けとして、正しい説明はどれか。

  • A. どちらの場合も AUTO CDC を使い、FROM SNAPSHOT は SQL のオプション句として指定する
  • B. スナップショットしか出さないソースには手書きの MERGE INTO を使うしかない
  • C. ソースが変更フィードを出す場合(CDF 有効な Delta、Debezium / Oracle GoldenGate 等の RDB CDC、シーケンス列付きの挿入/更新/削除ストリーム)は AUTO CDC(SQL の AUTO CDC ... INTO / Python の create_auto_cdc_flow()、SQL・Python 両対応)。ソースが CDC 非対応で定期スナップショット(全テーブルダンプ)のみの場合は AUTO CDC FROM SNAPSHOTcreate_auto_cdc_from_snapshot_flow()Python のみ)。どちらでも現在の状態のみ必要なら SCD Type 1、監査/ポイントインタイム/傾向分析で全履歴が必要なら SCD Type 2 を選ぶ
  • D. AUTO CDCAPPLY CHANGES とは構文が全く異なるため、旧コードは書き換えが必要である

Q3. シーケンス列(SEQUENCE BY)の要件と順序外イベントの扱いについて、正しい説明はどれか。

  • A. シーケンス列は単調減少でもよく、NULL も許容される
  • B. シーケンス列は単調増加の表現で、各シーケンス値でキーごとに 1 更新となり、NULL は不可並べ替え可能な型が必須。この列で定義した順序でイベントを処理するため、遅延/順序外レコードも正しい最終状態に収束する。複数列で順序付けたい場合は STRUCT(SQL: SEQUENCE BY STRUCT(timestamp_col, id_col) / Python: sequence_by = struct("timestamp_col", "id_col"))で結合し、最初のフィールド優先・同値なら次のフィールドで判定する
  • C. 順序外レコードは無視されるため、ソース側で必ず順序を保証する必要がある
  • D. 複数列で順序付ける場合は SEQUENCE BY col1, col2 のようにカンマ区切りで指定する

Q4. 次の CDC 入力に AUTO CDC ... STORED AS SCD TYPE 1KEYS (userId), SEQUENCE BY sequenceNum, APPLY AS DELETE WHEN operation = "DELETE")を適用したとき、userId = 125 の最終行はどうなるか。

userIdnamecityoperationsequenceNum
125MercedesTijuanaINSERT2
125MercedesGuadalajaraUPDATE6
125MercedesMexicaliUPDATE5
  • A. city = Tijuana(最初の INSERT が残る)
  • B. city = Mexicali(最後に到着した行が採用される)
  • C. 3 行すべてが残る(Type 1 でも履歴が保持される)
  • D. city = GuadalajarasequenceNum = 6 が最大なので、順序外で後から到着した sequenceNum = 5 の Mexicali は破棄される)

Q5. SCD Type 2 で「追跡する列を限定する」機能について、正しい説明はどれか。

  • A. 既定ではどの列が変わっても新しいバージョンを作るが、TRACK HISTORY ON(SQL)/ track_history_column_list / track_history_except_column_list(Python) で追跡する列を限定できる。追跡対象外の列の変更は新履歴を作らず現在バージョンを上書きするため、重要属性の履歴を残しつつストレージとクエリの複雑度を削減できる
  • B. TRACK HISTORY ON を指定すると、指定した列以外の変更でも必ず新しいバージョンが作られる
  • C. 追跡列の限定は SCD Type 1 でのみ利用でき、Type 2 では使えない
  • D. 追跡対象外の列が変更されると、その行は削除される

Q6. AUTO CDC FROM SNAPSHOT の制限と 2 つの処理パターンについて、正しい説明はどれか。

  • A. スナップショット間の中間変更もすべてキャプチャできるため、日次スナップショットでも 1 日内の複数変更を追跡できる
  • B. スナップショットは到着順に処理されるため、順序外に到着したスナップショットも必ず処理される
  • C. あるスナップショットから次への変更のみを認識し、中間変更は取得しない(例: 日次スナップショットで 1 日に住所を A→B→C と 2 回変えると、変更フィードは A→C に直接飛ぶ)。スナップショットはバージョン昇順で処理する必要があり、順序外のスナップショットは無視される。処理パターンは①パイプライン取り込み時間を使う(実行時にスナップショットを読み、取り込み時間をバージョンとする。定期的・順番に到着する場合)、②バージョン関数を使う(DataFrame, version_number) のタプルを返す関数を指定し、バージョン番号の昇順で処理。新規なしなら None。複数同時到着・順序外到着・順序を明示制御したい場合)
  • D. AUTO CDC FROM SNAPSHOT は初期読み込み専用で、継続処理には使えない

Q7. 変更データフィード(CDF)と AUTO CDC の役割の違いについて、正しい説明はどれか。

  • A. CDF は SCD Type 1 / 2 を自動計算する高レベル API で、AUTO CDC は行レベル変更を記録する低レベル機能である
  • B. CDF はテーブルの行レベル変更を記録・読み取る低レベル機能(変更を出す/取り込む)。AUTO CDC は変更を受けて SCD Type 1 / 2 を適用しテーブルを再具体化する高レベル API(変更を適用して整合したディメンションを作る)。公式チュートリアルも「CDF の主用途はパイプライン内の変更キャプチャであり、最初からテーブル変更の完全ビューを作ることではない。順序外イベントがあると実装が複雑になるが、Lakeflow パイプライン(AUTO CDC)は _sequence_by に基づき順序外レコードを自動処理してこの複雑さを解消する」と述べている
  • C. CDF と AUTO CDC は同じ機能の別名であり、SQL では CDF、Python では AUTO CDC と呼ぶ
  • D. AUTO CDC のターゲットは CDF を出力できないため、下流パイプラインは AUTO CDC の出力の変更を消費できない

Q8. CDF の有効化とメタデータ列について、正しい説明はどれか。

  • A. レガシー CDF と自動 CDF は併用できるため、両方を有効にしておくとよい
  • B. メタデータ列は _change_type / _commit_id / _commit_user の 3 つである
  • C. 自動 CDF は書き込み時に変更を記録するため、MERGE / UPDATE の書き込み性能が低下する
  • D. レガシー CDF はテーブルへの書き込み時に変更を記録し Delta Lake のみ対応で、delta.enableChangeDataFeed = true による個別テーブルでの明示的な有効化が必要自動 CDF(パブリックプレビュー) は行系列メタデータを使いクエリ(読み取り)時に変更を計算するため個別のテーブル構成が不要で、書き込みごとに変更を計算しないためレガシーより書き込み性能が高くストレージコストが低い(要件: DBR 18 以降、Unity Catalog 登録、行追跡有効。Delta Lake と Iceberg v3 で動作)。両方式は同じ読み取り API(readChangeFeed / table_changes())を使うが併用不可。メタデータ列は _change_typeinsert / update_preimage / update_postimage / delete)/ _commit_version / _commit_timestamp で、スキーマに同名列があると CDF は使えない

Q9. CDF の読み取りと保持について、正しい説明はどれか。

  • A. バッチ読み取りでは開始バージョンを省略でき、省略すると全履歴が返る
  • B. ストリーミング読み取りの初回起動時は、変更データのみが返され既存データは返らない
  • C. Databricks は構造化ストリーミング+CDF による増分処理を推奨(バージョンを自動追跡)。ストリーム初回起動時、CDF はテーブルの最新スナップショットを INSERT レコードとして返し、以降を変更データとして返す。ターゲットに既に特定時点までの変更が入っている場合は startingVersion を指定して既存状態を INSERT として再処理しないようにする。バッチ読み取りには開始バージョンが必須で、CDF 有効化前のバージョンを指定するとエラー。CDF は恒久的な変更記録ではなく、有効化後に発生した変更のみを指定保持期間内でのみアクセスでき、バージョン削除後は読めない(恒久履歴が必要なら trigger.AvailableNow などで別テーブルへ増分アーカイブする)。列の名称変更・削除・型変更・NULL 許容変更(非加法スキーマ変更)を跨ぐ範囲は読めないため範囲を分割する
  • D. CDF は有効化前に発生した変更も遡ってすべて読み取れる

Q10. メダリオンアーキテクチャにおけるデータモデリングの位置づけと、CDC API の要件について正しい説明はどれか。

  • A. 正規化は Gold 層で行い、ディメンショナルモデル(スタースキーマ)は Silver 層で作る
  • B. Bronze は生データを元の形式で保存し検証は最小限(削除データを防ぎスキーマ変更から守るため多くのフィールドを string / VARIANT / binary で保存するのが推奨)。Silver はクレンジング・検証・重複除去・順序外/遅延データの解決を行い、ここでデータモデリングを開始して正規化する(取り込みから直接 Silver に書くのは非推奨。Bronze からの読み取りはストリーミング読み取りを基本にする)。Goldディメンショナルモデリング(スタースキーマ)と集計を行い、集計はマテリアライズドビューで事前計算する。SCD はこのディメンションモデルの実装技法で、CDC により時間経過に対して安定したサロゲートキーが使える。CDC API は Pro または Advanced エディション(あるいはサーバーレス) のパイプラインが必要で、AUTO CDC は Apache Spark 宣言パイプラインでは非サポート
  • C. CDC API は Standard エディションのパイプラインでも利用でき、エディションによる制限はない
  • D. Gold 層には大量の履歴データを具体化し、Silver 層では集計済みデータのみを保持する

解答・解説

Q1. 正解: AType 1 は変更のたびに古い値を新しい値で上書きし最新バージョンのみを保持する(履歴なし)。使いどころは、現在の状態のみ必要/下流マテビューを増分リフレッシュ/結合に安定サロゲートキーが必要な場合。Type 2 はメタデータでタイムスタンプ付き複数バージョンを保持して完全な履歴を残し、__START_AT / __END_AT が各バージョンの有効期間を定義し、アクティブ行は __END_AT = NULL(D が誤り)。使いどころは監査/規制で履歴が必要/エンティティの進化を理解/ポイントインタイムレポート/傾向分析。SCD Type 2 では SEQUENCE BY のシーケンス値がそのまま __START_AT / __END_AT に伝播される(C が誤り)。ターゲットが SCD Type 1 のときはこれらの列は付かない。B は Type 1 と Type 2 が逆。

Q2. 正解: C ソースが変更フィードを出すなら AUTO CDC(SQL の AUTO CDC ... INTO / Python の create_auto_cdc_flow())、CDC 非対応で定期スナップショットのみなら AUTO CDC FROM SNAPSHOTcreate_auto_cdc_from_snapshot_flow()Python のみサポートで SQL 非対応)。A は FROM SNAPSHOT が SQL のオプション句であるという点で誤り。B は誤りで、まさにこのケースのために AUTO CDC FROM SNAPSHOT がある(従来はカスタム MERGE INTO に依存していたが、ステージングテーブル・ウィンドウ関数・シーケンス前提が必要で複雑・エラーが起きやすかった)。D は誤りで、AUTO CDCAPPLY CHANGES を置き換えるが構文は同一(旧称の APPLY CHANGES / create_apply_changes_flow も動作するが AUTO CDC が推奨)。

Q3. 正解: B シーケンス列は「各シーケンス値でキーごとに 1 更新」となる単調増加の表現である必要があり、NULL は不可並べ替え可能な型が必須(A が誤り)。AUTO CDC はこの列で定義した順序でイベントを処理し、順序外レコードを自動処理するため、遅延/順序外イベントも正しい最終状態に収束する(C が誤り)。複数列で順序付けたい場合(タイムスタンプ+ID でタイブレークするなど)は STRUCT で結合し、最初のフィールド優先・同値なら次のフィールドで判定する(SQL: SEQUENCE BY STRUCT(timestamp_col, id_col)、Python: sequence_by = struct("timestamp_col", "id_col"))ので D は誤り。SCD Type 2 ではシーケンス値がターゲットの __START_AT / __END_AT に伝播される。

Q4. 正解: D SCD Type 1 は最新のみを保持する。userId = 125 のイベントは sequenceNum = 2(Tijuana / INSERT)、5(Mexicali / UPDATE、順序外で後から到着)、6(Guadalajara / UPDATE)。シーケンス列で定義した順序で処理されるため、最大のシーケンス値 6 の Guadalajara が最終状態になり、順序外で後から到着した sequenceNum = 5 の Mexicali は破棄される。これが「順序外イベントをシーケンス値で決定論的に正しく処理する」ということ。B の「最後に到着した行が採用される」は到着順に依存する誤り。C は Type 1 が履歴を持たない点で誤り。なお同じ入力を SCD Type 2 にすると、125 は「Tijuana(2→5)」「Mexicali(5→6)」「Guadalajara(6→null)」の 3 バージョンとして履歴に残る。

Q5. 正解: A SCD Type 2 は既定ではどの列が変わっても新しいバージョンを作るが、TRACK HISTORY ON {cols | * EXCEPT(cols)}(SQL)/ track_history_column_list / track_history_except_column_list(Python) で追跡する列を限定できる。追跡対象外の列の変更は新履歴を作らず現在バージョンを上書きする(B・D が誤り)ため、重要属性の履歴を残しつつストレージとクエリの複雑度を削減できる。公式例で city を追跡対象外にすると、userId = 123 は 1 行(Chihuahua, 1→6)、125 は 1 行(Guadalajara, 2→null)に集約される。C は誤りで、この機能は Type 2 の履歴追跡を制御するものである。

Q6. 正解: CAUTO CDC FROM SNAPSHOT は連続するスナップショットを比較して挿入/更新/削除を識別し、差分から合成変更フィードを生成して AUTO CDC と同じ SCD Type 1 / 2 ロジックを適用する。重要な制限として、あるスナップショットから次への変更のみを認識し中間変更は取得しない(日次スナップショットで 1 日に住所を A→B→C と 2 回変えると変更フィードは A→C に直接飛ぶ。A が誤り)。またスナップショットはバージョン昇順で処理する必要があり、順序外のスナップショットは無視される(B が誤り)。初期読み込みだけではなく、スナップショットが唯一の形式である場合の継続処理用でもある(D が誤り)。処理パターンは①パイプライン取り込み時間ベース(定期的・順番に到着する場合)、②バージョン関数ベース((DataFrame, version_number) のタプルを返す関数を渡し、複数同時到着・順序外到着・順序の明示制御が必要な場合)。

Q7. 正解: B 役割の違いは頻出。CDF は Delta / Iceberg v3 テーブルのバージョン間の行レベル変更を記録・読み取る低レベル機能で、用途は増分 ETL・監査証跡・データレプリケーション。AUTO CDC は変更を受けて SCD Type 1 / 2 を適用しテーブルを再具体化する高レベル API。A・C は両者を取り違えている。D は誤りで、AUTO CDC のターゲットは独自の CDF を出力でき、下流パイプラインが AUTO CDC 出力の変更を消費できる(他にも、標準ストリーミングテーブルと異なり AUTO CDC ターゲット(UC テーブル)はパイプライン実行中でも INSERT / UPDATE / DELETE / MERGE をサポートし、実行ごとに num_upserted_rows / num_deleted_rows のメトリクスが自動キャプチャされる)。

Q8. 正解: D Databricks は 2 方式をサポートする。レガシー CDF は書き込み時に変更を記録し Delta Lake のみ対応で、delta.enableChangeDataFeed = true(テーブルプロパティ)による個別有効化が必要。自動 CDF(パブリックプレビュー) は行系列メタデータを使いクエリ(読み取り)時に変更を計算するため個別のテーブル構成が不要で、MERGE / UPDATE の書き込みごとに変更を計算しないため書き込み性能が高くストレージコストが低い(C が逆)。要件は DBR 18 以降・Unity Catalog 登録・行追跡有効で、Delta Lake と Iceberg v3 で動作する。両方式は同じ読み取り API を使うが併用不可(A が誤り)。メタデータ列は _change_typeinsert / update_preimage(更新前値)/ update_postimage(更新後値)/ delete)/ _commit_version / _commit_timestamp(B が誤り)で、スキーマにこれらと同名の列があると CDF は使えない(有効化前に列名変更で解消する)。

Q9. 正解: C Databricks は構造化ストリーミング+CDF による増分処理を推奨(バージョンを自動追跡)。ストリーム初回起動時、CDF はテーブルの最新スナップショットを INSERT レコードとして返し、以降を変更データとして返す(B が誤り)。ターゲットに既に特定時点までの変更が入っている場合(チェックポイント破損からの復旧など)は startingVersion を指定して既存状態を INSERT として再処理しないようにする。バッチ読み取りには開始バージョンが必須で、CDF 有効化前のバージョンを指定するとエラーになる(A・D が誤り)。CDF は恒久的な変更記録ではなく、有効化後に発生した変更のみを指定保持期間内でのみアクセスでき、バージョン削除後は読めない(マネージドテーブルは履歴を自動クリーンアップするので指定した開始バージョンは最終的に削除される)。恒久履歴が必要なら trigger.AvailableNow などで CDF から新テーブルへ増分書き込みしてアーカイブする。非加法スキーマ変更(列の名称変更・削除・型変更・NULL 許容変更)を跨ぐ範囲は読めないため範囲を分割する。範囲外バージョンは既定で timestampGreaterThanLatestCommit エラー(spark.databricks.delta.changeDataFeed.timestampOutOfRange.enabled = true で緩和可)。

Q10. 正解: B メダリオンの各層の役割は明確に区別する。Bronze は生データを元の形式で保存・維持し、データ検証は最小限(削除データを確実に防ぎスキーマ変更から守るため多くのフィールドを string / VARIANT / binary で保存することが推奨。_metadata.file_name などメタデータ列を付与することも)。Silver は 1 つ以上の Bronze / Silver テーブルから読み、スキーマ適用・null 処理・重複除去・順序外/遅延到着データの解決・品質チェック・型キャスト・結合を行い、ここでデータモデリングを開始して正規化する(取り込みから直接 Silver に書くのは非推奨で、Bronze からの読み取りはストリーミング読み取りを基本にし、バッチ読み取りは小さなディメンションテーブルに限定)。Goldディメンショナルモデリング(スタースキーマ)と集計を行い、集計はマテリアライズドビューで事前計算する。大量の履歴データは通常 Silver でアクセスし Gold には具体化しない(A・D が誤り)。SCD はディメンションモデルの実装技法で、CDC により時間経過に対して安定したサロゲートキーが使える。CDC API は Pro または Advanced エディション(あるいはサーバーレス) のパイプラインが必要で、AUTO CDC は Apache Spark 宣言パイプラインでは非サポート(C が誤り)。