FOR XML EXPLICIT - 重复属性值

时间:2015-07-24 17:31:08

标签: sql-server xml attributes explicit

我需要生成一个EXCEL Like XML文件,从SQL Server中的表中插入数据。经过一些研究后,我使用FOR XML EXPLICIT模式获得了以下SQL Server脚本:

DECLARE @T AS TABLE (col1 VARCHAR(20), col2 VARCHAR(20));
INSERT INTO @T VALUES('Row1 Col1', 'Row1 Col2');
INSERT INTO @T VALUES('Row2 Col1', 'Row2 Col2');

SELECT	1			as 'Tag'
		,NULL		as 'Parent'
		,NULL		as 'Row!1'
		,NULL		as 'Cell!2'
		,NULL		as 'Cell!2!Index'
		,NULL		as 'Cell!2!StyleID'
		,NULL		as 'Data!3'
		,NULL		as 'Data!3!Type'
		,NULL		as 'Cell!2'
		,NULL		as 'Cell!2!Index'
		,NULL		as 'Cell!2!StyleID'
		,NULL		as 'Data!3'
		,NULL		as 'Data!3!Type'
		,ROW_NUMBER() OVER (ORDER BY col1) as 'Row!1!A!HIDE'
		,1			as 'Row!1!B!HIDE'
FROM @T
UNION ALL
SELECT	2
		,1
		,NULL
		,NULL
		,'1'
		,'s1'
		,NULL
		,NULL
		,NULL
		,'2'
		,'s2'
		,NULL
		,NULL
		,ROW_NUMBER() OVER (ORDER BY col1)
		,2
FROM @T
UNION ALL
SELECT	3
		,2
		,NULL
		,NULL
		,NULL
		,NULL
		,col1
		,'String'
		,NULL
		,NULL
		,NULL
		,col2
		,'String'
		,ROW_NUMBER() OVER (ORDER BY col1)
		,3
FROM @T
ORDER BY 14, 15
FOR XML EXPLICIT

GO

我得到的结果是:

<Row>
  <Cell Index="1" StyleID="s1" Index="2" StyleID="s2">
    <Data Type="String" Type="String">Row1 Col1Row1 Col2</Data>
  </Cell>
</Row>
<Row>
  <Cell Index="1" StyleID="s1" Index="2" StyleID="s2">
    <Data Type="String" Type="String">Row2 Col1Row2 Col2</Data>
  </Cell>
</Row>

我期望的结果是:

<Row>
  <Cell Index="1" StyleID="s1">
    <Data Type="String">Row1 Col1</Data>
  </Cell>
  <Cell Index="2" StyleID="s2">
    <Data Type="String">Row1 Col2</Data>
  </Cell>
</Row>
<Row>
  <Cell Index="1" StyleID="s1">
    <Data Type="String">Row2 Col1</Data>
  </Cell>
  <Cell Index="2" StyleID="s2">
    <Data Type="String">Row2 Col2</Data>
  </Cell>
</Row>

任何帮助都将不胜感激。

2 个答案:

答案 0 :(得分:0)

这可以为您提供所需的结果,但不确定这是否适用于您提供的测试数据。

SELECT
    1 AS 'Tag',
    NULL AS 'Parent',
    NULL AS 'Row!1',
    NULL AS 'Cell!2',
    NULL AS 'Cell!2!Index',
    NULL AS 'Cell!2!StyleID',
    NULL AS 'Data!3',
    NULL AS 'Data!3!Type',
    ROW_NUMBER() OVER (ORDER BY col1) AS 'Row!1!A!HIDE',
    1 AS 'Row!1!B!HIDE'
FROM
    @T
UNION ALL
SELECT
    2,
    1,
    NULL,
    NULL,
    '1',
    's1',
    NULL,
    NULL,
    ROW_NUMBER() OVER (ORDER BY col1),
    2
FROM
    @T
UNION ALL
SELECT
    3,
    2,
    NULL,
    NULL,
    NULL,
    NULL,
    col1,
    'String',
    ROW_NUMBER() OVER (ORDER BY col1),
    2
FROM
    @T
UNION ALL
SELECT
    2,
    1,
    NULL,
    NULL,
    '2',
    's2',
    NULL,
    NULL,
    ROW_NUMBER() OVER (ORDER BY col1),
    3
FROM
    @T
UNION ALL    
SELECT
    3,
    2,
    NULL,
    NULL,
    NULL,
    NULL,
    col2,
    'String',
    ROW_NUMBER() OVER (ORDER BY col1),
    3
