【问题标题】:How to Select distinct values in FOR XML PATH?如何在 FOR XML PATH 中选择不同的值?
【发布时间】:2018-04-12 21:08:31
【问题描述】:

给定以下表格,其中T_DATA.ID = PARENT_IDCHILD.ID

姓名:T_DATA

+----+------+--------+
| ID | CODE | VALUE  |
+----+------+--------+
|  1 | 3186 | value1 |
|  2 | 3186 | value2 |
|  3 | 3189 | value3 |
|  4 | 3189 | value4 |
|  5 | 3190 | value5 |
+----+------+--------+

姓名:T_DATA_LINK

+-----------+----------+
| PARENT_ID | CHILD_ID |
+-----------+----------+
|         1 |        3 |
|         1 |        4 |
+-----------+----------+

我想返回一个这样的 xml 结构:

<ITEM_LIST>
    <ITEM>
        <CODE>3186</CODE>
        <ROWS>
            <ROW>
                <ID>1</ID>
                <ROW_INDEX>0</ROW_INDEX>
                <VALUE>value1</VALUE>
            </ROW>
            <ROW>
                <ID>2</ID>
                <ROW_INDEX>1</ROW_INDEX>
                <VALUE>value2</VALUE>
            </ROW>
        </ROWS>
    </ITEM>
    <ITEM>
        <CODE>3189</CODE>
        <ROWS>
            <ROW>
                <ID>3</ID>
                <ROW_INDEX>0</ROW_INDEX>
                <VALUE>value3</VALUE>
            </ROW>
            <ROW>
                <ID>4</ID>
                <ROW_INDEX>1</ROW_INDEX>
                <VALUE>value4</VALUE>
            </ROW>
        </ROWS>
    </ITEM>
    <ITEM>
        <CODE>3190</CODE>
        <VALUE>value5</VALUE>
    </ITEM>
</ITEM_LIST>

ROW_INDEX 每增加一个ROW,就加 1。

我需要T_DATA_LINK 表来了解ITEM 是否有父级。

如果它有一个父项,则意味着有多个记录具有该CODE 值,它们需要显示为ROWS,否则必须显示为单个ITEM

更新

我实际上需要检查 T_DATA_LINK 表,因为可能存在 ITEM 有父项且只有一条记录的情况,但它仍需要显示为 ROW。

@Shnugo 我尝试了您的解决方案,但即使现在我在 ROWS 中获得了正确的值,对于具有多个记录的每个 ITEM,我都会得到重复项。

这可能是因为我必须将我需要返回的其他字段添加到 GROUP BY 中,而我没有添加到示例中以使其更简单。

例如,对于没有任何ROWS的项目,ID需要显示在ITEM级别。

更新 2

@Shnugo 你是对的。第 3 项和第 4 项是第 1 项的子项,但您在 xml 中看不到关系。

所有的物品都是独一无二的,永远都是。

在 T_DATA_LINK 中引用的项目仍然是唯一的,但在我的应用程序中相互链接,它们显示在表格中。 基本上 PARENT 是表的第一列,而子项是其他列。

这是我想要得到的更新后的输出。

对于具有行的项目,ID 应始终为 -1。

PARENT_CODE 应该是父项的 CODE(如果项目是父项则等于 CODE)

<ITEM_LIST>
  <ITEM>
    <ID>-1</ID>
    <CODE>3186</CODE>
    <PARENT_CODE>3186</PARENT_CODE>
    <ROWS>
        <ROW>
            <ID>1</ID>
            <ROW_INDEX>0</ROW_INDEX>
            <VALUE>value1</VALUE>
        </ROW>
        <ROW>
            <ID>2</ID>
            <ROW_INDEX>1</ROW_INDEX>
            <VALUE>value2</VALUE>
        </ROW>
    </ROWS>
  </ITEM>
  <ITEM>
    <ID>-1</ID>
    <CODE>3189</CODE>
    <PARENT_CODE>3186</PARENT_CODE>
    <ROWS>
        <ROW>
            <ID>3</ID>
            <ROW_INDEX>0</ROW_INDEX>
            <VALUE>value3</VALUE>
        </ROW>
        <ROW>
            <ID>4</ID>
            <ROW_INDEX>1</ROW_INDEX>
            <VALUE>value4</VALUE>
        </ROW>
    </ROWS>
  </ITEM>
  <ITEM>
    <ID>5</ID>
    <CODE>3190</CODE>
    <VALUE>value5</VALUE>
  </ITEM>
</ITEM_LIST>

