OPENXML Rowpattern in SQL Server: Search Every Level and Read the Parent

An OPENXML rowpattern decides which XML elements become rows, and two small tricks make it far more useful. A double slash searches every level. A ../ in a column pattern reads the parent. Both are tested here on a small team tree.

A team tree with Maya at level 1, Arjun and Lena at level 2 and Noor at level 3, beside four rowpatterns and their rows: the one-level path returns Maya, the two-level path returns Arjun and Lena, the three-level path returns Noor, and a double slash path returns all four

The Sample Document

The document is a small team with four members. One sits at the top and two sit under them. One more sits under one of those. Every element is named Member, and the name sits in an attribute. Every result in this post comes from SQL Server 2025.

Run the blocks in order, in one batch, because the handle and @Doc live in the batch. The first block prepares the document.

DECLARE @Doc xml = N'<Root>
  <Member Name="Maya">
    <Member Name="Arjun">
      <Member Name="Noor"/>
    </Member>
    <Member Name="Lena"/>
  </Member>
</Root>';
DECLARE @Handle int;
EXEC sys.sp_xml_preparedocument @Handle OUTPUT, @Doc;

Absolute Rowpatterns

An absolute rowpattern names every level from the root. Each query below goes one level deeper, and each returns only the members at that exact level.

SELECT Name FROM OPENXML(@Handle, '/Root/Member', 1) WITH (Name varchar(20) '@Name');

SELECT Name FROM OPENXML(@Handle, '/Root/Member/Member', 1) WITH (Name varchar(20) '@Name');

SELECT Name FROM OPENXML(@Handle, '/Root/Member/Member/Member', 1) WITH (Name varchar(20) '@Name');

The first query returns Maya, the second returns Arjun and Lena, and the third returns Noor. Maya isn’t in the second result, because Maya sits one level up.

Search Every Level With Two Slashes

A double slash means any depth below that point. The pattern ‘/Root//Member’ returns every Member under Root, whatever its level. A pattern that starts with // searches the whole document.

SELECT Name FROM OPENXML(@Handle, '/Root//Member', 1) WITH (Name varchar(20) '@Name');

SELECT Name FROM OPENXML(@Handle, '//Member', 1) WITH (Name varchar(20) '@Name');

Both queries return four rows, in document order. Use the double slash when the element name matters more than its level. An org chart is a good example, because every person is a Member.

Paths are case sensitive. A lowercase member in the pattern below matches nothing, and SQL Server doesn’t raise an error. The query returns zero rows.

SELECT Name FROM OPENXML(@Handle, '/Root/member', 1) WITH (Name varchar(20) '@Name');

Read the Parent With ../

A column pattern is relative to the element the rowpattern found. ../ steps up one level, and ../../ steps up two. This query returns each member next to their parent and grandparent.

SELECT Name, Parent, GrandParent
FROM OPENXML(@Handle, '/Root//Member', 1)
WITH (Name varchar(20) '@Name', Parent varchar(20) '../@Name', GrandParent varchar(20) '../../@Name');

Maya has no parent member. The parent element is Root, and Root has no Name attribute, so both columns are NULL. Noor shows Arjun as the parent and Maya as the grandparent.

A grid of the four members with their parent and grandparent: Maya has NULL for both, Arjun has Maya and NULL, Noor has Arjun and Maya, and Lena has Maya and NULL, beside the column patterns @Name, ../@Name and ../../@Name

The Same Result With nodes()

The same paths work with the nodes() and value() methods. The next query uses the @Doc variable from the first block and needs no handle.

SELECT
  m.n.value('@Name', 'varchar(20)') AS Name,
  m.n.value('(../@Name)[1]', 'varchar(20)') AS Parent,
  m.n.value('(../../@Name)[1]', 'varchar(20)') AS GrandParent
FROM @Doc.nodes('/Root//Member') AS m(n);

It returns the same four rows. Prefer this form in new code, and keep OPENXML for scripts that already use it.

What the Flags Value Does

The third argument of OPENXML is the flags value. The query below leaves out the column patterns, so the flags decide where the Name column comes from.

SELECT 'flags 0' AS Setting, Name FROM OPENXML(@Handle, '/Root/Member', 0) WITH (Name varchar(20))
UNION ALL
SELECT 'flags 1', Name FROM OPENXML(@Handle, '/Root/Member', 1) WITH (Name varchar(20))
UNION ALL
SELECT 'flags 2', Name FROM OPENXML(@Handle, '/Root/Member', 2) WITH (Name varchar(20));

Flags 0 and 1 both read attributes, so both return Maya. Flags 2 reads child elements. Maya has no child element named Name, so that row is NULL.

Release the Handle

Release the document when you finish.

EXEC sys.sp_xml_removedocument @Handle;

What to Remember

Name every level for an absolute rowpattern, or use // to search every level. Use ../ in a column pattern to reach the parent. A missing parent gives NULL, not an error.

To go further, read XML nodes: Turn Repeated Elements Into Rows, XML value: Select One Typed Scalar and XML exist: A Present Boolean Expression Still Exists.

Quick reference card: OPENXML Rowpattern

A rowpattern is not a filter on names, it is a path through the tree.

Published by Pinal Dave on SQLAuthority. More of my work at pinaldave.com.


Discover more from SQL Authority with Pinal Dave

Subscribe to get the latest posts sent to your email.

SQL Scripts, SQL Server, SQL Stored Procedure, SQL XML
Previous Post
sp_xml_preparedocument: Prepare XML in Memory and Always Release It
Next Post
AT TIME ZONE Cost: Converting Many Rows Without Burning CPU

Related Posts

1 Comment. Leave new

  • sharath molagavalli
    March 19, 2013 5:26 pm

    Can somebody explain the difference between flag 0 argument & flag 1 argument
    , of OPENXML function

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.