' =====================================================
' Excel データチェック自動ツール — 完成版
' 説明: このモジュールは業務で使える「データチェック自動ツール」です。
' - 指定したシートのデータをスキャンして検査ルールに従いエラー/警告を抽出
' - 問題箇所をレポートシートに出力
' - 各種ルールはモジュール上の「設定」部分で簡単に変更可能
' 使い方:
' 1. このコードを標準モジュールに貼り付ける(例: Module1)
' 2. データは 「Data」 という名前のシートに下記のようなヘッダーを用意する
' 例: A:ID, B:Name, C:Date, D:Amount, E:Status
' 3. 設定を必要に応じて編集してから、RunDataCheck を実行
' =====================================================
Option Explicit
' --------------------
' === 設定セクション ===
' --------------------
' データシート名
Private Const DATA_SHEET_NAME As String = "Data"
' レポート出力先シート名(既存なら上書き)
Private Const REPORT_SHEET_NAME As String = "DataCheck_Report"
' ヘッダー行番号(0 ではなく 1 ベース)
Private Const HEADER_ROW As Long = 1
'
' 必須カラム(ヘッダー名を指定)
Private RequiredColumns As Variant
' 一意キーとしてチェックするカラム名(重複を検出)
Private UniqueKeyColumns As Variant
'
' データ型チェック(カラム名 -> 種類)
' 支持される型: "Long", "Double", "Date", "String"
Private TypeChecks As Variant
'
' 範囲チェック(カラム名 -> {min, max})
Private RangeChecks As Variant
'
' 値集合チェック(カラム名 -> Array of allowed values)
Private AllowedValues As Variant
'
' 初期化(デフォルト設定)
Private Sub InitConfig()
RequiredColumns = Array("ID", "Name", "Date", "Amount")
UniqueKeyColumns = Array("ID")
TypeChecks = Array(Array("ID", "Long"), _
Array("Name", "String"), _
Array("Date", "Date"), _
Array("Amount", "Double"), _
Array("Status", "String"))
RangeChecks = Array(Array("Amount", 0, 1000000)) ' Amount は 0〜1,000,000
AllowedValues = Array(Array("Status", Array("Open", "Closed", "Pending")))
End Sub
' --------------------
' === 実行メイン関数 ===
' --------------------
Public Sub RunDataCheck()
Dim wb As Workbook: Set wb = ThisWorkbook
Dim sh As Worksheet
On Error GoTo ErrHandler
InitConfig
Set sh = wb.Worksheets(DATA_SHEET_NAME)
Dim headerMap As Object: Set headerMap = BuildHeaderMap(sh, HEADER_ROW)
If headerMap.Count = 0 Then
MsgBox "ヘッダーが見つかりません。シート名・ヘッダー行を確認してください。", vbExclamation
Exit Sub
End If
Dim lastRow As Long: lastRow = GetLastRow(sh)
If lastRow <= HEADER_ROW Then
MsgBox "データが存在しません。", vbInformation
Exit Sub
End If
' レポートシート準備
Dim rpt As Worksheet: Set rpt = PrepareReportSheet(wb)
Dim rptRow As Long: rptRow = 2 ' ヘッダー分を避ける
' 一意キー用ディクショナリ
Dim dictKeys As Object: Set dictKeys = CreateObject("Scripting.Dictionary")
' ループ処理(行ごと)
Dim r As Long
For r = HEADER_ROW + 1 To lastRow
Dim issues As Collection: Set issues = New Collection
' 必須チェック
PerformRequiredCheck sh, headerMap, r, issues
' 型チェック
PerformTypeChecks sh, headerMap, r, issues
' 範囲チェック
PerformRangeChecks sh, headerMap, r, issues
' 値集合チェック
PerformAllowedValueChecks sh, headerMap, r, issues
' 一意キーチェック
PerformUniqueKeyChecks sh, headerMap, r, dictKeys, issues
' カスタムチェック: ここに業務ロジックを追加できます
' 例: 日付が未来日になっていないか、金額がマイナスでないか など
PerformCustomChecks sh, headerMap, r, issues
' もし issues があればレポートに書き出す
If issues.Count > 0 Then
Dim i As Long
For i = 1 To issues.Count
rpt.Cells(rptRow, 1).Value = r ' 元の行番号
rpt.Cells(rptRow, 2).Value = issues(i) ' メッセージ
rptRow = rptRow + 1
Next i
End If
Next r
' レポート整形
FormatReportSheet rpt
MsgBox "データチェックが完了しました。レポート: " & REPORT_SHEET_NAME, vbInformation
Exit Sub
ErrHandler:
MsgBox "実行中にエラーが発生しました: " & Err.Description, vbCritical
End Sub
' --------------------
' === ヘルパ関数 ===
' --------------------
' ヘッダー名->列番号 を作る
Private Function BuildHeaderMap(sh As Worksheet, headerRow As Long) As Object
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
Dim c As Long, lastCol As Long
lastCol = sh.Cells(headerRow, sh.Columns.Count).End(xlToLeft).Column
For c = 1 To lastCol
Dim h As String: h = Trim(CStr(sh.Cells(headerRow, c).Value))
If Len(h) > 0 Then dict(LCase(h)) = c
Next c
Set BuildHeaderMap = dict
End Function
' 最終行取得(A列基準)
Private Function GetLastRow(sh As Worksheet) As Long
GetLastRow = sh.Cells(sh.Rows.Count, 1).End(xlUp).Row
End Function
' レポートシートを作る(存在する場合はクリア)
Private Function PrepareReportSheet(wb As Workbook) As Worksheet
Dim rpt As Worksheet
On Error Resume Next
Set rpt = wb.Worksheets(REPORT_SHEET_NAME)
On Error GoTo 0
If Not rpt Is Nothing Then
Application.DisplayAlerts = False
rpt.Delete
Application.DisplayAlerts = True
End If
Set rpt = wb.Worksheets.Add(After:=wb.Worksheets(wb.Worksheets.Count))
rpt.Name = REPORT_SHEET_NAME
' ヘッダー
rpt.Cells(1, 1).Value = "Row"
rpt.Cells(1, 2).Value = "Issue"
rpt.Cells(1, 3).Value = "Sheet"
rpt.Cells(1, 4).Value = "CheckedAt"
rpt.Cells(1, 5).Value = "Extra"
Set PrepareReportSheet = rpt
End Function
' レポート整形
Private Sub FormatReportSheet(rpt As Worksheet)
With rpt
.Columns("A:E").AutoFit
.Rows(1).Font.Bold = True
.Cells(2, 4).Resize(.Rows.Count - 1, 1).Value = Now
End With
End Sub
' --------------------
' === 各チェック関数 ===
' --------------------
Private Sub PerformRequiredCheck(sh As Worksheet, headerMap As Object, r As Long, issues As Collection)
Dim i As Long
For i = LBound(RequiredColumns) To UBound(RequiredColumns)
Dim colName As String: colName = RequiredColumns(i)
Dim key As String: key = LCase(colName)
If Not headerMap.Exists(key) Then
issues.Add "必須カラムが見つかりません: " & colName
Else
Dim colNum As Long: colNum = headerMap(key)
If Trim(CStr(sh.Cells(r, colNum).Value)) = "" Then
issues.Add "必須値欠落: '" & colName & "' が空です"
End If
End If
Next i
End Sub
Private Sub PerformTypeChecks(sh As Worksheet, headerMap As Object, r As Long, issues As Collection)
Dim i As Long
For i = LBound(TypeChecks) To UBound(TypeChecks)
Dim colName As String: colName = TypeChecks(i)(0)
Dim wantType As String: wantType = TypeChecks(i)(1)
Dim key As String: key = LCase(colName)
If headerMap.Exists(key) Then
Dim colNum As Long: colNum = headerMap(key)
Dim val As Variant: val = sh.Cells(r, colNum).Value
If Not IsEmpty(val) And Trim(CStr(val)) <> "" Then
Select Case LCase(wantType)
Case "long"
If Not IsNumeric(val) Or CLng(val) <> val Then issues.Add "型不一致: '" & colName & "' は整数である必要があります"
Case "double"
If Not IsNumeric(val) Then issues.Add "型不一致: '" & colName & "' は数値である必要があります"
Case "date"
If Not IsDate(val) Then issues.Add "型不一致: '" & colName & "' は日付である必要があります"
Case "string"
' OK
Case Else
' 未知の型指定は無視
End Select
End If
End If
Next i
End Sub
Private Sub PerformRangeChecks(sh As Worksheet, headerMap As Object, r As Long, issues As Collection)
Dim i As Long
For i = LBound(RangeChecks) To UBound(RangeChecks)
Dim colName As String: colName = RangeChecks(i)(0)
Dim minVal As Variant: minVal = RangeChecks(i)(1)
Dim maxVal As Variant: maxVal = RangeChecks(i)(2)
Dim key As String: key = LCase(colName)
If headerMap.Exists(key) Then
Dim colNum As Long: colNum = headerMap(key)
Dim val As Variant: val = sh.Cells(r, colNum).Value
If IsNumeric(val) Then
If val < minVal Or val > maxVal Then issues.Add "範囲外: '" & colName & "' は " & minVal & "〜" & maxVal & " の範囲にある必要があります"
End If
End If
Next i
End Sub
Private Sub PerformAllowedValueChecks(sh As Worksheet, headerMap As Object, r As Long, issues As Collection)
Dim i As Long
For i = LBound(AllowedValues) To UBound(AllowedValues)
Dim colName As String: colName = AllowedValues(i)(0)
Dim allowed As Variant: allowed = AllowedValues(i)(1)
Dim key As String: key = LCase(colName)
If headerMap.Exists(key) Then
Dim colNum As Long: colNum = headerMap(key)
Dim val As Variant: val = CStr(sh.Cells(r, colNum).Value)
If Trim(val) <> "" Then
If Not IsInArray(val, allowed) Then issues.Add "不正な値: '" & colName & "' は許容値 (" & Join(allowed, ",") & ") のいずれかである必要があります"
End If
End If
Next i
End Sub
Private Sub PerformUniqueKeyChecks(sh As Worksheet, headerMap As Object, r As Long, dictKeys As Object, issues As Collection)
Dim i As Long
If UBound(UniqueKeyColumns) < LBound(UniqueKeyColumns) Then Exit Sub
Dim keyVal As String: keyVal = ""
For i = LBound(UniqueKeyColumns) To UBound(UniqueKeyColumns)
Dim colName As String: colName = UniqueKeyColumns(i)
Dim key As String: key = LCase(colName)
If headerMap.Exists(key) Then
Dim colNum As Long: colNum = headerMap(key)
keyVal = keyVal & "|" & CStr(sh.Cells(r, colNum).Value)
Else
' カラム missing は上の必須チェックで拾う
End If
Next i
If Trim(keyVal) <> "" Then
If dictKeys.Exists(keyVal) Then
issues.Add "重複キー: 一意キーが重複しています (" & keyVal & ")"
Else
dictKeys.Add keyVal, r
End If
End If
End Sub
Private Sub PerformCustomChecks(sh As Worksheet, headerMap As Object, r As Long, issues As Collection)
' ここに業務固有の検査を追加できます。
' 例: Date が未来日になっていないかをチェック
Dim key As String: key = LCase("Date")
If headerMap.Exists(key) Then
Dim c As Long: c = headerMap(key)
Dim v As Variant: v = sh.Cells(r, c).Value
If IsDate(v) Then
If CDate(v) > Date Then issues.Add "日付エラー: 'Date' が未来日になっています"
End If
End If
' 例: Amount がマイナスになっていないか
key = LCase("Amount")
If headerMap.Exists(key) Then
c = headerMap(key)
v = sh.Cells(r, c).Value
If IsNumeric(v) Then
If CDbl(v) < 0 Then issues.Add "金額エラー: 'Amount' が負の値です"
End If
End If
End Sub
' --------------------
' === ユーティリティ ===
' --------------------
Private Function IsInArray(val As Variant, arr As Variant) As Boolean
Dim i As Long
For i = LBound(arr) To UBound(arr)
If CStr(arr(i)) = CStr(val) Then
IsInArray = True
Exit Function
End If
Next i
IsInArray = False
End Function
' =====================================================
' End of module
' =====================================================
' --------------------
' 使い方メモ(モジュール内に追記)
' --------------------
' ・カラム名が異なる場合は InitConfig の RequiredColumns / TypeChecks / RangeChecks / AllowedValues を編集してください。
' ・チェックの追加は PerformCustomChecks に書き足してください。
' ・大量行を高速に処理したい場合は、Cells を直接参照するのではなく配列に読み込んで処理し、最後に書き戻す方法に変更すると速くなります。
' ・このツールはログ出力をシンプルにしています。必要ならレポートに "Sheet" や対象セルの値などを追加してください。
VB
Java
Java | 実務で for文 と foreach/Stream をどう使い分けるか

