powershellでspクエリパースしてIO出力するコード

【powershellでspクエリパースしてIO出力するコード】

外部コードソースがセキュリティ問題で落とせなそうなのでDeep Research使って作ってみた。

```
# PowerShell スクリプト：SQLファイルから INSERT/UPDATE/DELETE を抽出し Excel に出力

# --- 1. 必要な設定 ---
# ScriptDom DLL のパス（ユーザー環境に合わせて修正）
$scriptDomPath = "C:\Path\To\Microsoft.SqlServer.TransactSql.ScriptDom.dll"
Add-Type -Path $scriptDomPath

# 処理対象の SQL ファイルがあるフォルダ（再帰検索）
$sqlFolder = "C:\Path\To\SqlFiles"

# 出力先 Excel ファイルパス
$outputExcel = "C:\Path\To\Output.xlsx"

# --- 2. パーサー設定 ---
# SQL Server 2019 以降向け TSqlParser を作成（QuotedIdentifiers は false でも良い）
$parser = New-Object Microsoft.SqlServer.TransactSql.ScriptDom.TSql150Parser($false)

# --- 3. 結果格納用配列 ---
$results = @()

# --- 4. 再帰関数：BEGIN/IF/WHILE 等の中から DML 文を抽出 ---
function Get-DmlStatements($stmts) {
    $list = @()
    foreach ($st in $stmts) {
        # 直接 DML 文なら追加
        if ($st -is [Microsoft.SqlServer.TransactSql.ScriptDom.InsertStatement] -or
            $st -is [Microsoft.SqlServer.TransactSql.ScriptDom.UpdateStatement] -or
            $st -is [Microsoft.SqlServer.TransactSql.ScriptDom.DeleteStatement]) {
            $list += $st
        }
        # ブロック文や IF/WHILE/Try なら内部を再帰処理
        if ($st -is [Microsoft.SqlServer.TransactSql.ScriptDom.BlockStatement]) {
            $list += Get-DmlStatements $st.StatementList.Statements
        }
        elseif ($st -is [Microsoft.SqlServer.TransactSql.ScriptDom.IfStatement]) {
            # If の Then, Else 部をチェック
            if ($st.ThenStatement -ne $null) {
                $list += Get-DmlStatements $st.ThenStatements
            }
            if ($st.ElseStatement -ne $null) {
                $list += Get-DmlStatements $st.ElseStatements
            }
        }
        elseif ($st -is [Microsoft.SqlServer.TransactSql.ScriptDom.WhileStatement]) {
            $list += Get-DmlStatements $st.StatementList.Statements
        }
        elseif ($st -is [Microsoft.SqlServer.TransactSql.ScriptDom.TryCatchStatement]) {
            $list += Get-DmlStatements $st.TryStatements
            $list += Get-DmlStatements $st.CatchStatements
        }
    }
    return $list
}

# --- 5. ファイル処理: 各 .sql を解析 ---
$sqlFiles = Get-ChildItem -Path $sqlFolder -Filter *.sql -Recurse
foreach ($file in $sqlFiles) {
    $sqlText = Get-Content $file.FullName -Raw
    $errors = New-Object System.Collections.Generic.List[Microsoft.SqlServer.TransactSql.ScriptDom.ParseError]
    $reader = New-Object System.IO.StringReader($sqlText)
    $scriptFragment = $parser.Parse($reader, [ref]$errors)
    if ($errors.Count -gt 0) {
        Write-Warning "Parsing errors in file $($file.Name): $($errors.Count) errors"
        continue
    }
    # スクリプト内のバッチからストアドプロシージャ定義を探す
    if ($scriptFragment -is [Microsoft.SqlServer.TransactSql.ScriptDom.TSqlScript]) {
        foreach ($batch in $scriptFragment.Batches) {
            foreach ($stmt in $batch.Statements) {
                if ($stmt -is [Microsoft.SqlServer.TransactSql.ScriptDom.CreateProcedureStatement]) {
                    $proc = $stmt
                    # プロシージャ名取得 (スキーマ名付きなら"dbo.proc" 形式)
                    $schemaId = $proc.ProcedureReference.Name.SchemaIdentifier
                    $procName = $proc.ProcedureReference.Name.BaseIdentifier.Value
                    if ($schemaId -ne $null) {
                        $procName = "$($schemaId.Value).$procName"
                    }
                    # BEGIN...END 内の全 DML 文を取得
                    $dmlList = Get-DmlStatements $proc.StatementList.Statements
                    foreach ($dml in $dmlList) {
                        $row = [PSCustomObject]@{
                            ファイル名     = $file.Name
                            プロシージャ名 = $procName
                            文タイプ       = ""  # 後でセット
                            対象テーブル   = ""
                            対象カラム     = ""
                            条件カラム     = ""
                            条件値         = ""
                        }
                        if ($dml -is [Microsoft.SqlServer.TransactSql.ScriptDom.InsertStatement]) {
                            # INSERT 文
                            $row.文タイプ = "INSERT"
                            $spec = $dml.InsertSpecification
                            # 対象テーブル名 (スキーマ名付き)
                            $tref = $spec.Target.SchemaObject
                            $schemaId = $tref.SchemaIdentifier
                            $tableId = $tref.BaseIdentifier.Value
                            if ($schemaId -ne $null) { $row.対象テーブル = "$($schemaId.Value).$tableId" }
                            else { $row.対象テーブル = $tableId }
                            # 対象カラム (Insert 文で指定されている列)
                            $cols = @()
                            foreach ($colRef in $spec.Columns) {
                                # ColumnReferenceExpression を文字列化
                                $cols += ($colRef.MultiPartIdentifier.Identifiers | ForEach-Object Value) -join "."
                            }
                            $row.対象カラム = ($cols -join ",")
                            # INSERT は通常 WHERE なし (空白)
                        }
                        elseif ($dml -is [Microsoft.SqlServer.TransactSql.ScriptDom.UpdateStatement]) {
                            # UPDATE 文
                            $row.文タイプ = "UPDATE"
                            $spec = $dml.UpdateSpecification
                            # 対象テーブル
                            $tref = $spec.Target.SchemaObject
                            $schemaId = $tref.SchemaIdentifier
                            $tableId = $tref.BaseIdentifier.Value
                            if ($schemaId -ne $null) { $row.対象テーブル = "$($schemaId.Value).$tableId" }
                            else { $row.対象テーブル = $tableId }
                            # 対象カラム (UPDATE のセット句左辺)
                            $setCols = @()
                            foreach ($setClause in $spec.SetClauses) {
                                if ($setClause -is [Microsoft.SqlServer.TransactSql.ScriptDom.AssignmentSetClause]) {
                                    $colExpr = $setClause.Column
                                    if ($colExpr -ne $null) {
                                        $name = ($colExpr.MultiPartIdentifier.Identifiers | ForEach-Object Value) -join "."
                                        $setCols += $name
                                    }
                                }
                            }
                            $row.対象カラム = ($setCols -join ",")
                            # WHERE 句の条件列・値を取得
                            if ($spec.WhereClause -ne $null) {
                                $condCols = @(); $condVals = @()
                                function Parse-Condition($cond) {
                                    if ($cond -is [Microsoft.SqlServer.TransactSql.ScriptDom.BooleanComparisonExpression]) {
                                        $left = $cond.FirstExpression
                                        $right = $cond.SecondExpression
                                        # 左辺が列参照、右辺が定数または文字列の場合に抽出
                                        if ($left -is [Microsoft.SqlServer.TransactSql.ScriptDom.ColumnReferenceExpression]) {
                                            $colName = ($left.MultiPartIdentifier.Identifiers | ForEach-Object Value) -join "."
                                            # 右辺がリテラルならその値を取得 (他も単純に ToString)
                                            if ($right -is [Microsoft.SqlServer.TransactSql.ScriptDom.StringLiteral] -or
                                                $right -is [Microsoft.SqlServer.TransactSql.ScriptDom.IntegerLiteral] -or
                                                $right -is [Microsoft.SqlServer.TransactSql.ScriptDom.NumericLiteral]) {
                                                $val = $right.Value
                                            }
                                            else {
                                                $val = $right.ToString()
                                            }
                                            $condCols += $colName
                                            $condVals += $val
                                        }
                                    }
                                    elseif ($cond -is [Microsoft.SqlServer.TransactSql.ScriptDom.BooleanBinaryExpression]) {
                                        Parse-Condition $cond.FirstExpression
                                        Parse-Condition $cond.SecondExpression
                                    }
                                }
                                Parse-Condition $spec.WhereClause.SearchCondition
                                $row.条件カラム = ($condCols -join ",")
                                $row.条件値   = ($condVals -join ",")
                            }
                        }
                        elseif ($dml -is [Microsoft.SqlServer.TransactSql.ScriptDom.DeleteStatement]) {
                            # DELETE 文
                            $row.文タイプ = "DELETE"
                            $spec = $dml.DeleteSpecification
                            # 対象テーブル
                            $tref = $spec.Target.SchemaObject
                            $schemaId = $tref.SchemaIdentifier
                            $tableId = $tref.BaseIdentifier.Value
                            if ($schemaId -ne $null) { $row.対象テーブル = "$($schemaId.Value).$tableId" }
                            else { $row.対象テーブル = $tableId }
                            # DELETE は対象カラム無し
                            # WHERE 句の条件列・値を取得
                            if ($spec.WhereClause -ne $null) {
                                $condCols = @(); $condVals = @()
                                function Parse-Condition($cond) {
                                    if ($cond -is [Microsoft.SqlServer.TransactSql.ScriptDom.BooleanComparisonExpression]) {
                                        $left = $cond.FirstExpression
                                        $right = $cond.SecondExpression
                                        if ($left -is [Microsoft.SqlServer.TransactSql.ScriptDom.ColumnReferenceExpression]) {
                                            $colName = ($left.MultiPartIdentifier.Identifiers | ForEach-Object Value) -join "."
                                            if ($right -is [Microsoft.SqlServer.TransactSql.ScriptDom.StringLiteral] -or
                                                $right -is [Microsoft.SqlServer.TransactSql.ScriptDom.IntegerLiteral] -or
                                                $right -is [Microsoft.SqlServer.TransactSql.ScriptDom.NumericLiteral]) {
                                                $val = $right.Value
                                            }
                                            else {
                                                $val = $right.ToString()
                                            }
                                            $condCols += $colName
                                            $condVals += $val
                                        }
                                    }
                                    elseif ($cond -is [Microsoft.SqlServer.TransactSql.ScriptDom.BooleanBinaryExpression]) {
                                        Parse-Condition $cond.FirstExpression
                                        Parse-Condition $cond.SecondExpression
                                    }
                                }
                                Parse-Condition $spec.WhereClause.SearchCondition
                                $row.条件カラム = ($condCols -join ",")
                                $row.条件値   = ($condVals -join ",")
                            }
                        }
                        # 結果リストに追加
                        $results += $row
                    }
                }
            }
        }
    }
}

# --- 6. Excel に出力 ---
# Excel COMオブジェクト起動
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
$workbook = $excel.Workbooks.Add()
$sheet = $workbook.Worksheets.Item(1)

# ヘッダー行
$headers = @("ファイル名","プロシージャ名","文タイプ","対象テーブル","対象カラム","条件カラム","条件値")
for ($i=0; $i -lt $headers.Count; $i++) {
    $sheet.Cells.Item(1, $i+1) = $headers[$i]
}

# データ行書き込み
$rowIndex = 2
foreach ($rec in $results) {
    $sheet.Cells.Item($rowIndex, 1) = $rec.ファイル名
    $sheet.Cells.Item($rowIndex, 2) = $rec.プロシージャ名
    $sheet.Cells.Item($rowIndex, 3) = $rec.文タイプ
    $sheet.Cells.Item($rowIndex, 4) = $rec.対象テーブル
    $sheet.Cells.Item($rowIndex, 5) = $rec.対象カラム
    $sheet.Cells.Item($rowIndex, 6) = $rec.条件カラム
    $sheet.Cells.Item($rowIndex, 7) = $rec.条件値
    $rowIndex++
}

# 列幅自動調整（任意）
$sheet.Columns.AutoFit()

# ファイル保存 (51 = xlOpenXMLWorkbook)
$workbook.SaveAs($outputExcel, 51)
$excel.Quit()
```