MySQL(mysqlコマンド)の実行結果をXMLにしてPowerShellで扱う

この記事は、先日書いた以下の記事のMySQL(mysqlコマンド)版です。

https://qiita.com/noobow/items/25efab18a2836abb668d

# まずは結果から
サンプルスクリプトは以下のGistに格納しました。
https://gist.github.com/noobow34/42384dc343f2df44793b3bff7261fc70

https://gist.github.com/noobow34/42384dc343f2df44793b3bff7261fc70

```powershell
PS C:\> .\Get-MySqlXmlData.ps1                                                      
-----------行を指定して使用する-----------

SEQ2 SEQ
---- ---
3    2
-----------行と列を指定して使用する-----------
3
-----------ループで処理する場合-----------
1:2
2:3
3:4
4:5
5:6
6:7
7:8
8:9
9:10
10:11
```

# やりかた
## 扱うデータ
Oracle版で使ったのと同じ結果になるSQLをChatGPTに作ってもらいました。
```sql
WITH RECURSIVE Numbers AS (
    SELECT 1 AS SEQ
    UNION ALL
    SELECT SEQ + 1 FROM Numbers WHERE SEQ < 10
)
SELECT SEQ, SEQ + 1 AS SEQ2 FROM Numbers;
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149348/85383b5c-6ec7-4d3e-b7b3-6559dbb2eb9c.png)
```powershell
PS C:\> @"
>> WITH RECURSIVE Numbers AS (
>>     SELECT 1 AS SEQ
>>     UNION ALL
>>     SELECT SEQ + 1 FROM Numbers WHERE SEQ < 10
>> )
>> SELECT SEQ, SEQ + 1 AS SEQ2 FROM Numbers;
>> "@ | mysql -u user
SEQ     SEQ2
1       2
2       3
3       4
4       5
5       6
6       7
7       8
8       9
9       10
10      11
```

## mysqlの出力をXMLにする
なんとmysqlコマンドは`--xml`オプションをつけるだけで出力がXMLになります。簡単じゃん！と思ったのですが、出力が以下のようになります。
```xml
PS C:\> @"
>> WITH RECURSIVE Numbers AS (
>>     SELECT 1 AS SEQ
>>     UNION ALL
>>     SELECT SEQ + 1 FROM Numbers WHERE SEQ < 10
>> )
>> SELECT SEQ, SEQ + 1 AS SEQ2 FROM Numbers;
>> "@ | mysql -u user --xml
<?xml version="1.0"?>

<resultset statement="WITH RECURSIVE Numbers AS (
    SELECT 1 AS SEQ
    UNION ALL
    SELECT SEQ + 1 FROM Numbers WHERE SEQ &lt; 10
)
SELECT SEQ, SEQ + 1 AS SEQ2 FROM Numbers" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <row>
        <field name="SEQ">1</field>
        <field name="SEQ2">2</field>
  </row>

  <row>
        <field name="SEQ">2</field>
        <field name="SEQ2">3</field>
  </row>

  <row>
        <field name="SEQ">3</field>
        <field name="SEQ2">4</field>
  </row>

  <row>
        <field name="SEQ">4</field>
        <field name="SEQ2">5</field>
  </row>

  <row>
        <field name="SEQ">5</field>
        <field name="SEQ2">6</field>
  </row>

  <row>
        <field name="SEQ">6</field>
        <field name="SEQ2">7</field>
  </row>

  <row>
        <field name="SEQ">7</field>
        <field name="SEQ2">8</field>
  </row>

  <row>
        <field name="SEQ">8</field>
        <field name="SEQ2">9</field>
  </row>

  <row>
        <field name="SEQ">9</field>
        <field name="SEQ2">10</field>
  </row>

  <row>
        <field name="SEQ">10</field>
        <field name="SEQ2">11</field>
  </row>
</resultset>
```
各列が列名のタグではなくfieldタグでname属性に列名という形なので、そのままXML型でPowerShellの変数に入れても`$row.SEQ`の様に列名で扱うことができません。
そこで、簡単に列名で扱えるようにするためにカスタムオブジェクトに変換する関数をかませることにしました。この関数はChatGPTにサクッと作ってもらいました。

```powershell
function Convert-XmlToCustomObject {
    param (
        [Parameter(ValueFromPipeline)] [xml]$xml
    )

    process {
         # すべての <row> 要素をカスタムオブジェクトに変換
        $rows = $xml.resultset.row | ForEach-Object {
            $properties = @{}
            
            # 各 <field> 要素の name 属性をプロパティ名に、#text の値をプロパティ値にする
            $_.field | ForEach-Object {
                $properties[$_.name] = $_.'#text'
            }

            # PSCustomObject に変換
            [PSCustomObject]$properties
        }
        return $rows
    }
}
```

mysqlコマンドの結果を上記の関数に渡せばカスタムオブジェクトの出来上がりです。
```powershell
$xml = [xml]($execSql | mysql -u user --xml) | Convert-XmlToCustomObject
```

# カスタムオブジェクトの扱い方
`$xml`自体が行の配列になっているので`$xml[0]`の様にインデックスを指定して扱います。あとは、`$xml[0].SEQ`の様に列名をそのままプロパティとして扱えます。