[新機能]Databricks の Excel ファイルフォーマットサポートを試してみた

[新機能]Databricks の Excel ファイルフォーマットサポートを試してみた

DatabricksでデフォルトサポートされたネイティブなExcelファイル読み込み機能を、Python・SQL・Auto Loader・COPY INTOの4パターンで試してみました。外部ライブラリ不要での取り込みから制限事項まで、実運用で役立つポイントをまとめています。
2026.07.10

かわばたです。

Databricksの新機能として、Excelファイルフォーマットのネイティブサポートがデフォルトで利用可能になりました。これまでベータ機能としてワークスペース管理者による有効化が必要でしたが、2026年7月7日よりすべてのワークスペースでデフォルト利用可能になっています。

https://docs.databricks.com/aws/en/release-notes/product/2026/july#excel-file-format-support-is-now-available-by-default

https://docs.databricks.com/aws/en/query/formats/excel

外部ライブラリなしで.xls/.xlsx/.xlsmファイルを直接読み込めるようになったので、Python(spark.read.excel)・SQL(read_files)・COPY INTO・Auto Loaderの4パターンの挙動を実際に試してみました。

機能概要

DatabricksのExcelファイルフォーマットサポートは、外部ライブラリを追加することなく.xls.xlsx.xlsmファイルを直接取り込み・解析・クエリできる機能です。従来はopenpyxlpandasなどのライブラリを別途インストールして読み込む必要がありましたが、spark.read.excel()read_files()関数で直接扱えるようになります。