FROM
    @T
ORDER BY
    9,
    10
FOR   
    XML EXPLICIT 

GO

诀窍是在调用FOR XML EXPLICIT之前以正确的顺序获取数据它应该看起来与此类似

Tag Parent  Row!1  Cell!2  Cell!2!Index  Cell!2!StyleID  Data!3     Data!3!Type  Row!1!A!HIDE  Row!1!B!HIDE
1   NULL    NULL   NULL    NULL          NULL            NULL       NULL         1             1
2   1       NULL   NULL    1             s1              NULL       NULL         1             2
3   2       NULL   NULL    NULL          NULL            Row1 Col1  String       1             2
2   1       NULL   NULL    2             s2              NULL       NULL         1             3
3   2       NULL   NULL    NULL          NULL            Row1 Col2  String       1             3
1   NULL    NULL   NULL    NULL          NULL            NULL       NULL         2             1
2   1       NULL   NULL    1             s1              NULL       NULL         2             2
3   2       NULL   NULL    NULL          NULL            Row2 Col1  String       2             2
2   1       NULL   NULL    2             s2              NULL       NULL         2             3
3   2       NULL   NULL    NULL          NULL            Row2 Col2  String       2             3

答案 1 :(得分:0)

我设法使用FOR XML PATH模式找到了更好的方法:

DECLARE @T AS TABLE (
  col1 VARCHAR(20),
  col2 VARCHAR(20),
  col3 VARCHAR(20)
);

INSERT INTO @T VALUES
  ('Row1 Col1','Row1 Col2','Row1 Col3'),
  ('Row2 Col1',NULL,'Row2 Col3'),
  ('Row3 Col1','Row3 Col2',NULL);

SELECT
  '1'      as 'Cell/@Index',
  's1'     as 'Cell/@StyleID',
  'String' as 'Cell/Data/@Type',
  col1     as 'Cell/Data',
  '',
  '2'      as 'Cell/@Index',
  's2'     as 'Cell/@StyleID',
  'String' as 'Cell/Data/@Type',
  col2     as 'Cell/Data',
  '',
  '3'      as 'Cell/@Index',
  's3'     as 'Cell/@StyleID',
  'String' as 'Cell/Data/@Type',
  col3     as 'Cell/Data'
FROM @T
FOR XML PATH ('Row')

GO

我得到了理想的结果:

<Row>
  <Cell Index="1" StyleID="s1">
    <Data Type="String">Row1 Col1</Data>
  </Cell>
  <Cell Index="2" StyleID="s2">
    <Data Type="String">Row1 Col2</Data>
  </Cell>
  <Cell Index="3" StyleID="s3">
    <Data Type="String">Row1 Col3</Data>
  </Cell>
</Row>
<Row>
  <Cell Index="1" StyleID="s1">
    <Data Type="String">Row2 Col1</Data>
  </Cell>
  <Cell Index="2" StyleID="s2">
    <Data Type="String" />
  </Cell>
  <Cell Index="3" StyleID="s3">
    <Data Type="String">Row2 Col3</Data>
  </Cell>
</Row>
<Row>
  <Cell Index="1" StyleID="s1">
    <Data Type="String">Row3 Col1</Data>
  </Cell>
  <Cell Index="2" StyleID="s2">
    <Data Type="String">Row3 Col2</Data>
  </Cell>
  <Cell Index="3" StyleID="s3">
    <Data Type="String" />
  </Cell>
</Row>

在sql语句中添加空字符串('')可以解决问题。

但它变得越来越好:我不想为NULL值打印标签。我应该怎么做呢?这就是答案:

DECLARE @T AS TABLE (
  col1 VARCHAR(20),
  col2 VARCHAR(20),
  col3 VARCHAR(20)
);

INSERT INTO @T VALUES
  ('Row1 Col1','Row1 Col2','Row1 Col3'),
  ('Row2 Col1',NULL,'Row2 Col3'),
  ('Row3 Col1','Row3 Col2',NULL);

SELECT
  'Cell/@Index'     = case when col1 is not null then '1'      else NULL end,
  'Cell/@StyleID'   = case when col1 is not null then 's1'     else NULL end,
  'Cell/Data/@Type' = case when col1 is not null then 'String' else NULL end,
  col1              as 'Cell/Data',
  '',
  'Cell/@Index'     = case when col2 is not null then '2'      else NULL end,
  'Cell/@StyleID'   = case when col2 is not null then 's2'     else NULL end,
  'Cell/Data/@Type' = case when col2 is not null then 'String' else NULL end,
  col2              as 'Cell/Data',
  '',
  'Cell/@Index'     = case when col3 is not null then '3'      else NULL end,
  'Cell/@StyleID'   = case when col3 is not null then 's3'     else NULL end,
  'Cell/Data/@Type' = case when col3 is not null then 'String' else NULL end,
  col3              as 'Cell/Data'
