まず王道な手段から。
1. リレーションシップビュー
- 場所: メニューの「データベースツール」タブ → 「リレーションシップ」
-
機能: テーブル同士の結合線を図で表示してくれます。
→ 外部キーや主キーの関係も一目で分かるので、「どのテーブルがどのテーブルと繋がっているか」が確認できます。 - 用途: SQLのJOINを文字だけで追うよりも、結合の構造を俯瞰するのに便利。
2. クエリデザインビュー
- クエリをSQLビューではなくデザインビューで開くと、結合線がテーブル間に表示されます。
- デザインビューでは、結合条件をダブルクリックして「結合の種類(内部結合/左外部結合など)」も確認・変更可能です。
3. クエリの依存関係ビュー
- 場所: ナビゲーションペインで対象クエリを右クリック → 「オブジェクトの依存関係」
- 機能: 「このクエリはどのテーブルや別クエリを参照しているか」「逆にどのフォームやレポートから参照されているか」をツリー形式で表示してくれます。
- 用途: 複数のサブクエリが重なっている場合に「どの層でどの結合が発生しているか」を把握しやすい。
4. SQLテキストの整理支援(手動)
- Access自体にはSQLフォーマッタ(自動整形機能)は搭載されていません。
- そのため、VBAでクエリのSQLを抽出して整形するツールを作る、あるいは外部エディタ(例えばSQL Pretty PrinterやVSCodeのSQLプラグイン)で読みやすくするのが一般的です。
→ 以前あなたが取り組んでいた「AccessクエリをExcelに出力してシンタックスハイライト」する方法はまさにこの補助的な解決策です。
まとめ
- Access標準で「結合関係を図で確認する」なら → リレーションシップビュー or クエリデザインビュー。
- 「依存関係を俯瞰する」なら → オブジェクトの依存関係ビュー。
- 「SQL自体を見やすくする」なら → 外部整形ツールや自作VBAを活用。
AccessのUNIONクエリを「読む用」に自動整形するVBA(Excel出力つき)
UNION入りはデザインビュー不可なので、SQLテキストを**自動整形(改行・インデント)**して読みやすくするのが現実的です。下のVBAをAccessに貼るだけで、選んだクエリ(または全クエリ)のSQLを整形し、**Excelに1行ずつ出力(折返し・等幅)**します。
※レファレンスで Microsoft VBScript Regular Expressions 5.5 を有効にしてください。
使い方(3手順)
- AccessのVBAエディタ(Alt+F11)で標準モジュールを作成
- 参照設定:
Microsoft VBScript Regular Expressions 5.5
をチェック - マクロ実行:
- あるクエリだけ →
FormatOneQueryToExcel "クエリ名"
- すべて →
FormatAllQueriesToExcel
コード(貼り付け用)
Option Compare Database
Option Explicit
'==============================
' modSQLFormatter
'==============================
' ▼整形の入り口:単一クエリをExcelへ
Public Sub FormatOneQueryToExcel(ByVal queryName As String)
Dim qd As DAO.QueryDef
On Error GoTo EH
Set qd = CurrentDb.QueryDefs(queryName)
ExportSqlPartsToExcel queryName, SplitUnionParts(qd.SQL)
MsgBox "整形完了:" & queryName, vbInformation
Exit Sub
EH:
MsgBox "クエリが見つかりません: " & queryName, vbExclamation
End Sub
' ▼整形の入り口:すべてのSELECT系クエリをExcelへ
Public Sub FormatAllQueriesToExcel()
Dim qd As DAO.QueryDef, parts As Collection
Dim cnt As Long: cnt = 0
For Each qd In CurrentDb.QueryDefs
If IsSelectLike(qd.SQL) Then
Set parts = SplitUnionParts(qd.SQL)
ExportSqlPartsToExcel qd.Name, parts, True '追記モード
cnt = cnt + 1
End If
Next
MsgBox "整形完了:" & cnt & " 件", vbInformation
End Sub
' ==============================
' 判定・分割・整形
' ==============================
Private Function IsSelectLike(ByVal sqlText As String) As Boolean
Dim s As String: s = LCase$(Trim$(sqlText))
IsSelectLike = (Left$(s, 6) = "select" Or Left$(s, 9) = "transform" Or InStr(s, " union ") > 0)
End Function
' ▼UNION/UNION ALLで分割し、各パートを整形して返す
Private Function SplitUnionParts(ByVal sqlText As String) As Collection
Dim re As New RegExp, m As Match, arr As Variant, i As Long
Dim cleaned As String: cleaned = NormalizeWhitespace(sqlText)
Dim result As New Collection
' 「) UNION (」のようなケースも壊さないように、改行を一律正規化した上で区切る
' 正規表現:大文字小文字無視で "UNION ALL" または "UNION" を区切りとして保持
Dim breaker As String
breaker = "(\sUNION\sALL\s|\sUNION\s)"
Set re = New RegExp
re.IgnoreCase = True
re.Global = True
re.Pattern = breaker
Dim tokens As Collection: Set tokens = RegexSplitWithDelimiter(cleaned, re)
' tokens は [part, delimiter, part, delimiter, part, ...] の交互配列
Dim current As String
current = ""
For i = 1 To tokens.Count
If LCase$(Trim$(tokens(i))) = "union" Or LCase$(Trim$(tokens(i))) = "union all" Then
' デリミタ単体で来ることはないように調整済みだが保険
' ここでは無視
ElseIf LCase$(Left$(Trim$(tokens(i)), 5)) = "union" Then
' 例えば "UNION ALL" が一塊で来たケース
' ここでは無視(直前でパート確定済み)
Else
' パート本文
Dim formatted As String
formatted = PrettyPrintSql(tokens(i))
result.Add formatted
End If
Next
' 区切りがない場合(UNIONなし)でも1件返る
Set SplitUnionParts = result
End Function
' ▼SQL整形本体(改行・インデント)
Private Function PrettyPrintSql(ByVal sqlText As String) As String
Dim s As String
s = NormalizeWhitespace(sqlText)
s = AddLineBreaks(s)
s = IndentSql(s)
PrettyPrintSql = s
End Function
' ▼空白正規化(改行→スペース、タブ→スペース、2連続以上を1つ)
Private Function NormalizeWhitespace(ByVal s As String) As String
Dim re As New RegExp
s = Replace(s, vbCr, " ")
s = Replace(s, vbLf, " ")
s = Replace(s, vbTab, " ")
re.IgnoreCase = True
re.Global = True
' -- コメントは残す("--"以降は行頭に改行を入れると可読性UP)
Dim i As Long: i = InStr(1, s, "--", vbTextCompare)
Do While i > 0
s = Left$(s, i - 1) & " " & Mid$(s, i) ' 前にスペースを1つ足すだけ(壊さない)
i = InStr(i + 2, s, "--", vbTextCompare)
Loop
' 連続スペースを1つへ
re.Pattern = "[ ]{2,}"
s = re.Replace(s, " ")
NormalizeWhitespace = Trim$(s)
End Function
' ▼主要キーワードの前に改行を入れる
Private Function AddLineBreaks(ByVal s As String) As String
Dim keys As Variant
keys = Array( _
"SELECT", "FROM", "WHERE", "GROUP BY", "HAVING", "ORDER BY", _
"INNER JOIN", "LEFT JOIN", "RIGHT JOIN", "FULL JOIN", "JOIN", _
"ON", "UNION", "UNION ALL", "TRANSFORM", "PIVOT" _
)
Dim k As Variant
For Each k In keys
s = ReplaceCIKeepSpacing(s, " " & k & " ", vbCrLf & k & " ")
s = ReplaceCIKeepSpacing(s, "(" & k & " ", "(" & vbCrLf & k & " ")
s = ReplaceCIKeepSpacing(s, " " & k & "(", vbCrLf & k & " (")
Next k
' AND / OR は条件の改行にも有効(ただしJOINのON直後は過剰改行になり過ぎない程度に)
s = ReplaceCIKeepSpacing(s, " AND ", vbCrLf & " AND ")
s = ReplaceCIKeepSpacing(s, " OR ", vbCrLf & " OR ")
AddLineBreaks = Trim$(s)
End Function
' 大文字小文字無視で置換(周辺のスペースは壊さない)
Private Function ReplaceCIKeepSpacing(ByVal src As String, ByVal find As String, ByVal repl As String) As String
Dim re As New RegExp
re.IgnoreCase = True
re.Global = True
re.Pattern = EscapeRegex(find)
ReplaceCIKeepSpacing = re.Replace(src, repl)
End Function
' 正規表現用エスケープ
Private Function EscapeRegex(ByVal s As String) As String
Dim chars As Variant, c As Variant
chars = Array("\", ".", "+", "*", "?", "(", ")", "[", "]", "{", "}", "^", "$", "|")
For Each c In chars
s = Replace(s, c, "\" & c)
Next
EscapeRegex = s
End Function
' ▼インデント付与(JOIN/ON/WHERE等で階層化の簡易版)
Private Function IndentSql(ByVal s As String) As String
Dim lines() As String: lines = Split(s, vbCrLf)
Dim i As Long, line As String, buf As String
Dim indent As Long: indent = 0
For i = LBound(lines) To UBound(lines)
line = Trim$(lines(i))
If line = "" Then GoTo AppendLine
' 閉じ括弧が先に来たらデデント
If Left$(line, 1) = ")" Then
indent = Application.Max(0, indent - 1)
End If
buf = buf & String$(indent * 2, " ") & line & vbCrLf
' 開き括弧やJOIN, WHERE, GROUP BY などでインデント微調整
If Right$(line, 1) = "(" Then indent = indent + 1
If LCase$(Left$(line, 9)) = "inner join" _
Or LCase$(Left$(line, 9)) = "left join" _
Or LCase$(Left$(line, 5)) = "join " Then
indent = indent + 1
End If
If LCase$(Left$(line, 6)) = "where " Then indent = indent + 1
If LCase$(Left$(line, 8)) = "group by" Then indent = indent + 1
If LCase$(Left$(line, 8)) = "order by" Then indent = indent + 1
AppendLine:
Next
IndentSql = Trim$(buf)
End Function
' ▼正規表現で区切り文字も保持して分割([part, delimiter, part, ...])
Private Function RegexSplitWithDelimiter(ByVal text As String, ByVal re As RegExp) As Collection
Dim result As New Collection
Dim pos As Long: pos = 1
Dim m As Match
Dim lastEnd As Long: lastEnd = 1
For Each m In re.Execute(text)
' 直前パート
result.Add Mid$(text, lastEnd, m.FirstIndex + 1 - lastEnd)
' デリミタ("UNION"/"UNION ALL")は捨てる=ここでは追加しない
lastEnd = m.FirstIndex + m.Length + 1
Next
' 最終パート
result.Add Mid$(text, lastEnd)
Set RegexSplitWithDelimiter = result
End Function
' ==============================
' Excel出力
' ==============================
Private Sub ExportSqlPartsToExcel(ByVal queryName As String, ByVal parts As Collection, Optional ByVal appendMode As Boolean = False)
Dim xl As Object, wb As Object, ws As Object
Dim i As Long, ln As Variant, r As Long, part As Variant, lines() As String
Set xl = CreateObject("Excel.Application")
xl.Visible = True
If appendMode And xl.Workbooks.Count > 0 Then
Set wb = xl.ActiveWorkbook
Else
Set wb = xl.Workbooks.Add
End If
On Error Resume Next
Set ws = wb.Worksheets("SQL_Formatted")
On Error GoTo 0
If ws Is Nothing Then
Set ws = wb.Worksheets(1)
ws.Name = "SQL_Formatted"
' ヘッダ
ws.Range("A1:D1").Value = Array("QueryName", "UnionPart#", "Line#", "SQL")
ws.Range("A1:D1").Font.Bold = True
ws.Columns("A:D").ColumnWidth = 40
ws.Columns("C:C").ColumnWidth = 8
ws.Columns("D:D").ColumnWidth = 120
ws.Columns("D:D").WrapText = True
ws.Range("D:D").Font.Name = "Consolas"
End If
r = ws.Cells(ws.Rows.Count, "A").End(-4162).Row + 1 ' xlUp=-4162
For i = 1 To parts.Count
lines = Split(parts(i), vbCrLf)
Dim j As Long
For j = LBound(lines) To UBound(lines)
If Trim$(lines(j)) <> "" Then
ws.Cells(r, 1).Value = queryName
ws.Cells(r, 2).Value = i
ws.Cells(r, 3).Value = j + 1
ws.Cells(r, 4).Value = lines(j)
r = r + 1
End If
Next j
' 区切り行
ws.Cells(r, 1).Resize(1, 4).Value = Array(queryName, i, "", "----")
ws.Cells(r, 4).Font.Color = RGB(150, 150, 150)
r = r + 1
Next i
' テーブル風
ws.Range("A1").CurrentRegion.Borders.LineStyle = 1 'xlContinuous
End Sub
補足(調整ポイント)
-
キーワード追加:
AddLineBreaks
のkeys
配列に、よく使う句(DISTINCTROW
、CASE WHEN
など)を足せます。 -
インデント規則:
IndentSql
の if 条件を増やせば、社内流儀に合わせて細かく調整可能。 -
大量のクエリ:
FormatAllQueriesToExcel
はTRANSFORM
(クロス集計)も対象に含めています。不要ならIsSelectLike
で除外してください。 - 書き戻しはしません(閲覧専用)。誤って元SQLを壊さない設計です。
これでできること
- UNIONで分割表示 → 各パートを整形・行番号付きでExcelに出力
- AND/ORの改行とJOIN/ONの階層インデントで、条件の塊が追いやすい
- Excel側で折返し+等幅フォントなので、レビュー配布・画面共有にそのまま使える