SQL查询xml到文本重新格式化

时间:2014-01-15 08:18:47

标签: sql sql-server xml xquery

我正在尝试使用sqlserver xquery语法重新格式化sql xml列中的值。

对于每个流派标记,我希望“G”以字符串形式写入, 内部类型取决于图书的标记”_“如果absend或”。如果可用,没有考虑到价值。 输出字符串必须与xml完全相同。

这是我输入的xml

<Lib>
  <Genre name=Horror>
     <Book>
        <Title>1</Title>
     </Book>
     <Book>
        <Title>2</Title>
        <Lost>1</Lost>
     </Book>
     <Book>
        <Title>3</Title>
     </Book>
  </Genre>
  <Genre name=Romance>
     <Book>
        <Title>5</Title>
     </Book>
     <Book>
        <Title>6</Title>
        <Lost>0</Lost>
     </Book>
  </Genre>
</Lib>

我想要实现的输出是:

G_._G_.

2 个答案:

答案 0 :(得分:2)

一种解决方案是循环播放流派和书籍以构建结果:

for $genre in //Genre
return (
  "G",
  for $book in $genre/Book
  return (
    if ($book/Lost)
    then "."
    else "_"
  )
)

Try on sqlfiddle.

由于您需要按文档顺序返回字母,您可以选择循环遍历所有节点,采用类似sax的方法:

for $node in //*
return (
  "G"[$node/self::Genre],
  "."[$node/self::Book[Lost]],
  "_"[$node/self::Book[not(Lost)]]
)

Try on sqlfiddle.

请记住,节点序列的序列化是依赖于实现的,如果有任何不需要的空格,可能会有一个省略它的选项。或者,将其包装到string-join($sequence, '')

答案 1 :(得分:1)

首先,您无法摆脱SQL Server中格式错误的XML。 部分<Genre name=Horror>不完整,您必须写<Genre name="Horror">

见:

DECLARE @lib xml = 
'<Lib>
  <Genre name="Horror">
     <Book>
        <Title>1</Title>
     </Book>
     <Book>
        <Title>2</Title>
        <Lost>1</Lost>
     </Book>
     <Book>
        <Title>3</Title>
     </Book>
  </Genre>
  <Genre name="Romance">
     <Book>
        <Title>5</Title>
     </Book>
     <Book>
        <Title>6</Title>
        <Lost>0</Lost>
     </Book>
  </Genre>
</Lib>'

如果省略引号,将返回错误。

另一件事是,这可能是应用程序逻辑的工作。但是,如果您没有应用程序层,并且您需要这个...报告(?),那么您可以使用以下代码。请注意此处使用@lib变量。

DECLARE @output nvarchar(max) 

;WITH numberedBook AS (
    SELECT
        GenreName,
        RowNumber = ROW_NUMBER() OVER(PARTITION BY GenreName ORDER BY (SELECT NULL)),
        BookTitle,
        BookLost
    FROM 
        @lib.nodes('Lib/Genre/Book') T(c)
        CROSS APPLY(
            SELECT 
                GenreName = T.c.value('../@name', 'nvarchar(255)'),
                BookTitle = T.c.value('./Title[1]', 'nvarchar(255)'),
                BookLost = T.c.value('./Lost[1]', 'nvarchar(255)')
        ) lib
)
SELECT @output = 
    COALESCE(@output + '', '') + 
        CASE WHEN RowNumber = 1 THEN 'G' ELSE '' END +
        CASE WHEN BookLost IS NULL THEN '_' ELSE '.' END
FROM numberedBook

SELECT @output