свернуть строки с помощью функции sql stuff - PullRequest
0 голосов
/ 22 мая 2019

У меня есть три таблицы в моей базе данных SQL, и я пытаюсь свернуть строки на сервере SQL с помощью функции вещи из 3 таблиц

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

select T1.Id, 
       T1.Name,
       T2.Id,
       T2.T1_Id,
       T2.Name,
       stuff(
       (
           select ','+T3.Name 
           from Test3 T3 
           where T3.Id=T2.T3_Id for xml path('')
       ),1,1,'') as Test5
from Test1 T1,Test2 T2 
where T1.Id=T2.T1_Id

я получил результат как

Id  Name    Id  T1_Id   Name    Test5
----------------------------------------------
1   Test1   1    1      ASD      BAAN
1   Test1   2    1      ASD      KAAL

ожидается

Id  Name    Id  T1_Id   Name    Test5
1   Test1   1     1      ASD  BAAN,KAAL

Мои сценарии таблиц и примеры данных, и я использую SQL Server 2014

   CREATE TABLE [dbo].[Test1](
        [Id] [int] NULL,
        [Name] [varchar](50) NULL
    ) ON [PRIMARY]
    GO

   INSERT INTO [dbo].[Test1]
           ([Id]
           ,[Name])
     VALUES
           (1
           ,'Test1')
           GO

    CREATE TABLE [dbo].[Test2](
        [Id] [int] NOT NULL,
        [T1_Id] [int] NOT NULL,
        [T3_Id] [int] NOT NULL,
        [Name] [varchar](50) NULL
    ) ON [PRIMARY]


    INSERT INTO [dbo].[Test2]
           ([Id]
           ,[T1_Id]
           ,[T3_Id]
           ,[Name])
     VALUES
           (1
           ,1
           ,1
           ,'ASD'),(2,1,2,'ASD')
           GO


    CREATE TABLE [dbo].[Test3](
        [Id] [int] NULL,
        [Name] [varchar](50) NULL
    ) ON [PRIMARY]

    GO

   INSERT INTO [dbo].[Test3]
           ([Id]
           ,[Name])
     VALUES
           (1
           ,'KAAL'),(2,'BAAL')
           Go

Ответы [ 4 ]

2 голосов
/ 22 мая 2019

В языке SQL нет слова "Свернуть". То, что вы опубликовали, показывает группировку и агрегирование. Для строк единственными значимыми агрегатами являются MIN, MAX и конкатенация строк.

SQL Server 2017 обеспечивает конкатенацию строк с помощью функции STRING_AGG. Другие продукты баз данных используют другие имена, например GROUP_CONCAT. В более ранних версиях SQL Server для достижения этой цели использовались различные методы. То, что вы опубликовали, - это техника XML.

Однако в запросе нет предложения GROUP BY, поэтому возвращается несколько строк.

В SQL Server 2017 запрос будет выглядеть следующим образом:

select T1.Id, 
       T1.Name,
       MIN(T2.Id) as T2_ID,
       MIN(T2.T1_Id) as T1_ID,
       MIN(T2.Name) as T2_Name,
       STRING_AGG(T3.Name,', ') as Test5
from @Test1 T1 
    inner join @Test2 T2 on T1.Id=T2.T1_Id
    inner join @Test3 T3 on T3.Id=T2.T3_Id 
GROUP BY T1.ID,T1.Name

Делать то же самое в более ранних версиях сложнее. Запрос FOR XML может коррелировать только со столбцами, которые появляются в предложении GROUP BY:

select T1.Id, 
       T1.Name,
       MIN(T2.Id),
       MIN(T2.T1_Id),
       MIN(T2.Name),
       stuff(
       (
           SELECT ','+T3.Name 
           FROM @Test3 T3 
              inner join @Test2 TT2 on T3.Id=TT2.T3_Id
           WHERE TT2.T1_ID=T1.Id 
           FOR XML PATH('')
       ),1,1,'') as Test5
from @Test1 T1 
    inner join @Test2 T2 on T1.Id=T2.T1_Id
GROUP BY T1.ID,T1.Name

Думайте о всей части STUFF(... FOR XML).. как о единой функции, которая принимает в качестве аргумента один из столбцов группировки в предложении WHERE, ищет некоторые таблицы и объединяет строковые результаты.

Это означает, что условие корреляции

WHERE TT2.T1_ID=T1.Id 

может ссылаться только на столбцы группировки во внешнем запросе. Нам нужно соединение с T2, потому что мы не можем напрямую попасть в T1.ID из T3

Если вам не нужны столбцы T2, вы можете избавиться от JOIN во внешнем запросе:

select T1.Id, 
       T1.Name,
       stuff(
       (
           select ','+T3.Name 
           from @Test3 T3 
              inner join @Test2 TT2 on T3.Id=TT2.T3_Id
              where TT2.T1_ID=T1.Id for xml path('')
       ),1,1,'') as Test5
from @Test1 T1 
GROUP BY T1.ID,T1.Name
1 голос
/ 22 мая 2019

Если вас не интересует Test2.Name, вы можете получить результат, подобный приведенному ниже:

WITH tblMain as (SELECT T1.Id,T1.Name, T3.Name T3Name
                 FROM Test2 T2 LEFT OUTER JOIN
                 Test1 T1 on T2.T1_Id = T1.Id LEFT OUTER JOIN
                 Test3 T3 on T2.T3_Id = T3.Id)

SELECT Id, Name, 
    STUFF((SELECT ', ' + T3Name
           FROM tblMain b 
           WHERE b.Id = a.Id AND b.Name = a.Name 
           FOR XML PATH('')), 1, 2, '') as Test5
FROM tblMain a
GROUP BY Id, Name

В противном случае, если вам нужны другие столбцы из Test2, вы должны добавить их также в предложение GROUP BY.

1 голос
/ 22 мая 2019

Попробуйте это

;WITH CTE AS 
(
SELECT T1.Id AS T1_Id,
       T1.Name AS T1_Name,
       T2.Id  AS  T2_Id,
       T2.T1_Id AS T2_T1_Id,
       T2.Name AS T2_Name, 
       T3.Name AS Test5
FROM [Test2] t2
    INNER JOIN [Test1] t1
ON t1.Id = t2.T1_Id
    INNER JOIN [Test3] t3
ON t3.id = t2.T3_Id
)
SELECT  T1_Id,  
        T1_Name,            
        T2_T1_Id,   
        T2_Name,    
        STUFF((SELECT  ', '+Test5 
                FROM CTE i WHERE i.T1_Id = o.T1_Id 
                FOR XML PATH ('')),1,1,'') AS Test5
FROM CTE o
GROUP BY T1_Id, 
        T1_Name,            
        T2_T1_Id,   
        T2_Name
0 голосов
/ 22 мая 2019

Попробуйте следующий скрипт.

Примечание: Вы должны избегать идентификаторов из Test2 и Test3, так как в противном случае вы не можете применять GROUP BY для STUFF, поскольку они имеют разные значения в столбце ID..

SELECT *,
STUFF
(
    (
        SELECT ',' + Name 
        FROM
        (
            SELECT T3.Name
            FROM Test1 T1 
            INNER JOIN Test2 T2 ON T1.Id = T2.T1_Id
            INNER JOIN Test3 T3 ON T2.T3_Id = T3.ID
        ) B
        FOR XML PATH ('')
    ), 1, 1, ''
) Test5
FROM
(
    SELECT T1.Id,T1.Name T1_Name,T2.Name T2_Name
    FROM Test1 T1 
    INNER JOIN Test2 T2 ON T1.Id = T2.T1_Id
)A  
GROUP BY A.Id,A.T1_Name,A.T2_Name
...