Как создать список через запятую, используя SQL-запрос?

52

У меня есть 3 таблицы:

  • Приложения (id, name)
  • Ресурсы (id, name)
  • ApplicationsResources (id, app_id, resource_id)

Я хочу показать в GUI таблицу всех имен ресурсов. В одной ячейке каждой строки я хотел бы указать все приложения (разделенные запятыми) этого ресурса.

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

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

Есть ли способ сделать это в одном запросе?

  • 0
    Какую базу данных вы используете?
  • 0
    я хотел бы, чтобы мое решение было дБ агностиком
Показать ещё 5 комментариев
Теги:
database
sql-server
tsql
string-aggregation

11 ответов

2
Лучший ответ

Невозможно сделать это с помощью DB-agnostic. Поэтому вам нужно получить весь набор данных следующим образом:

select 
  r.name as ResName, 
  a.name as AppName
from 
  Resouces as r, 
  Applications as a, 
  ApplicationsResources as ar
where
  ar.app_id = a.id 
  and ar.resource_id = r.id

И затем соедините AppName программно при группировке по ResName.

114

MySQL

  SELECT r.name,
         GROUP_CONCAT(a.name SEPARATOR ',')
    FROM RESOURCES r
    JOIN APPLICATIONSRESOURCES ar ON ar.resource_id = r.id
    JOIN APPLICATIONS a ON a.id = ar.app_id
GROUP BY r.name

SQL Server (2005+)

SELECT r.name,
       STUFF((SELECT ','+ a.name
               FROM APPLICATIONS a
               JOIN APPLICATIONRESOURCES ar ON ar.app_id = a.id
              WHERE ar.resource_id = r.id
           GROUP BY a.name
            FOR XML PATH(''), TYPE).value('.','VARCHAR(max)'), 1, 1, '')
 FROM RESOURCES r

SQL Server (2017+)

  SELECT r.name,
         STRING_AGG(a.name, ',')
    FROM RESOURCES r
    JOIN APPLICATIONSRESOURCES ar ON ar.resource_id = r.id
    JOIN APPLICATIONS a ON a.id = ar.app_id
GROUP BY r.name

оракул

Я рекомендую прочитать об агрегации/конкатенации строк в Oracle.

  • 0
    Я обновил синтаксис для правильного использования GROUP_CONCAT.
  • 7
    SQL Server сделал это без причины
Показать ещё 2 комментария
25

Использование COALESCE для создания строкой с разделителями-запятыми в SQL Server
http://www.sqlteam.com/article/using-coalesce-to-build-comma-delimited-string

Пример:

DECLARE @EmployeeList varchar(100)

SELECT @EmployeeList = COALESCE(@EmployeeList + ', ', '') + 
   CAST(Emp_UniqueID AS varchar(5))
FROM SalesCallsEmployees
WHERE SalCal_UniqueID = 1

SELECT @EmployeeList
  • 3
    это кажется гораздо лучшим решением, чем ответы с более высоким рейтингом. Легко и универсально.
  • 0
    Что если вы хотите использовать EmployeeList для каждого (Group by) отдела?
Показать ещё 1 комментарий
11

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

Для SQL Server 2005 и выше вы можете использовать:

SELECT
     r.ID, r.Name,
     Resources = STUFF(
       (SELECT ','+a.Name
        FROM dbo.Applications a
        INNER JOIN dbo.ApplicationsResources ar ON ar.app_id = a.id
        WHERE ar.resource_id = r.id
        FOR XML PATH('')), 1, 1, '')
FROM
     dbo.Resources r

Он использует конструкцию SQL Server 2005 FOR XML PATH для перечисления подэлементов (приложений для данного ресурса) в виде списка, разделенного запятыми.

Марк

  • 0
    Я полагаю, что вам не хватает "р". ВНУТРЕННЕЕ СОЕДИНЕНИЕ должно сказать "ON ar.app_id = a.id". У меня сейчас работает. Благодарю.
  • 1
    @MagnusSmith: абсолютно! Спасибо за ваши "орлиные глаза", обнаружившие эту опечатку! Исправлена.
5

Я считаю, что вы хотите:

SELECT ItemName, GROUP_CONCAT(DepartmentId) FROM table_name GROUP BY ItemName

Если вы используете MySQL

Ссылка

  • 0
    GROUP_CONCAT работает только в MySQL и SQLite. ОП сказал в комментарии, что они используют MS SQL Server 2008.
  • 0
    Не видел комментарий, пока после поста. Должен ли я удалить свой ответ за неактуальность или просто оставить его для интереса?
Показать ещё 2 комментария
5

Предполагая SQL Server:

Структура таблицы:

CREATE TABLE [dbo].[item_dept](
    [ItemName] char(20) NULL,
    [DepartmentID] int NULL   
)

