SQL-сервер 2012.

Изменить: мой исходный запрос был более сложным, чем должен быть, поскольку я попытался выполнить особый запрос для подмножества полей в таблице и присоединиться к нему в самой таблице, чтобы получить другие (текстовые) поля. следующий запрос тоже поможет:

SELECT DISTINCT
    p1.id
    ,p1.Name
    ,CAST( p1.[Description] AS nvarchar(max)) AS Description
    ,( SELECT [Category] + ', '
           FROM [dbo].[Company] AS p2
          WHERE p2.Id = p1.Id
          ORDER BY Name
            FOR XML PATH('') ) AS Categories
  FROM [dbo].[Company] AS p1
  ORDER BY p1.Id

У меня есть таблица с данными, подобными этой (несколько записей для каждой компании, которые идентичны, за исключением поля категории):

+----+------+-----------------+----------+
| Id | Name | Description     | Category |
+----+------+-----------------+----------+
| 1  | AAA  | <loads of text> | cat1     |
| 1  | AAA  | <loads of text> | cat2     |
| 2  | BBB  | <even more text>| cat1     |
| 2  | BBB  | <even more text>| cat3     |
+----+------+-----------------+----------+

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

| 1  | AAA  | <loads of text> | cat1, cat2 |
| 2  | BBB  | <even more text>| cat1, cat3 |

Используя информацию из разных тем по SO, я придумал следующее:

SELECT 
    t1.Id
    ,t2.Name
    ,t2.[Description]
    ,t1.Category
FROM  [dbo].[Company] AS t2 
INNER JOIN (SELECT DISTINCT p1.Id
      ,( SELECT [Category] + ', '
           FROM [dbo].[Company] AS p2
          WHERE p2.Id = p1.Id
          ORDER BY Name
            FOR XML PATH('') ) AS Category
  FROM [dbo].[Company] AS p1
  ) AS t1 ON t1.Id = t2.Id
  ORDER BY t1.Id

В результате запроса есть запись для каждой записи в таблице Company, где категории объединены в поле категории:

+----+------+-----------------+------------+
| Id | Name | Description     | Category   |
+----+------+-----------------+------------+
| 1  | AAA  | <loads of text> | cat1, cat2 |
| 1  | AAA  | <loads of text> | cat1, cat2 |
| 2  | BBB  | <even more text>| cat1, cat3 |
| 2  | BBB  | <even more text>| cat1, cat3 |
+----+------+-----------------+------------+

Я думал, что INNER JOIN будет выбирать строки только в том случае, если обе таблицы совпадают. Подзапрос сам по себе дает ожидаемый результат (одна запись на каждый идентификатор с агрегированными категориями). Я попробовал использовать другое предложение group by для всего запроса, но это не удалось, потому что я не могу включить поле Description в предложение group, поскольку это поле текстового типа.

Что мне не хватает?

1
Mark Hagers 6 Фев 2015 в 17:43

3 ответа

Лучший ответ

Я бы попробовал DISTINCT во внешнем запросе. Это должно решить вашу проблему, если описания / имена не различаются для некоторых строк, что вполне возможно, поскольку ваша таблица базы данных, вероятно, должна быть двумя таблицами, и, вероятно, вы никогда не писали код, чтобы гарантировать, что описание / имя остается неизменным для каждого МНЕ БЫ. Если у вас есть составной уникальный индекс по идентификатору, имени и описанию, то, скорее всего, все в порядке.

Если у вас есть проблема с несколькими описаниями, вам нужно будет использовать агрегат, чтобы исправить проблему во внешнем запросе. Или вам нужно будет исправить данные и добавить уникальный индекс, чтобы предотвратить появление в будущем.

Что касается того, почему у вас возникла эта проблема, соединение работает нормально, но у вас есть отношение «один-много» между производной таблицей и другой таблицей. Вот почему вы получаете несколько записей и почему отдельный должен исправить это, если данные для идентификатора не отличаются по имени и описанию.

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

SELECT DISTINCT
    t1.Id
    ,t2.Name
    ,cast(t2.[Description] as nvarchar(max))
    ,t1.Category
FROM  [dbo].[Company] AS t2 
INNER JOIN (SELECT DISTINCT p1.Id
      ,( SELECT [Category] + ', '
           FROM [dbo].[Company] AS p2
          WHERE p2.Id = p1.Id
          ORDER BY Name
            FOR XML PATH('') ) AS Category
  FROM [dbo].[Company] AS p1
  ) AS t1 ON t1.Id = t2.Id
  ORDER BY t1.Id

В качестве альтернативы вы можете исправить свой плохой дизайн стола.

2
HLGEM 6 Фев 2015 в 15:19
SELECT  *
FROM    ( SELECT    t1.Id ,
                    t2.Name ,
                    t2.[Description] ,
                    t1.Category ,
                    ROW_NUMBER() OVER ( PARTITION BY t1.Id, t2.Name,
                                        t1.Category ORDER BY t1.id ) row_num
          FROM      [dbo].[Company] AS t2
                    INNER JOIN ( SELECT DISTINCT
                                        p1.Id ,
                                        ( SELECT    [Category] + ', '
                                          FROM      [dbo].[Company] AS p2
                                          WHERE     p2.Id = p1.Id
                                          ORDER BY  Name
                                        FOR
                                          XML PATH('')
                                        ) AS Category
                                 FROM   [dbo].[Company] AS p1
                               ) AS t1 ON t1.Id = t2.Id
        ) t1
ORDER BY t1.Id
1
Dmitrij Kultasev 6 Фев 2015 в 15:22

Чтобы обойти неприятную проблему использования текста в качестве типа данных, вы можете преобразовать этот столбец, когда вы его потянете. Если это вообще возможно, я бы навсегда изменил столбец на varchar (max).

Что-то вроде этого:

SELECT 
    t1.Id
    , t2.Name
    , cast(t2.[Description] as varchar(max)) as Description
    , t1.Category
FROM  [dbo].[Company] AS t2 
INNER JOIN (SELECT DISTINCT p1.Id
      ,( SELECT [Category] + ', '
           FROM [dbo].[Company] AS p2
          WHERE p2.Id = p1.Id
          ORDER BY Name
            FOR XML PATH('') ) AS Category
  FROM [dbo].[Company] AS p1
  ) AS t1 ON t1.Id = t2.Id
  GROUP BY Id
    , Name
    , cast(t2.[Description] as varchar(max))
  ORDER BY t1.Id
1
Sean Lange 6 Фев 2015 в 15:17