MW211 EXIT

devlog
MSSQL/FOR XML PATHを復習
2022年05月28日
複数行を横一列にする方法として、MSSQLでは「FOR XML PATH」を用いる。
その意味をもう一度復習

「FOR XML PATH」をつけると、XML形式として横一列に表示ができる。
┌──────────────────────────────────────┐
│WITH [表] AS (                                                              │
│         SELECT * FROM (VALUES ('A'),                                       │
│                               ('B'),                                       │
│                               ('C')) AS [表]([列])                         │
│     )                                                                      │
│SELECT [列]                                                                 │
│    FROM [表]                                                               │
│    FOR XML PATH ('');                                                      │
│--→「<列>A</列><列>B</列><列>C</列>」                                      │
└──────────────────────────────────────┘
通常は列名でタグが付いてくる。

これを、列名[data()]にすると、スペース区切りにすることができる
┌──────────────────────────────────────┐
│WITH [表] AS (                                                              │
│         SELECT * FROM (VALUES ('A'),                                       │
│                               ('B'),                                       │
│                               ('C')) AS [表]([列])                         │
│     )                                                                      │
│SELECT [列] AS [data()]                                                     │
│    FROM [表]                                                               │
│    FOR XML PATH ('');                                                      │
│--→「A B C」                                                               │
└──────────────────────────────────────┘
内側だけにスペースが付くので便利。
このスペースを区切り文字に置換すればよいわけだ。

では列名をなくしてしまえばどうだろう。
どのようなやり方でもよいがAS句で列名を指定しなければないけない形にもっていく。
(以下の場合は人畜無害な空文字を付加する形をとってみた)
┌──────────────────────────────────────┐
│WITH [表] AS (                                                              │
│         SELECT * FROM (VALUES ('A'),                                       │
│                               ('B'),                                       │
│                               ('C')) AS [表]([列])                         │
│     )                                                                      │
│SELECT [列] + ''                                                            │
│    FROM [表]                                                               │
│    FOR XML PATH ('');                                                      │
│--→「ABC」                                                                 │
└──────────────────────────────────────┘
これで列名によるタグがなくなるが、今度は区切りがまったくなくなってしまう。

ということで、自前で区切り文字をあらかじめつけたのが以下。
┌──────────────────────────────────────┐
│WITH [表] AS (                                                              │
│         SELECT * FROM (VALUES ('A'),                                       │
│                               ('B'),                                       │
│                               ('C')) AS [表]([列])                         │
│     )                                                                      │
│SELECT [列] + ','                                                           │
│    FROM [表]                                                               │
│    FOR XML PATH ('');                                                      │
│--→「A,B,C,」                                                              │
└──────────────────────────────────────┘
好きな区切り文字を指定できるが、外側にもついてしまうのが難点。


ちなみに「',' + [列]」と逆にすると、「,A,B,C」になる。
┌──────────────────────────────────────┐
│WITH [表] AS (                                                              │
│         SELECT * FROM (VALUES ('A'),                                       │
│                               ('B'),                                       │
│                               ('C')) AS [表]([列])                         │
│     )                                                                      │
│SELECT STUFF((SELECT ',' + [列]                                             │
│                  FROM [表]                                                 │
│                  FOR XML PATH ('')), 1, 1, '');                            │
│--→「A,B,C」                                                               │
└──────────────────────────────────────┘
そこでSTUFF関数と組み合わせればなんとなくいけるかも。

列名[data()]のやり方だとデータ中に半角スペースが混じっていると
破綻してしまうのだが、以下のような感じで
使われないであろう文字を区切り文字にして回避することができてしまう。
┌──────────────────────────────────────┐
│WITH [表] AS (                                                              │
│         SELECT * FROM (VALUES ('A X'),                                     │
│                               ('B Y'),                                     │
│                               ('C Z')) AS [表]([列])                       │
│     )                                                                      │
│SELECT REPLACE(STUFF((SELECT '\n' + [列]                                    │
│                          FROM [表]                                         │
│                          FOR XML PATH ('')), 1, 2, ''), '\n', CHAR(10));   │
│--→「A X                                                                   │
│      B Y                                                                   │
│      C Z」                                                                 │
└──────────────────────────────────────┘
分類:MSSQL