はじめに
AWS Glueでデータパイプラインを構築していると、Decimal型の扱いに悩むことがあります。
特に以下のようなケースでDecimal型からDouble型への変換が必要になることがあります。
- Athenaでのクエリパフォーマンス改善
- 下流システムがDouble型のみ対応
- 精度よりも処理速度を優先したい場合
- レガシーシステムとの連携
手作業で置換するのは面倒ですし、見落としも発生しがちです。そこで、PySpark / Scala / JSONスキーマファイルを一括変換するVBAツールを作成しました。
対応する変換パターン
本ツールは、AWS Glueで使われる様々な型定義パターンに対応しています。
PySpark(Python)
# 変換前
from pyspark.sql.types import DecimalType
schema = StructType([
StructField("price", DecimalType(10, 2)),
StructField("quantity", DecimalType()),
])
df = df.withColumn("amount", col("amount").cast("decimal(18,4)"))
# 変換後
from pyspark.sql.types import DoubleType # ※importの追加は手動で確認
schema = StructType([
StructField("price", DoubleType()),
StructField("quantity", DoubleType()),
])
df = df.withColumn("amount", col("amount").cast("double"))
Scala
// 変換前
import org.apache.spark.sql.types.{DataTypes, DecimalType}
val schema = StructType(Seq(
StructField("price", DataTypes.createDecimalType(10, 2)),
StructField("rate", Decimal(18, 6))
))
// 変換後
import org.apache.spark.sql.types.{DataTypes, DoubleType}
val schema = StructType(Seq(
StructField("price", DataTypes.DoubleType),
StructField("rate", Double)
))
AWS Glue JSONスキーマ
// 変換前
{
"columns": [
{"name": "price", "type": "decimal(10,2)"},
{"name": "rate", "type": "decimal"}
]
}
// 変換後
{
"columns": [
{"name": "price", "type": "double"},
{"name": "rate", "type": "double"}
]
}
機能一覧
| マクロ名 | 機能 | 用途 |
|---|---|---|
ConvertDecimalToDouble_Folder |
フォルダ一括変換 | 複数ファイルをまとめて変換 |
ConvertDecimalToDouble_SingleFile |
単一ファイル変換 | 特定ファイルのみ変換 |
PreviewConversion |
プレビュー | 変換結果を事前確認 |
ConvertWithCustomPatterns |
カスタムパターン | 独自の変換ルールを追加 |
ソースコード
'==============================================================================
' AWS Glue Decimal to Double Converter
' 概要: AWS Glueのソースコード内のDecimal型をDouble型に一括変換するVBAマクロ
'==============================================================================
Option Explicit
'------------------------------------------------------------------------------
' メイン処理: フォルダ内の全ファイルを変換
'------------------------------------------------------------------------------
Public Sub ConvertDecimalToDouble_Folder()
Dim folderPath As String
Dim outputFolder As String
Dim fso As Object
Dim folder As Object
Dim file As Object
Dim convertedCount As Long
Dim totalFiles As Long
' フォルダ選択ダイアログ
With Application.FileDialog(msoFileDialogFolderPicker)
.Title = "変換対象のフォルダを選択してください"
If .Show = -1 Then
folderPath = .SelectedItems(1)
Else
MsgBox "キャンセルされました。", vbInformation
Exit Sub
End If
End With
' 出力フォルダの作成
outputFolder = folderPath & "\converted_" & Format(Now, "yyyymmdd_hhnnss")
Set fso = CreateObject("Scripting.FileSystemObject")
If Not fso.FolderExists(outputFolder) Then
fso.CreateFolder outputFolder
End If
' 対象拡張子
Dim targetExtensions As Variant
targetExtensions = Array(".py", ".scala", ".json", ".txt")
Set folder = fso.GetFolder(folderPath)
' ファイル処理
For Each file In folder.Files
If IsTargetFile(file.Name, targetExtensions) Then
totalFiles = totalFiles + 1
If ConvertFile(file.Path, outputFolder & "\" & file.Name) Then
convertedCount = convertedCount + 1
End If
End If
Next file
' 結果表示
MsgBox "変換完了!" & vbCrLf & _
"対象ファイル数: " & totalFiles & vbCrLf & _
"変換成功: " & convertedCount & vbCrLf & _
"出力先: " & outputFolder, vbInformation
Set fso = Nothing
End Sub
'------------------------------------------------------------------------------
' 単一ファイルの変換
'------------------------------------------------------------------------------
Public Sub ConvertDecimalToDouble_SingleFile()
Dim inputPath As String
Dim outputPath As String
Dim fso As Object
' ファイル選択ダイアログ
With Application.FileDialog(msoFileDialogFilePicker)
.Title = "変換対象のファイルを選択してください"
.Filters.Clear
.Filters.Add "Python Files", "*.py"
.Filters.Add "Scala Files", "*.scala"
.Filters.Add "JSON Files", "*.json"
.Filters.Add "All Files", "*.*"
If .Show = -1 Then
inputPath = .SelectedItems(1)
Else
MsgBox "キャンセルされました。", vbInformation
Exit Sub
End If
End With
Set fso = CreateObject("Scripting.FileSystemObject")
' 出力ファイルパス生成
Dim baseName As String
Dim ext As String
baseName = fso.GetBaseName(inputPath)
ext = fso.GetExtensionName(inputPath)
outputPath = fso.GetParentFolderName(inputPath) & "\" & baseName & "_converted." & ext
' 変換実行
If ConvertFile(inputPath, outputPath) Then
MsgBox "変換完了!" & vbCrLf & "出力先: " & outputPath, vbInformation
Else
MsgBox "変換に失敗しました。", vbExclamation
End If
Set fso = Nothing
End Sub
'------------------------------------------------------------------------------
' ファイル変換処理
'------------------------------------------------------------------------------
Private Function ConvertFile(inputPath As String, outputPath As String) As Boolean
On Error GoTo ErrorHandler
Dim fso As Object
Dim inputFile As Object
Dim outputFile As Object
Dim content As String
Dim convertedContent As String
Dim changeLog As String
Set fso = CreateObject("Scripting.FileSystemObject")
' ファイル読み込み (UTF-8対応)
content = ReadFileUTF8(inputPath)
' 変換実行
convertedContent = ConvertDecimalTypes(content, changeLog)
' ファイル出力 (UTF-8)
WriteFileUTF8 outputPath, convertedContent
' 変更ログをデバッグ出力
If Len(changeLog) > 0 Then
Debug.Print "=== " & inputPath & " ==="
Debug.Print changeLog
End If
ConvertFile = True
Exit Function
ErrorHandler:
Debug.Print "Error in ConvertFile: " & Err.Description
ConvertFile = False
End Function
'------------------------------------------------------------------------------
' Decimal型からDouble型への変換ロジック
'------------------------------------------------------------------------------
Private Function ConvertDecimalTypes(content As String, ByRef changeLog As String) As String
Dim result As String
Dim patterns As Variant
Dim replacements As Variant
Dim i As Long
Dim beforeCount As Long
Dim afterCount As Long
result = content
changeLog = ""
' 変換パターン定義 (AWS Glue / PySpark / Scala 対応)
patterns = Array( _
"DecimalType\(\s*\d*\s*,?\s*\d*\s*\)", _
"DecimalType\(\)", _
"DecimalType", _
"""decimal\(\d+,\s*\d+\)""", _
"""decimal""", _
"'decimal\(\d+,\s*\d+\)'", _
"'decimal'", _
"decimal\(\d+,\s*\d+\)", _
"DataTypes\.createDecimalType\(\s*\d+\s*,\s*\d+\s*\)", _
"DataTypes\.DecimalType", _
"Decimal\s*\(\s*\d+\s*,\s*\d+\s*\)" _
)
replacements = Array( _
"DoubleType()", _
"DoubleType()", _
"DoubleType", _
"""double""", _
"""double""", _
"'double'", _
"'double'", _
"double", _
"DataTypes.DoubleType", _
"DataTypes.DoubleType", _
"Double" _
)
' 正規表現による変換
Dim regex As Object
Set regex = CreateObject("VBScript.RegExp")
regex.Global = True
regex.IgnoreCase = False
For i = LBound(patterns) To UBound(patterns)
regex.Pattern = patterns(i)
' 変換前のマッチ数をカウント
If regex.Test(result) Then
Dim matches As Object
Set matches = regex.Execute(result)
changeLog = changeLog & " " & patterns(i) & " → " & replacements(i) & _
" (" & matches.Count & "件)" & vbCrLf
' 置換実行
result = regex.Replace(result, replacements(i))
End If
Next i
' import文の追加チェック (PySpark)
If InStr(result, "DoubleType") > 0 And InStr(result, "from pyspark.sql.types import") > 0 Then
If InStr(result, "DoubleType") > 0 And InStr(content, "DoubleType") = 0 Then
changeLog = changeLog & " ※DoubleTypeのimportが必要な場合があります" & vbCrLf
End If
End If
Set regex = Nothing
ConvertDecimalTypes = result
End Function
'------------------------------------------------------------------------------
' 対象ファイルかどうかを判定
'------------------------------------------------------------------------------
Private Function IsTargetFile(fileName As String, extensions As Variant) As Boolean
Dim ext As Variant
Dim lowerName As String
lowerName = LCase(fileName)
For Each ext In extensions
If Right(lowerName, Len(ext)) = LCase(ext) Then
IsTargetFile = True
Exit Function
End If
Next ext
IsTargetFile = False
End Function
'------------------------------------------------------------------------------
' UTF-8ファイル読み込み
'------------------------------------------------------------------------------
Private Function ReadFileUTF8(filePath As String) As String
Dim stream As Object
Set stream = CreateObject("ADODB.Stream")
With stream
.Type = 2 ' adTypeText
.Charset = "UTF-8"
.Open
.LoadFromFile filePath
ReadFileUTF8 = .ReadText
.Close
End With
Set stream = Nothing
End Function
'------------------------------------------------------------------------------
' UTF-8ファイル書き込み (BOMなし)
'------------------------------------------------------------------------------
Private Sub WriteFileUTF8(filePath As String, content As String)
Dim stream As Object
Dim binaryStream As Object
' UTF-8で文字列をエンコード
Set stream = CreateObject("ADODB.Stream")
With stream
.Type = 2 ' adTypeText
.Charset = "UTF-8"
.Open
.WriteText content
.Position = 0
.Type = 1 ' adTypeBinary
.Position = 3 ' BOMスキップ
' BOMなしで保存
Set binaryStream = CreateObject("ADODB.Stream")
binaryStream.Type = 1 ' adTypeBinary
binaryStream.Open
binaryStream.Write .Read
binaryStream.SaveToFile filePath, 2 ' adSaveCreateOverWrite
binaryStream.Close
.Close
End With
Set stream = Nothing
Set binaryStream = Nothing
End Sub
'------------------------------------------------------------------------------
' プレビュー機能: 変換結果をシートに出力
'------------------------------------------------------------------------------
Public Sub PreviewConversion()
Dim inputPath As String
Dim content As String
Dim convertedContent As String
Dim changeLog As String
Dim ws As Worksheet
' ファイル選択
With Application.FileDialog(msoFileDialogFilePicker)
.Title = "プレビューするファイルを選択してください"
.Filters.Clear
.Filters.Add "Python Files", "*.py"
.Filters.Add "Scala Files", "*.scala"
.Filters.Add "JSON Files", "*.json"
.Filters.Add "All Files", "*.*"
If .Show = -1 Then
inputPath = .SelectedItems(1)
Else
Exit Sub
End If
End With
' ファイル読み込みと変換
content = ReadFileUTF8(inputPath)
convertedContent = ConvertDecimalTypes(content, changeLog)
' 新しいシートに出力
Set ws = ThisWorkbook.Worksheets.Add
ws.Name = "Preview_" & Format(Now, "hhnnss")
' ヘッダー
ws.Range("A1").Value = "変換前"
ws.Range("B1").Value = "変換後"
ws.Range("C1").Value = "変更内容"
' 内容
ws.Range("A2").Value = content
ws.Range("B2").Value = convertedContent
ws.Range("C2").Value = changeLog
' 書式設定
ws.Columns("A:C").ColumnWidth = 80
ws.Rows("2").WrapText = True
ws.Range("A1:C1").Font.Bold = True
MsgBox "プレビューをシートに出力しました。", vbInformation
End Sub
'------------------------------------------------------------------------------
' カスタムパターン追加機能
'------------------------------------------------------------------------------
Public Sub ConvertWithCustomPatterns()
Dim inputPath As String
Dim outputPath As String
Dim content As String
Dim convertedContent As String
Dim customPattern As String
Dim customReplacement As String
Dim fso As Object
Dim regex As Object
' ファイル選択
With Application.FileDialog(msoFileDialogFilePicker)
.Title = "変換対象のファイルを選択してください"
If .Show = -1 Then
inputPath = .SelectedItems(1)
Else
Exit Sub
End If
End With
' カスタムパターン入力
customPattern = InputBox("追加の検索パターン (正規表現):" & vbCrLf & _
"例: numeric\(\d+,\s*\d+\)", "カスタムパターン")
If customPattern = "" Then
MsgBox "標準パターンのみで変換します。", vbInformation
Else
customReplacement = InputBox("置換文字列:", "置換先", "double")
End If
Set fso = CreateObject("Scripting.FileSystemObject")
' ファイル読み込み
content = ReadFileUTF8(inputPath)
' 標準変換
Dim changeLog As String
convertedContent = ConvertDecimalTypes(content, changeLog)
' カスタムパターン適用
If customPattern <> "" Then
Set regex = CreateObject("VBScript.RegExp")
regex.Global = True
regex.Pattern = customPattern
If regex.Test(convertedContent) Then
Dim matches As Object
Set matches = regex.Execute(convertedContent)
changeLog = changeLog & " [カスタム] " & customPattern & " → " & _
customReplacement & " (" & matches.Count & "件)" & vbCrLf
convertedContent = regex.Replace(convertedContent, customReplacement)
End If
Set regex = Nothing
End If
' 出力
Dim baseName As String
Dim ext As String
baseName = fso.GetBaseName(inputPath)
ext = fso.GetExtensionName(inputPath)
outputPath = fso.GetParentFolderName(inputPath) & "\" & baseName & "_converted." & ext
WriteFileUTF8 outputPath, convertedContent
MsgBox "変換完了!" & vbCrLf & vbCrLf & _
"変更内容:" & vbCrLf & changeLog & vbCrLf & _
"出力先: " & outputPath, vbInformation
Set fso = Nothing
End Sub
使い方
1. VBAモジュールのインポート
- Excelを開き、
Alt + F11でVBAエディタを起動 - メニューから「ファイル」→「ファイルのインポート」を選択
- ダウンロードした
.basファイルを選択
2. マクロの実行
-
Alt + F8でマクロ一覧を表示 - 実行したいマクロを選択して「実行」
3. フォルダ一括変換の場合
1. ConvertDecimalToDouble_Folder を実行
2. 変換対象のフォルダを選択
3. 変換完了後、converted_yyyymmdd_hhnnss フォルダに出力される
4. プレビュー機能
変換前に結果を確認したい場合は PreviewConversion を使用します。
変換前後の内容と変更ログがExcelシートに出力されるため、安心して本番変換を実行できます。
カスタムパターンの追加
プロジェクト固有の型定義がある場合、ConvertWithCustomPatterns で追加の正規表現パターンを指定できます。
例: numeric(\d+,\s*\d+) → double
注意事項
import文の確認
PySpark の場合、DecimalType から DoubleType に変換すると、import文の修正が必要になる場合があります。
# 変換前
from pyspark.sql.types import StructType, StructField, DecimalType
# 変換後(手動で修正が必要)
from pyspark.sql.types import StructType, StructField, DoubleType
変換ログに警告が出力されるので、確認してください。
精度の違い
Decimal型とDouble型では精度が異なります。金融系など高精度が必要な場面では、変換による影響を十分に検討してください。
| 型 | 精度 | 用途 |
|---|---|---|
| Decimal(38,18) | 最大38桁 | 金融計算、会計処理 |
| Double | 約15-17桁 | 科学計算、一般的な数値処理 |
バックアップ
変換前のファイルは上書きされませんが、念のため事前にバックアップを取ることをお勧めします。
動作環境
- Microsoft Excel 2016以降
- Windows 10/11
- VBScript.RegExp(標準搭載)
- ADODB.Stream(標準搭載)
まとめ
AWS Glueのデータパイプライン開発において、型変換は地味ながら重要な作業です。本ツールを使うことで、以下のメリットがあります。
- ✅ 手作業による見落としを防止
- ✅ 複数ファイルを一括処理
- ✅ 変更ログで何が変換されたか把握可能
- ✅ プレビュー機能で安全に確認
ぜひ活用してみてください!
参考リンク
最後までお読みいただきありがとうございました。
いいねやストックをいただけると励みになります!