Query:

SELECT ItemName,
       STUFF((SELECT ',' + rtrim(convert(char(10),DepartmentID))
        FROM   item_dept b
        WHERE  a.ItemName = b.ItemName
        FOR XML PATH('')),1,1,'') DepartmentID
FROM   item_dept a
GROUP BY ItemName

Результаты:

ItemName    DepartmentID
item1       21,13,9,36
item2       4,9,44
  • 0
    +1 Это работает, но ИМХО это решение для SQL Server взломано, ужасно некрасиво и не интуитивно понятно. Не твоя вина, хотя. :)
  • 0
    Существует другое решение SQL Server, которое можно использовать с пользовательскими агрегатами CLR, если гарантированный порядок элементов с разделителями не является обязательным.
4

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

Create FUNCTION dbo.ufnGetEmployeeMultiple(@DepartmentID int)
RETURNS VARCHAR(1000) AS

BEGIN

DECLARE @Employeelist varchar(1000)

SELECT @Employeelist = COALESCE(@Employeelist + ', ', '') + E.LoginID
FROM humanresources.Employee E

Left JOIN humanresources.EmployeeDepartmentHistory H ON
E.BusinessEntityID = H.BusinessEntityID

INNER JOIN HumanResources.Department D ON
H.DepartmentID = D.DepartmentID

Where H.DepartmentID = @DepartmentID

Return @Employeelist

END

SELECT D.name as Department, dbo.ufnGetEmployeeMultiple (D.DepartmentID)as Employees
FROM HumanResources.Department D

SELECT Distinct (D.name) as Department, dbo.ufnGetEmployeeMultiple (D.DepartmentID) as 
Employees
FROM HumanResources.Department D
3

Из следующая версия SQL Server вы сможете сделать

SELECT r.name,
       STRING_AGG(a.name, ',')
FROM   RESOURCES r
       JOIN APPLICATIONSRESOURCES ar
         ON ar.resource_id = r.id
       JOIN APPLICATIONS a
         ON a.id = ar.app_id
GROUP  BY r.name 

Для предыдущих версий продукта существует довольно много разных подходов к этой проблеме. Отличный обзор их в статье: Объединение строк в Transact-SQL.

  • Конкатенация значений, когда количество элементов неизвестно

    • Рекурсивный метод CTE
    • XML-методы blackbox
    • Использование режима Common Language Runtime
    • Скалярный UDF с рекурсией
    • Таблица с оценкой UDF с циклом WHILE
    • Динамический SQL
    • Подход курсора
      .
  • Не надежные подходы

    • Скалярный UDF с расширением расширения t-SQL
    • Скалярный UDF с конкатенацией переменных в SELECT
2

MySQL

  SELECT r.name,
         GROUP_CONCAT(a.name SEPARATOR ',')
    FROM RESOURCES r
    JOIN APPLICATIONSRESOURCES ar ON ar.resource_id = r.id
    JOIN APPLICATIONS a ON a.id = ar.app_id
GROUP BY r.name

**


MS SQL Server

SELECT r.name,
       STUFF((SELECT ','+ a.name
               FROM APPLICATIONS a
               JOIN APPLICATIONRESOURCES ar ON ar.app_id = a.id
              WHERE ar.resource_id = r.id
           GROUP BY a.name
            FOR XML PATH(''), TYPE).value('.','VARCHAR(max)'), 1, 1, '')
 FROM RESOURCES r
 GROUP BY deptno;

Oracle

  SELECT r.name,
         LISTAGG(a.name SEPARATOR ',') WITHIN GROUP (ORDER BY a.name)
  FROM RESOURCES r
        JOIN APPLICATIONSRESOURCES ar ON ar.resource_id = r.id
        JOIN APPLICATIONS a ON a.id = ar.app_id
  GROUP BY r.name;
1

Это будет сделано в SQL Server:

DECLARE @listStr VARCHAR(MAX)
SELECT @listStr = COALESCE(@listStr+',' ,'') + Convert(nvarchar(8),DepartmentId)
FROM Table
SELECT @listStr
  • 1
    Это не будет делать это для каждой GROUP BY, хотя. Для этого вам необходимо изменить ответ marc_s на возможный дубликат вопроса stackoverflow.com/questions/1817985/… (или см. Ответ Кеннета в этом вопросе)
1

Чтобы быть агностиком, откиньтесь назад и punt.

Select a.name as a_name, r.name as r_name
  from ApplicationsResource ar, Applications a, Resources r
 where a.id = ar.app_id
   and r.id = ar.resource_id
 order by r.name, a.name;

Теперь пользовательский язык программирования сервера объединяет a_names, а r_name - тот же, что и в последний раз.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню