Как получить несколько записей против одной записи на основе отношения?
У меня есть две таблицы "Организация" и "Сотрудник", имеющие отношение "один ко многим", т.е. в одной организации может быть несколько сотрудников. Теперь я хочу выбрать всю информацию о конкретной организации плюс имя всех сотрудников этой организации. Какой лучший способ сделать это? Могу ли я получить все это в одном наборе записей, или мне придется получить несколько строк на основе нет. из сотрудников? Вот немного графическая демонстрация того, что я хочу:
Org_ID Org_Address Org_OtherDetails Employess
1 132A B Road List of details Emp1, Emp2, Emp3.....
9 ответов
Первоначальный вопрос был специфичен для базы данных, но, возможно, это хорошее место, чтобы включить более общий ответ. Это общий вопрос. Концепцию, которую вы описываете, часто называют "объединением групп". Там нет стандартного решения в SQL-92 или SQL-99. Так что вам нужно решение для конкретного поставщика.
- MySQL - возможно, самое простое решение. Используйте встроенную функцию GROUP_CONCAT. В вашем примере вы хотели бы что-то вроде этого:
Выбрать o.ID, o.Address, o.OtherDetails, GROUP_CONCAT( concat(e.firstname, ' ', e.lastname)) в качестве сотрудников от сотрудники е организация внутреннего присоединения o на o.org_id=e.org_id группа по o.org_id
- PostgreSQL - РЕДАКТИРОВАТЬ: PostgreSQL 9.0 теперь одинаково прост, поскольку string_agg (выражение, разделитель) встроен. Вот с запятой между элементами:
Выбрать o.ID, o.Address, o.OtherDetails, STRING_AGG( (e.firstname || ' ' || e.lastname), ', ') в качестве сотрудников от сотрудники е организация внутреннего присоединения o на o.org_id=e.org_id группа по o.org_id
PostgreSQL до 9.0 позволяет вам определять ваши собственные агрегатные функции с помощью CREATE AGGREGATE. Чуть больше работы, чем MySQL, но гораздо более гибкий. Смотрите этот другой пост для более подробной информации. (Конечно, PostgreSQL 9.0 и более поздние версии также имеют эту опцию.)
- Oracle & MS SQL Server - создайте хранимую процедуру, которая принимает org_id в качестве входных данных и выводит объединенные имена сотрудников. Затем используйте эту хранимую процедуру в своем запросе. Некоторые другие ответы здесь включают некоторые подробности о том, как писать хранимые процедуры, подобные этим.
Выбрать o.ID, o.Address, o.OtherDetails, MY_CUSTOM_GROUP_CONCAT_PROCEDURE( o.ID) в качестве сотрудников от организация о
- Другие технологии СУБД - маршрут хранимой процедуры является наиболее вероятным. Возможно, другие могут обновить этот ответ более специфичными для технологии ответами.
Поскольку вопрос помечен как MySQL, вы должны иметь возможность использовать решение, специфичное для MySQL, а именно GROUP_CONCAT. Например,
select Org_ID, Org_Address, Org_OtherDetails,
GROUP_CONCAT(employees) as Employees
from employees a, organization b
where a.org_id=b.org_id
group by b.org_id;
В MS SQL вы можете сделать:
create function dbo.table2list (@input int)
returns varchar(8000)
as
BEGIN
declare @putout varchar(8000)
set @putout = ''
select @putout = @putout + ', ' + <employeename>
from <employeetable>
where <orgid> = @input
return @putout
end
затем сделайте:
select * from org, dbo.table2list(orgid)
from <organisationtable>
Я думаю, что вы можете сделать это с помощью COALESCE(), но не можете вспомнить синтаксис на моей голове
Короткий ответ - нет".
Как отмечалось в других ответах, существуют конкретные способы достижения этого результата, но не существует чистого решения SQL, которое бы работало в одном запросе.
Извини за это:(
Предположительно одно из решений конкретного производителя подойдет вам?
Если вы используете Oracle, вы можете создать функцию PL/SQL, которую вы можете использовать в своем запросе, которая принимает в качестве входных данных значение organization_id и возвращает имя всех сотрудников, принадлежащих к этой организации, в виде строки. Например:-
select
o.org_id,
o.org_address,
o.org_otherdetails,
org_employees( o.org_id ) as org_employees
from
organization o
Вот что вы можете сделать, у вас есть 2 варианта:
select *
FROM
users u
LEFT JOIN organizations o ON (u.idorg = o.id);
Таким образом, вы получите дополнительные данные по каждой строке - полная информация об организации, которая вам на самом деле не нужна.
Или вы можете сделать:
select o.*, group_concat(u.name)
FROM
users u
LEFT JOIN organizations o ON (u.idorg = o.id)
GROUP BY
o.id
http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html
Второй подход применим, если вы хотите увидеть, т.е. список имен пользователей "user1, user2, user3", но не хотят работать с самими полями...
Для SQL Server агрегаты SQLCLR в этом проекте основаны на примере кода MSDN, однако они работают намного лучше и позволяют при необходимости сортировать (в виде строк) и альтернативные разделители. Они предлагают почти эквивалентную функциональность MySQL GROUP_CONCAT для SQL Server.
Полное сравнение преимуществ / недостатков агрегатов CLR и решения FOR XML можно найти в документации:
Для SQL Server 2017 и Azure SQL теперь есть функция STRING_AGG (Transact-SQL), чтобы это было на одном уровне с MySQL:
https://docs.microsoft.com/en-us/sql/t-sql/functions/string-agg-transact-sql?view=sql-server-ver15.
SELECT attribute1, STRING_AGG (attribute2, '|') AS Attribute2
FROM table
GROUP BY attribute
Все это зависит. Если вы выполните объединение, вы получите все данные об организации в каждой строке. (1 строка на сотрудника). Это имеет цену. Если вы делаете два запроса. (Org и Emp), который имеет другую стоимость.
Выбрать свой яд.