【问题讨论】:

  • 我不认为 sql server 会以你想要的方式保存它。您可以使用像 c# 这样的编程语言来轻松读取和保存 xml。
  • @jdweng 为什么你认为 SQL-Server 不能解决这个问题?
  • 这是一个非常好的问题!独立示例,明确的问题,预期的输出... +1 形成我的一面

标签: sql xml sql-server-2008 tsql xpath


【解决方案1】:

这是一个新的答案...请尝试将所有需要的信息放入最初的问题中...

DECLARE @t_data TABLE(ID INT,CODE INT,VALUE VARCHAR(100));
INSERT INTO @t_data VALUES
 (1,3186,'value1')
,(2,3186,'value2')
,(3,3189,'value3')
,(4,3189,'value4')
,(5,3190,'value5');

DECLARE @t_data_link TABLE(PARENT_ID INT, CHILD_ID INT)
INSERT INTO @t_data_link VALUES
 (1,3)
,(1,4);

--CTE 链接两个表并允许将它们作为一个派生表处理

WITH Combined AS
(
    SELECT d.*
          ,d2.CODE AS PARENT_CODE
          ,COUNT(*) OVER(PARTITION BY d.CODE) AS CountRows 
    FROM @t_data AS d
    LEFT JOIN @t_data_link AS dl ON d.ID=dl.CHILD_ID
    LEFT JOIN @t_data AS d2 ON dl.PARENT_ID=d2.ID
)
SELECT CASE WHEN c.CountRows>1 THEN -1 END AS ID 
      ,CASE WHEN c.CountRows>1 THEN c.CODE END AS CODE
      ,CASE WHEN c.CountRows>1 THEN ISNULL(c.PARENT_CODE,c.CODE) END AS PARENT_CODE
       --This part for elements with just one row per code
      ,(
        SELECT d2.ID
              ,d2.CODE
              ,d2.VALUE
        FROM @t_data AS d2
        WHERE c.CODE=d2.CODE 
          AND c.CountRows=1
        FOR XML PATH(''),TYPE
       )
       --This part for elements with more rows per code
       ,(
        SELECT d2.ID
              ,ROW_NUMBER() OVER(ORDER BY (SELECT NULL))-1 AS ROW_INDEX
              ,d2.VALUE
        FROM @t_data AS d2
        WHERE c.CODE=d2.CODE 
          AND c.CountRows>1
        FOR XML PATH('ROW'),ROOT('ROWS'),TYPE
       )
FROM Combined AS c
GROUP BY c.CODE,c.CountRows,c.PARENT_CODE
FOR XML PATH('ITEM'),ROOT('ITEM_LIST');

结果

<ITEM_LIST>
  <ITEM>
    <ID>-1</ID>
    <CODE>3186</CODE>
    <PARENT_CODE>3186</PARENT_CODE>
    <ROWS>
      <ROW>
        <ID>1</ID>
        <ROW_INDEX>0</ROW_INDEX>
        <VALUE>value1</VALUE>
      </ROW>
      <ROW>
        <ID>2</ID>
        <ROW_INDEX>1</ROW_INDEX>
        <VALUE>value2</VALUE>
      </ROW>
    </ROWS>
  </ITEM>
  <ITEM>
    <ID>-1</ID>
    <CODE>3189</CODE>
    <PARENT_CODE>3186</PARENT_CODE>
    <ROWS>
      <ROW>
        <ID>3</ID>
        <ROW_INDEX>0</ROW_INDEX>
        <VALUE>value3</VALUE>
      </ROW>
      <ROW>
        <ID>4</ID>
        <ROW_INDEX>1</ROW_INDEX>
        <VALUE>value4</VALUE>
      </ROW>
    </ROWS>
  </ITEM>
  <ITEM>
    <ID>5</ID>
    <CODE>3190</CODE>
    <VALUE>value5</VALUE>
  </ITEM>
</ITEM_LIST>

XML 将省略任何 NULL 值。如果没有找到任何内容,则子选择中的 WHERE 子句将返回 NULL...

【讨论】:

  • @FabioL。我不太了解data_link-table 中的数据,3 和 4 是 1 的孩子吗?您的数据没有反映这一点 - 或者我不明白......请检查您的问题,为您的极端情况添加数据并调整预期的输出。这对你来说可能很清楚,但对我来说不是......
  • 我根据我的实际表格结构调整了您的答案,并且效果很好。非常感谢!
猜你喜欢
  • 2011-06-15
  • 1970-01-01
  • 1970-01-01
  • 2012-03-20
  • 2011-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-21
相关资源
最近更新 更多