SQL Server XML Query Hierarchy not as awaited

43 views Asked by At

There are five or more database tables, that are related to each other like in the following database schema:

enter image description here

Here is the code for creating them:

-- Table 1
CREATE TABLE [dbo].[Table1](
    [Id] [INT] NOT NULL,
    [Title] [NCHAR](10) NOT NULL,
    [Annotation] [NCHAR](10) NOT NULL,
 CONSTRAINT [PK_Table1] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
-- Table 2 referencing Table 1
CREATE TABLE [dbo].[Table2](
    [Id] [INT] NOT NULL,
    [Table1_Id] [INT] NOT NULL,
    [Title] [NCHAR](10) NOT NULL,
 CONSTRAINT [PK_Table2] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Table2]  WITH CHECK ADD  CONSTRAINT [FK_Table2_Table1] FOREIGN KEY([Table1_Id])
REFERENCES [dbo].[Table1] ([Id])
GO
ALTER TABLE [dbo].[Table2] CHECK CONSTRAINT [FK_Table2_Table1]
GO
-- Table 2_1 referencing Table 2
CREATE TABLE [dbo].[Table2_1](
    [Id] [INT] NOT NULL,
    [Table2_Id] [INT] NOT NULL,
    [Title] [NCHAR](10) NOT NULL,
 CONSTRAINT [PK_Table2_1] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Table2_1]  WITH CHECK ADD  CONSTRAINT [FK_Table2_1_Table2] FOREIGN KEY([Table2_Id])
REFERENCES [dbo].[Table2] ([Id])
GO

ALTER TABLE [dbo].[Table2_1] CHECK CONSTRAINT [FK_Table2_1_Table2]
GO
-- Table 3 referencing Table 1
CREATE TABLE [dbo].[Table3](
    [Id] [INT] NOT NULL,
    [Table1_Id] [INT] NOT NULL,
    [Title] [NCHAR](10) NOT NULL,
 CONSTRAINT [PK_Table3] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Table3]  WITH CHECK ADD  CONSTRAINT [FK_Table3_Table1] FOREIGN KEY([Table1_Id])
REFERENCES [dbo].[Table1] ([Id])
GO
ALTER TABLE [dbo].[Table3] CHECK CONSTRAINT [FK_Table3_Table1]
GO
-- Table 3_1 referencing Table 3
CREATE TABLE [dbo].[Table3_1](
    [Id] [INT] NOT NULL,
    [Table3_Id] [INT] NOT NULL,
    [Title] [NCHAR](10) NOT NULL,
 CONSTRAINT [PK_Table3_1] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Table3_1]  WITH CHECK ADD  CONSTRAINT [FK_Table3_1_Table3] FOREIGN KEY([Table3_Id])
REFERENCES [dbo].[Table3] ([Id])
GO
ALTER TABLE [dbo].[Table3_1] CHECK CONSTRAINT [FK_Table3_1_Table3]
GO

Now I add the following record samples to the table:

INSERT INTO table1 VALUES (1, 'FirstTitle', 'FirstAnno')
INSERT INTO table2 VALUES (1, 1, 'Tab2Title')
INSERT INTO table3 VALUES (1, 1, 'Tab3Title')
INSERT INTO table2_1 VALUES (1, 1, 'Tab21Sub')
INSERT INTO table3_1 VALUES (1, 1, 'Tab31Sub')

Querying this tables with a JOIN FOR XML like

 SELECT * FROM Table1 AS T1
   JOIN Table2 AS T2 ON T1.Id = T2.Table1_Id
   JOIN Table3 AS T3 ON T1.Id = T3.Table1_Id
   JOIN Table2_1 AS T21 ON T2.Id = T21.Table2_Id
   JOIN Table3_1 AS T31 ON T3.Id = T31.Table2_Id
FOR XML AUTO

will end in this result

<T1 Id="1" Title="FirstTitle" Annotation="FirstAnno ">
  <T2 Id="1" Table1_Id="1" Title="Tab2Title ">
    <T3 Id="1" Table1_Id="1" Title="Tab3Title ">
      <T21 Id="1" Table2_Id="1" Title="Tab21Sub  ">
      <T31 Id="1" Table3_Id="1" Title="Tab31Sub  " />
      </T21>
    </T3>
  </T2>
</T1>

while I'm expecting this

 <T1 Id="1" Title="FirstTitle" Annotation="FirstAnno ">
  <T2 Id="1" Table1_Id="1" Title="Tab2Title ">
    <T21 Id="1" Table2_Id="1" Title="Tab21Sub" />
  </T2>
  <T3 Id="1" Table1_Id="1" Title="Tab3Title ">
    <T31 Id="1" Table3_Id="1" Title="Tab31Sub" />
  </T3>
</T1>

So how can I modify the query, perhaps making subqueries to get the expected result, sorting Table 3 at the same level and not beneath table2, and also sorting the childs of table2_1 and table3_1 beneath there parents?

1

There are 1 answers

0
AlwaysLearning On BEST ANSWER

Given your hierarchical example I've fleshed out the example data to include some more subitems...

INSERT INTO table1 VALUES (1, 'FirstTitle', 'FirstAnno');

INSERT INTO table2 VALUES (1, 1, 'Tab2Title1');
INSERT INTO table2_1 VALUES (1, 1, 'Tab21Sub1.1');
INSERT INTO table2_1 VALUES (2, 1, 'Tab21Sub1.2');
INSERT INTO table2_1 VALUES (3, 1, 'Tab21Sub1.3');

INSERT INTO table2 VALUES (2, 1, 'Tab2Title2');
INSERT INTO table2_1 VALUES (4, 2, 'Tab21Sub2.1');
INSERT INTO table2_1 VALUES (5, 2, 'Tab21Sub2.2');

INSERT INTO table3 VALUES (1, 1, 'Tab3Title1');
INSERT INTO table3_1 VALUES (1, 1, 'Tab31Sub');

INSERT INTO table3 VALUES (2, 1, 'Tab3Title2');

If you use FOR XML AUTO and correlated subqueries that return FOR XML AUTO, TYPE such as the following:

SELECT T1.*,
  (
    SELECT T2.*,
      (
        SELECT T21.*
        FROM Table2_1 AS T21
        WHERE T2.Id = T21.Table2_Id
        FOR XML AUTO, TYPE
      )
    FROM Table2 AS T2
    WHERE T1.Id = T2.Table1_Id
    FOR XML AUTO, TYPE
  ),
  (
    SELECT T3.*,
      (
        SELECT T31.*
        FROM Table3_1 AS T31
        WHERE T3.Id = T31.Table3_Id
        FOR XML AUTO, TYPE
      )
    FROM Table3 AS T3
    WHERE T1.Id = T3.Table1_Id
    FOR XML AUTO, TYPE
  )
FROM Table1 AS T1
FOR XML AUTO;

You can return nested XML data such as the following:

<T1 Id="1" Title="FirstTitle" Annotation="FirstAnno ">
    <T2 Id="1" Table1_Id="1" Title="Tab2Title1">
        <T21 Id="1" Table2_Id="1" Title="Tab21Sub1.1"/>
        <T21 Id="2" Table2_Id="1" Title="Tab21Sub1.2"/>
        <T21 Id="3" Table2_Id="1" Title="Tab21Sub1.3"/>
    </T2>
    <T2 Id="2" Table1_Id="1" Title="Tab2Title2">
        <T21 Id="4" Table2_Id="2" Title="Tab21Sub2.1"/>
        <T21 Id="5" Table2_Id="2" Title="Tab21Sub2.2"/>
    </T2>
    <T3 Id="1" Table1_Id="1" Title="Tab3Title1">
        <T31 Id="1" Table3_Id="1" Title="Tab31Sub   "/>
    </T3>
    <T3 Id="2" Table1_Id="1" Title="Tab3Title2"/>
</T1>