主な特徴は以下の通りです。

  • 4つの取り込み・読み込み方法: Python(spark.read.excel)、SQL(read_files)、COPY INTO、Auto Loader(cloudFiles.format = "excel"
  • シート指定・セル範囲指定: dataAddressオプションで"Sheet1!A1:E10"のようにシートやセル範囲を指定可能
  • スキーマ自動推論: カラム名・データ型を自動推論(headerRowsオプションでヘッダー行数を指定)
  • 数式の計算済み値取得: 数式セルは計算結果の値として読み込まれる
  • シート一覧取得: Pythonのspark.read.excel(..., listSheets=True)、または.option("operation", "listSheets") / SQLのoperation => "listSheets"でシート名一覧を取得可能

spark.read.excel()メソッドのシグネチャは以下の通りです。

excel(path, dataAddress=None, headerRows=None, listSheets=None, dateFormat=None, timestampFormat=None)

また、.option()メソッドで指定可能な主なオプションは以下の通りです。

オプション デフォルト値 説明
dataAddress String なし 読み込むセル範囲(例: Sheet1!C5:H10
headerRows Integer 0 ヘッダーとして使用する先頭行数
ignoreMissingSheet Boolean false 指定シートが存在しないファイルをスキップするか
includePhoneticRuns Boolean false ふりがな・ピンイン等のフォネティック注釈を含めるか
operation String readSheet 操作モード(readSheet or listSheets
dateFormat String yyyy-MM-dd Date型のカスタムフォーマット
timestampNTZFormat String yyyy-MM-dd'T'HH:mm:ss[.SSS] タイムスタンプ(タイムゾーンなし)のカスタムフォーマット

なお、spark.read.excel()のPythonメソッド引数ではtimestampFormatが定義されていますが、.option()で指定するExcel ReaderオプションとしてはtimestampNTZFormatが公式リファレンスに記載されています。

制限事項

2026年7月9日時点での制限事項は以下の通りです。

  • パスワード保護されたExcelファイルは読み込み不可
  • ヘッダー行は1行のみ対応(複数ヘッダー行は非サポート)
  • マージセルは左上のセルのみ値を保持し、他のセルはNULLになる
  • Auto Loaderでのスキーマ進化(Schema Evolution)は非サポート
  • Strict OOXML形式は非サポート
  • .xlsmファイルはデータとして読み込めるが、マクロは実行されない。取り込み処理内でマクロに依存した値更新は行われないため、必要な計算結果は事前にExcel側で保存しておく必要がある
  • ignoreCorruptFilesオプションは非サポート

前提条件

  • Databricks Runtime: 17.1以上({実際のバージョンに置き換えてください}を使用)
  • Databricks ワークスペース: AWS
  • 本機能のステータス: リリースノートでは2026年7月7日に"available by default"と記載。一方、公式Docページ自体は2026年6月12日更新の内容として"Beta"表記が残っているため、ドキュメント間でステータス表記に差分がある状態でした(2026年7月9日確認時点)
    • Databricksのリリースは段階的に展開されるため、環境によっては反映まで時間がかかる可能性があります
  • ストリーミング利用時: Auto Loaderが必要

事前準備

検証用Excelファイルの作成

検証用のExcelファイルをPython(openpyxl)で作成します。基本データ・マージセル・数式・複数シートを含むファイルを用意します。ここでopenpyxlを使用しているのは検証用Excelファイルを作成するためであり、DatabricksでExcelファイルを読み込む処理自体には外部ライブラリを使用していません。
私はDatabricksのNotebookで実行し、ワークスペースに一時的に保存しました。

Excelファイルの作成
%pip install openpyxl

from openpyxl import Workbook
from datetime import date, datetime

wb = Workbook()

# --- Sheet1: 基本データ(ECサイトの注文データ、20行) ---
ws1 = wb.active
ws1.title = "orders"

headers = ["order_id", "customer_name", "email", "order_date", "product", "quantity", "unit_price", "total_price", "status", "region"]
ws1.append(headers)

# order_date を datetime.date から datetime.datetime に変換(openpyxlはdatetime.datetime型を推奨)
data = [
    [1, "Taro Yamada", "taro@example.com", datetime(2025, 1, 5), "Wireless Mouse", 2, 25.00, 50.00, "Shipped", "Japan"],
    [2, "Hanako Sato", "hanako@example.com", datetime(2025, 1, 6), "USB Keyboard", 1, 45.00, 45.00, "Delivered", "Japan"],
    [3, "John Smith", "john@example.com", datetime(2025, 1, 7), "Monitor Stand", 1, 35.00, 35.00, "Shipped", "US"],
    [4, "Emily Chen", "emily@example.com", datetime(2025, 1, 8), "Webcam HD", 3, 60.00, 180.00, "Processing", "US"],
    [5, None, "unknown@example.com", datetime(2025, 1, 9), "Desk Lamp", 1, 30.00, 30.00, "Shipped", "Japan"],
    [6, "Kenji Tanaka", "kenji@example.com", datetime(2025, 1, 10), "Notebook", 5, 12.00, 60.00, "Delivered", "Japan"],
    [7, "Maria Garcia", "maria@example.com", datetime(2025, 1, 11), "Headphones", 1, 80.00, 80.00, "Shipped", "Spain"],
    [8, "Yuki Suzuki", "yuki@example.com", datetime(2025, 1, 12), "Mouse Pad", 2, 15.00, 30.00, "Delivered", "Japan"],
    [9, "Alex Kim", "alex@example.com", datetime(2025, 1, 13), "USB Hub", 1, 25.00, 25.00, "Cancelled", "Korea"],
    [10, "Lisa Wang", "lisa@example.com", datetime(2025, 1, 14), "Cable Organizer", 4, 10.00, 40.00, "Shipped", "China"],
    [11, "Ryo Nakamura", "ryo@example.com", datetime(2025, 1, 15), "Wireless Mouse", 1, 25.00, 25.00, "Delivered", "Japan"],
    [12, "Sophie Brown", "sophie@example.com", datetime(2025, 1, 16), "Keyboard Cover", 2, 18.00, 36.00, "Shipped", "UK"],
    [13, None, "guest@example.com", datetime(2025, 1, 17), "Screen Cleaner", 3, 8.00, 24.00, "Processing", "Japan"],
    [14, "David Lee", "david@example.com", datetime(2025, 1, 18), "Laptop Stand", 1, 55.00, 55.00, "Shipped", "US"],
    [15, "Mika Ito", "mika@example.com", datetime(2025, 1, 19), "Wireless Charger", 1, 40.00, 40.00, "Delivered", "Japan"],
    [16, "Tom Wilson", "tom@example.com", datetime(2025, 1, 20), "Desk Mat", 1, 28.00, 28.00, "Shipped", "US"],
    [17, "Aiko Watanabe", "aiko@example.com", datetime(2025, 1, 21), "USB Keyboard", -1, 45.00, -45.00, "Returned", "Japan"],
    [18, "Carlos Ruiz", "carlos@example.com", datetime(2025, 1, 22), "Monitor Stand", 2, 35.00, 70.00, "Delivered", "Mexico"],
    [19, "Hana Kim", "hana@example.com", datetime(2025, 1, 23), "Webcam HD", 1, 60.00, 60.00, "Shipped", "Korea"],
    [20, "Takeshi Oda", "takeshi@example.com", datetime(2025, 1, 24), "Headphones", 1, 80.00, 80.00, "Processing", "Japan"],
]

for row in data:
    ws1.append(row)

# --- Sheet2: マージセルと数式を含むサマリーシート ---
ws2 = wb.create_sheet(title="summary")

# マージセル(A1:C1)
ws2.merge_cells("A1:C1")
ws2["A1"] = "注文サマリーレポート"

ws2["A3"] = "指標"
ws2["B3"] = "値"

ws2["A4"] = "総注文数"
ws2["B4"] = 20

ws2["A5"] = "合計金額"
# 数式セル
ws2["B5"] = "=SUM(orders!H2:H21)"

ws2["A6"] = "平均単価"
ws2["B6"] = "=AVERAGE(orders!G2:G21)"

# ファイル保存(ワークスペースのExcelフォルダに保存)
save_path = "/Workspace/Users/<id>/Excel/test_excel_data.xlsx"
wb.save(save_path)
print(f"検証用Excelファイルを作成しました: {save_path}")

2026-07-10_17h25_31

ファイルのアップロード

作成したExcelファイルをUnity Catalogボリュームにアップロードします。

# Unity Catalog ボリュームにコピー
dbutils.fs.cp(
    "file:/Workspace/Users/<id>/Excel/test_excel_data.xlsx",
    "dbfs:/Volumes/<catalog>/<schema>/<volume>/test_excel_data.xlsx"
)

2026-07-10_17h20_33

2026-07-10_17h21_38

Excelの内容

2026-07-10_17h44_15

2026-07-10_17h45_03

試してみた

Python(spark.read.excel)で基本読み込み

まずは最小構成でspark.read.excel()を使ってExcelファイルを読み込みます。

# 最小構成での読み込み
volume_path = "/Volumes/<catalog>/<schema>/<volume>/test_excel_data.xlsx"

df = spark.read.excel(volume_path)
df.show(5)

2026-07-10_17h45_44

headerRowsを指定しない場合(デフォルト: 0)、1行目のヘッダーもデータ行として扱われます。headerRows=1を指定してヘッダーを認識させます。

# ヘッダー行を指定して読み込み
df = spark.read.excel(volume_path, headerRows=1)
df.show(5)
df.printSchema()

2026-07-10_17h46_57

ヘッダー行がカラム名として認識され、データ型も自動推論されていれば問題ありません。

次に、dataAddressオプションでシートとセル範囲を指定して読み込みます。

# シート名とセル範囲を指定して読み込み
df_range = spark.read.excel(
    volume_path,
    dataAddress="orders!A1:E10",
    headerRows=1
)
df_range.show()

2026-07-10_17h48_03

order_idproductの5列、9行のデータが取得できていればOKです。

シート一覧の取得

Pythonのspark.read.excel()ではlistSheets=Trueを指定すると、ファイル内のシート名一覧を取得できます。.option()形式やSQLのread_filesでは、operation => "listSheets"を指定します。

# シート一覧を取得
df_sheets = spark.read.excel(volume_path, listSheets=True)
df_sheets.show()

2026-07-10_17h48_53

orderssummaryの2つのシート名が表示されれば問題ありません。

SQL(read_files)で基本読み込み

SQLのread_files関数を使って同じExcelファイルを読み込みます。

-- 基本読み込み
SELECT *
FROM read_files(
  '/Volumes/<catalog>/<schema>/<volume>/test_excel_data.xlsx',
  format => 'excel',
  headerRows => 1,
  schemaEvolutionMode => 'none'
)
LIMIT 5;

2026-07-10_17h52_31

Python版と同じデータが取得できていれば問題ありません。

-- シートとセル範囲を指定
SELECT *
FROM read_files(
  '/Volumes/<catalog>/<schema>/<volume>/test_excel_data.xlsx',
  format => 'excel',
  headerRows => 1,
  dataAddress => 'orders!A1:F10',
  schemaEvolutionMode => 'none'
)

2026-07-10_17h53_58

order_idquantityの6列、9行のデータが取得できていれば問題ありません。

-- summaryシートの読み込み
SELECT *
FROM read_files(
  '/Volumes/<catalog>/<schema>/<volume>/test_excel_data.xlsx',
  format => 'excel',
  headerRows => 1,
  dataAddress => 'summary!A3:B6',
  schemaEvolutionMode => 'none'
)

2026-07-10_17h55_08

summaryシートの指標名と値が取得できていれば問題ありません。

Auto Loaderでのストリーミング取り込み

Auto Loaderを使って、Excelファイルのストリーミング取り込みを試します。新しいExcelファイルがアップロードされると自動的に取り込まれる仕組みです。

まず、ソースディレクトリに検証用Excelファイルを配置します。

# ソースディレクトリにファイルを配置
source_path = "/Volumes/<catalog>/<schema>/<volume>/excel_autoloader_demo/input/"

dbutils.fs.cp(
    "dbfs:/Volumes/<catalog>/<schema>/<volume>/test_excel_data.xlsx",
    f"dbfs:{source_path}test_excel_data.xlsx"
)

Auto Loaderでストリーミング読み込みを実行します。入力ファイル置き場とcheckpoint/schemaLocationは分けておくのが運用上おすすめです。

# Auto Loader でストリーミング読み込み
base_path = "/Volumes/<catalog>/<schema>/<volume>/excel_autoloader_demo"

source_path = f"{base_path}/input/"
schema_path = f"{base_path}/schema/"
checkpoint_path = f"{base_path}/checkpoint/"
target_table = "<catalog>.<schema>.excel_streaming_demo"

df_stream = (spark.readStream
    .format("cloudFiles")
    .option("cloudFiles.format", "excel")
    .option("cloudFiles.inferColumnTypes", "true")
    .option("headerRows", 1)
    .option("cloudFiles.schemaLocation", schema_path)
    .option("cloudFiles.schemaEvolutionMode", "none")
    .load(source_path)
)

# availableNow=True で現時点のファイルを一括処理(検証向け)
query = (df_stream.writeStream
    .format("delta")
    .option("checkpointLocation", checkpoint_path)
    .trigger(availableNow=True)
    .outputMode("append")
    .table(target_table)
)

query.awaitTermination()

2026-07-10_18h05_06

取り込みが完了したら、テーブルの内容を確認します。

SELECT * FROM <catalog>.<schema>.excel_streaming_demo LIMIT 10;

2026-07-10_18h05_33
今回はdataAddressを指定していないため、Excelファイル内の最初のシートであるordersシートが取り込まれます。ordersシートの20行のデータがDeltaテーブルに取り込まれていれば問題ありません。

COPY INTOによるテーブルへの取り込み

COPY INTOを使うと、ExcelファイルをDeltaテーブルに直接取り込むことができます。SQLウェアハウスからの利用が中心の場合に便利です。

CREATE TABLE IF NOT EXISTS <catalog>.<schema>.excel_copy_into_demo (
  order_id BIGINT,
  customer_name STRING,
  email STRING,
  order_date TIMESTAMP_NTZ,
  product STRING,
  quantity BIGINT,
  unit_price DECIMAL(10,2),
  total_price DECIMAL(10,2),
  status STRING,
  region STRING
);

COPY INTO <catalog>.<schema>.excel_copy_into_demo
FROM '/Volumes/<catalog>/<schema>/<volume>/test_excel_data.xlsx'
FILEFORMAT = EXCEL
FORMAT_OPTIONS (
  'headerRows' = '1',
  'dataAddress' = 'orders!A1:J21'
)
COPY_OPTIONS (
  'mergeSchema' = 'true'
);

2026-07-10_18h11_34

SELECT * FROM <catalog>.<schema>.excel_copy_into_demo LIMIT 5;

2026-07-10_18h12_00

ordersシートのデータがDeltaテーブルに取り込まれていれば問題ありません。

制限事項の検証

公式Docに記載されている制限事項の挙動を実際に確認します。

マージセルの挙動

summaryシートにはA1:C1がマージされたセルがあります。この挙動を確認します。

# マージセルを含む範囲を読み込み
df_merge = spark.read.excel(
    volume_path,
    dataAddress="summary!A1:C2",
    headerRows=0
)
df_merge.show()

2026-07-10_18h13_29

マージセルの左上(A1)にのみ「注文サマリーレポート」の値が入り、B1・C1はNULLになっていればドキュメント通りの挙動です。

数式セルの挙動

summaryシートのB5セル(=SUM(orders!H2:H21))やB6セル(=AVERAGE(orders!G2:G21))が計算済みの値として取得されるかを確認します。このデータの場合、期待値は以下です。

  • 合計金額(SUM): 948.0
  • 平均単価(AVERAGE): 36.55
# 数式を含むセル範囲を読み込み
df_formula = spark.read.excel(
    volume_path,
    dataAddress="summary!A3:B6",
    headerRows=1
)
df_formula.show()

2026-07-10_18h16_10

キャッシュされていない可能性があるので、実際のExcelアプリケーションで一度開いて保存し直したファイルで検証します。

2026-07-10_18h18_40

無事に期待通りの出力結果が得られました。

複数シートの扱い

Excelファイル内の全シートを一度に読み込む機能はなく、1回の読み込みで対象になるのは1シートのみです。dataAddressを指定しない場合、デフォルトでは最初のシートの左上から右下の非空セルまでが読み込まれます。

# dataAddress未指定(デフォルト: 最初のシート)
df_default = spark.read.excel(volume_path, headerRows=1)
print(f"カラム: {df_default.columns}")
print(f"行数: {df_default.count()}")

2026-07-10_18h20_25

ordersシート(最初のシート)のカラムと20行のデータが返れば問題ありません。

全シートを処理したい場合は、listSheetsでシート一覧を取得してからループで各シートを読み込む方法が取れます。

# 全シートをループで読み込む
sheets_df = spark.read.excel(volume_path, listSheets=True)
sheet_info = [(row["sheetIndex"], row["sheetName"]) for row in sheets_df.collect()]

for sheet_index, sheet_name in sheet_info:
    print(f"\n--- {sheet_name} (シートインデックス: {sheet_index}) ---")
    # dataAddressにシート名のみを指定するとシート全体を読み込めます
    # ここではシート名を含む範囲指定を使用(範囲を広めに指定)
    df_sheet = spark.read.excel(
        volume_path,
        dataAddress=f"{sheet_name}!A1:Z1000",
        headerRows=1
    )
    df_sheet.show(5)

2026-07-10_18h28_20

各シートのフォーマットが異なるので、添付のような形になりましたがフォーマットが同じであればループさせて読み込みができそうです。

最後に

DatabricksのExcelファイルフォーマットサポートを試して、Python(spark.read.excel)、SQL(read_files)、COPY INTO、Auto Loaderの4パターンでの読み込みと、制限事項の挙動を確認できました。

外部ライブラリのインストールが不要になったのは大きなメリットで、特にSQLのread_files関数でサクッと読み込めるのは便利だと感じました。

注意点としては、マージセルの挙動(左上セルのみ値保持)やパスワード保護ファイルの非対応、Auto Loaderでのスキーマ進化非サポートあたりは把握しておくとよさそうです。

この記事が何かの参考になれば幸いです!

この記事をシェアする

関連記事