FROM @T
FOR XML PATH ('Row')

GO

我得到了更好的结果:

<Row>
  <Cell Index="1" StyleID="s1">
    <Data Type="String">Row1 Col1</Data>
  </Cell>
  <Cell Index="2" StyleID="s2">
    <Data Type="String">Row1 Col2</Data>
  </Cell>
  <Cell Index="3" StyleID="s3">
    <Data Type="String">Row1 Col3</Data>
  </Cell>
</Row>
<Row>
  <Cell Index="1" StyleID="s1">
    <Data Type="String">Row2 Col1</Data>
  </Cell>
  <Cell Index="3" StyleID="s3">
    <Data Type="String">Row2 Col3</Data>
  </Cell>
</Row>
<Row>
  <Cell Index="1" StyleID="s1">
    <Data Type="String">Row3 Col1</Data>
  </Cell>
  <Cell Index="2" StyleID="s2">
    <Data Type="String">Row3 Col2</Data>
  </Cell>
</Row>

我在SQLS 2008上使用CASE做过。对于SQLS 2012,您可以使用IIF。

最后,我想在属性上添加名称空间指示符。我这样做了:

DECLARE @T AS TABLE (
  col1 VARCHAR(20),
  col2 VARCHAR(20),
  col3 VARCHAR(20)
);

INSERT INTO @T VALUES
  ('Row1 Col1','Row1 Col2','Row1 Col3'),
  ('Row2 Col1',NULL,'Row2 Col3'),
  ('Row3 Col1','Row3 Col2',NULL);

WITH XMLNAMESPACES ('urn:schemas-microsoft-com:office:spreadsheet' as ss)
SELECT
  'Cell/@ss:Index'     = case when col1 is not null then '1'      else NULL end,
  'Cell/@ss:StyleID'   = case when col1 is not null then 's1'     else NULL end,
  'Cell/Data/@ss:Type' = case when col1 is not null then 'String' else NULL end,
  col1              as 'Cell/Data',
  '',
  'Cell/@ss:Index'     = case when col2 is not null then '2'      else NULL end,
  'Cell/@ss:StyleID'   = case when col2 is not null then 's2'     else NULL end,
  'Cell/Data/@ss:Type' = case when col2 is not null then 'String' else NULL end,
  col2              as 'Cell/Data',
  '',
  'Cell/@ss:Index'     = case when col3 is not null then '3'      else NULL end,
  'Cell/@ss:StyleID'   = case when col3 is not null then 's3'     else NULL end,
  'Cell/Data/@ss:Type' = case when col3 is not null then 'String' else NULL end,
  col3              as 'Cell/Data'
FROM @T
FOR XML PATH ('Row'), ROOT ('Worksheet')

GO

我得到的结果与我正在寻找的结果更接近:

<Worksheet xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
  <Row>
    <Cell ss:Index="1" ss:StyleID="s1">
      <Data ss:Type="String">Row1 Col1</Data>
    </Cell>
    <Cell ss:Index="2" ss:StyleID="s2">
      <Data ss:Type="String">Row1 Col2</Data>
    </Cell>
    <Cell ss:Index="3" ss:StyleID="s3">
      <Data ss:Type="String">Row1 Col3</Data>
    </Cell>
  </Row>
  <Row>
    <Cell ss:Index="1" ss:StyleID="s1">
      <Data ss:Type="String">Row2 Col1</Data>
    </Cell>
    <Cell ss:Index="3" ss:StyleID="s3">
      <Data ss:Type="String">Row2 Col3</Data>
    </Cell>
  </Row>
  <Row>
    <Cell ss:Index="1" ss:StyleID="s1">
      <Data ss:Type="String">Row3 Col1</Data>
    </Cell>
    <Cell ss:Index="2" ss:StyleID="s2">
      <Data ss:Type="String">Row3 Col2</Data>
    </Cell>
  </Row>
</Worksheet>

我必须在XML文档的中间插入这个片段,所以如果我可以摆脱第一行和最后一行以连接固定的页眉和页脚,那将是很好的。

希望这有助于其他人。