Search code examples
sqlsql-serverxmlxquerysqlxml

Using XQuery with order by all elements and attribute


I still have a problem about sorting xml by XQuery.

Please check below code.

As is :

<Main name = "test">
    <Sample id="1">
      <cal>
        <tree abc="123"/>
        <tree abc="789/>
        <tree-order abc="456/>
      </cal>
    </Sample>

     <Sample id="2">
      <cal>
        <tree abc="123"/>
        <tree abc="789/>
        <tree-order abc="456/>
      </cal>
    </Sample>

    <Sample id="3">
      <cal>
        <tree abc="123"/>
        <tree abc="789/>
        <tree-order abc="456/>
      </cal>
    </Sample>

</Main>

I want to order by attribute "abc"

To be 1:

<Main name = "test">
    <Sample id="1">
      <cal>
        <tree abc="123"/>
        <tree-order abc="456/>
        <tree abc="789/>
      </cal>
    </Sample>

     <Sample id="2">
      <cal>
        <tree abc="123"/>
        <tree-order abc="456/>
        <tree abc="789/>
      </cal>
    </Sample>

    <Sample id="3">
      <cal>
        <tree abc="123"/>
        <tree-order abc="456/>
        <tree abc="789/>
      </cal>
    </Sample>
</Main>

after that is it possible to remove attribute??

Final.

<Main name = "test">
    <Sample id="1">
      <cal>
        <tree />
        <tree-order />
        <tree />
      </cal>
    </Sample>

     <Sample id="2">
      <cal>
        <tree />
        <tree-order />
        <tree />
      </cal>
    </Sample>

    <Sample id="3">
      <cal>
        <tree />
        <tree-order />
        <tree />
      </cal>
    </Sample>
</Main>

like this.

so attribute abc is only for sorting.

I tried to like this

select @data.query('for $j in * order by number($j/@abc) return $j ')

then it's will show xml format without sorting.

Is there any way to solve this problem?


Solution

  • Process the XML recursively so that you can perform a sort on the <cal> element children and preserve its ancestor structure:

    declare function local:sort(
      $xml as element(cal)
    ) as element()
    {
      element cal {
        for $e in $xml/*
        order by $e/@abc
        return local:dispatch($e)
      }
    };
    
    declare function local:remove-atts(
      $xml as element()
    ) as element()
    {
      element { node-name($xml) } {
        $xml/@* except $xml/@abc,
        $xml/node()
      }
    };
    
    declare function local:dispatch(
      $xml as element()
    ) as element()
    {
      typeswitch ($xml)
        case element(cal) return local:sort($xml)
        case element(tree) return local:remove-atts($xml)
        case element(tree-order) return local:remove-atts($xml)
        default return local:process($xml)
    };
    
    declare function local:process(
      $xml as element()
    ) as element()
    {
      element { node-name($xml) } {
        $xml/@*, 
        for $n in $xml/node()
        return local:dispatch($n)
      }
    };
    
    local:process($xml)