Запрос SQL Server FOR XML не возвращает результаты в ожидаемой иерархии - PullRequest
0 голосов
/ 25 мая 2018

У меня есть система из четырех связанных таблиц, A, B, C и D, и мне нужно получить XML-представление этих данных.Таблицы выглядят так:

A ← B´
↑
B               Both table B and B´ have table A as a parent.
↑               Table C has table B as a parent.
C

За исключением таблицы A, у которой нет родителя, каждая из таблиц имеет 3 поля:

ID     int  -- integer ID of field
XStuff varchar(20) -- Replace 'X' with table name letter, such as AStuff, BStuff, etc.
Y_ID   int  -- Foreign key to parent table. Replace 'Y' with parent table name letter.

Данные таблицы:

tblA                 tblB
===============      =============
    ID | 1               ID | 1
AStuff | 'This'      BStuff | 'is'
                       A_ID | 1


tblC                 tblBPrime
============         ====================
    ID | 1                    ID | 1
CStuff | 'a'         BPrimeStuff | 'test'
  B_ID | 1                  A_ID | 1

Запрос:

SELECT tblA.AStuff
      ,tblB.BStuff
      ,tblC.CStuff
      ,tblBPrime.BPrimeStuff
  FROM tblA 
    JOIN tblB ON tblB.A_ID = tblA.ID
    JOIN tblC ON tblC.B_ID = tblB.ID
    JOIN tblBPrime ON tblBPrime.A_ID = tblA.ID
  FOR XML AUTO, TYPE, ELEMENTS

Что приводит к следующему выводу:

<tblA>
  <AStuff>This</AStuff>
  <tblB>
    <BStuff>is</BStuff>
    <tblC>
      <CStuff>a</CStuff>
      <tblBPrime>
        <BPrimeStuff>test</BPrimeStuff>
      </tblBPrime>
    </tblC>
  </tblB>
</tblA>

Это сбивает с толку, потому что tblBPrime подчиняется tblA, а не tblC, и я ожидаю, чтовывод будет примерно таким:

<tblA>
  <AStuff>This</AStuff>
  <tblB>
    <BStuff>is</BStuff>
    <tblC>
      <CStuff>a</CStuff>
    </tblC>
  </tblB>
  <tblBPrime>
    <BPrimeStuff>test</BPrimeStuff>
  </tblBPrime>
</tblA>

Я хотел бы знать, как следует изменить запрос, чтобы получить ожидаемые результаты.Кстати, я пометил этот SQL Server 2008, потому что самый старый блок SQL Server, который должен будет выполнять этот код, находится на этом уровне, но он также должен работать на SQL Server версии 2012 и 2014.

РЕДАКТИРОВАТЬ:

В соответствии с запросом, вот сценарии для создания таблиц и их отношений и заполнения их:

Создание таблиц и отношений:

CREATE TABLE [dbo].[tblA](
    [ID] [int] NOT NULL,
    [AStuff] [varchar](50) NOT NULL,
 CONSTRAINT [PK_tblA] PRIMARY KEY CLUSTERED(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[tblB](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [BStuff] [varchar](50) NOT NULL,
    [A_ID] [int] NOT NULL,
 CONSTRAINT [PK_tblB] PRIMARY KEY CLUSTERED(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[tblB]  WITH CHECK ADD  CONSTRAINT [FK_tblB_tblA] FOREIGN KEY([A_ID])
REFERENCES [dbo].[tblA] ([ID])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[tblB] CHECK CONSTRAINT [FK_tblB_tblA]
GO
CREATE TABLE [dbo].[tblBPrime](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [BPrimeStuff] [varchar](50) NOT NULL,
    [A_ID] [int] NOT NULL,
 CONSTRAINT [PK_tblBPrime] PRIMARY KEY CLUSTERED(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[tblBPrime]  WITH CHECK ADD  CONSTRAINT [FK_tblBPrime_tblA] FOREIGN KEY([A_ID])
REFERENCES [dbo].[tblA] ([ID])
GO
ALTER TABLE [dbo].[tblBPrime] CHECK CONSTRAINT [FK_tblBPrime_tblA]
GO
CREATE TABLE [dbo].[tblC](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [CStuff] [varchar](50) NOT NULL,
    [B_ID] [int] NOT NULL,
 CONSTRAINT [PK_tblC] PRIMARY KEY CLUSTERED(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[tblC]  WITH CHECK ADD  CONSTRAINT [FK_tblC_tblB] FOREIGN KEY([B_ID])
REFERENCES [dbo].[tblB] ([ID])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[tblC] CHECK CONSTRAINT [FK_tblC_tblB]
GO

Заполните таблицы:

SET XACT_ABORT ON;
DECLARE @A_ID int = 1,@B_ID int,@C_ID int
BEGIN TRAN
INSERT INTO [dbo].[tblA]([ID],[AStuff])
  VALUES (@A_ID,'This');
INSERT INTO [dbo].[tblB]([BStuff],[A_ID])
   VALUES('is',@A_ID);
SET @B_ID = SCOPE_IDENTITY();
INSERT INTO [dbo].[tblC]([CStuff],[B_ID])
   VALUES('a',@B_ID);
SET @C_ID = SCOPE_IDENTITY();
INSERT INTO [dbo].[tblBPrime]([BPrimeStuff],[A_ID])
   VALUES('test',@A_ID);
COMMIT TRAN

1 Ответ

0 голосов
/ 25 мая 2018

Похоже, что одним из решений является использование FOR XML PATH вместо FOR XML AUTO.Я нашел отличную информацию об использовании операторов FOR XML в ряде статей, написанных Якобом Себастьяном на SQLServerCentral.com: http://www.sqlservercentral.com/articles/XML/3022/.

Следующий запрос дает ожидаемые результаты, но зависит от наличияиерархия каждого элемента, определенного непосредственно в запросе.Я надеюсь, что есть лучший способ:

SELECT tblA.AStuff
      ,tblB.BStuff as [tblB/BStuff]
      ,tblC.CStuff as [tblB/tblC/Cstuff]
      ,tblBPrime.BPrimeStuff as [tblBPrime/BPrimeStuff]
  FROM tblA 
    JOIN tblB ON tblB.A_ID = tblA.ID
    JOIN tblC ON tblC.B_ID = tblB.ID
    JOIN tblBPrime ON tblBPrime.A_ID = tblA.ID
  FOR XML PATH ('tblA'), ELEMENTS;

... выводит ...

<tblA>
  <AStuff>This</AStuff>
  <tblB>
    <BStuff>is</BStuff>
    <tblC>
      <Cstuff>a</Cstuff>
    </tblC>
  </tblB>
  <tblBPrime>
    <BPrimeStuff>test</BPrimeStuff>
  </tblBPrime>
</tblA>
...