Как вы объединяете строки из двух таблиц SQL без дублирования строк?

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

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

Два примера таблиц (люди и job_roles) приведены ниже, чтобы вам было легче понять вопрос.

Люди

 id |  name  |    email_address   |  phone_number
 1  |  paul  |  [email protected]  |  123456
 2  |   bob  |  [email protected]   |  567891
 3  |  bart  |  [email protected]  |  987561

job_roles

 id  |  person_id  |     job_title   | department
  1  |      1      |     secretary   |    hr
  2  |      1      |     assistant   |   media
  3  |      2      |      manager    |    IT
  4  |      3      |  finance clerk  |  finance
  4  |      3      |      manager    |    IT

так что я могу вывести каждого человека и его роли вот так

Name: paul
Email Address: [email protected]
Phone: 123456
Job Roles: 
Secretary for HR department
Assistant for media department
_______
Name: bob
Email address: [email protected]
Phone: 567891
Job roles:
Manager for IT department

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

Спасибо

Павел

PS это будет база данных mySQL, если это имеет значение


person AdrenalineJunky    schedule 23.06.2010    source источник


Ответы (4)


RE:

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

Вы можете довольно близко подобраться к

SELECT  p.id, p.name, p.email_address, p.phone_number,
group_concat(concat(job_title, ' for ', department, ' department')  SEPARATOR '\n') AS JobRoles
FROM People AS p 
    INNER JOIN job_roles AS r ON p.id = r.person_id
GROUP BY p.id, p.name, p.email_address, p.phone_number
 ORDER BY p.name;
person Martin Smith    schedule 23.06.2010
comment
Это очень умно и полезно знать, хотя я по-прежнему предпочитаю выполнять обычное соединение и форматировать данные в PHP. Если вы когда-нибудь захотите изменить форматирование (например, добавить ‹br› между рабочими ролями), вам придется изменить SQL-запрос. GROUP_CONCAT устраняет разделение логики БД и представления. Это также значительно усложняет модульное тестирование. - person dave1010; 27.07.2010

Это выглядит как прямое соединение:

SELECT p.*, j.*
  FROM People AS p INNER JOIN Roles AS r ON p.id = r.person_id
 ORDER BY p.name;

Оставшаяся часть работы — форматирование; это лучше всего сделать с помощью пакета отчетов.


Спасибо за быстрый ответ, это кажется хорошим началом, но вы получаете несколько строк на человека, например (вы должны представить, что это таблица, поскольку вы, похоже, не можете форматировать комментарии):

id | Name | email_address    | phone_number | job_role  | department
 1 | paul | [email protected] | 123456       | secretary | HR
 1 | paul | [email protected] | 123456       | assistant | media
 2 | bob  | [email protected]  | 567891       | manager   | IT

Я бы хотел, чтобы в идеале по одной строке на человека со всеми их рабочими ролями, если это возможно?

Это зависит от вашей СУБД, но большинство доступных не поддерживают RVA — атрибуты, значимые для отношений. Вам бы хотелось, чтобы роль работы и отдел были частью результата, как таблица, связанная с пользователем:

+----+------+------------------+--------------+------------------------+
| id | Name | email_address    | phone_number |   dept_role            |
+----+------+------------------+--------------+------------------------+
|    |      |                  |              | +--------------------+ |
|    |      |                  |              | | job_role   | dept  | |
|  1 | paul | [email protected] | 123456       | | secretary  | HR    | |
|    |      |                  |              | | assistant  | media | |
|    |      |                  |              | +--------------------+ |
+----+------+------------------+--------------+------------------------+
|    |      |                  |              | +--------------------+ |
|    |      |                  |              | | job_role   | dept  | |
|  2 | bob  | [email protected]  | 567891       | | manager    | IT    | |
|    |      |                  |              | +--------------------+ |
+----+------+------------------+--------------+------------------------+

Это точно представляет нужную информацию, но обычно это не вариант.

Итак, что произойдет дальше, зависит от вашего инструмента генерации отчетов. Используя тот, с которым я больше всего знаком (Informix ACE, часть Informix SQL, доступный от IBM для использования с СУБД Informix), вы просто убедитесь, что данные отсортированы, а затем напечатаете имя, адрес электронной почты и номер телефона. в разделе «ДО ГРУППА id» отчета, а в разделе «НА КАЖДОЙ СТРОКЕ» вы будете обрабатывать (печатать) только информацию о роли и отделе.

Часто рекомендуется отделить форматирование отчета от операций извлечения данных; это пример того, где это необходимо, если ваша СУБД не имеет необычных функций, помогающих форматировать выбранные данные.


О боже, это звучит очень сложно, и это не то, что я мог бы легко запустить в базе данных mySQL на странице PHP?

Материал RVA - вы правы, это не для MySQL и PHP.

С другой стороны, существуют миллионы отчетов (имеются в виду результаты запросов, отформатированные для представления пользователю), которые примерно так и делают. Технический термин для них — Отчет Control-Break. основная идея не сложная.

Вы сохраняете запись номера 'id', который вы в последний раз обрабатывали - вы можете инициализировать его значением -1 или 0. Когда текущая запись имеет номер id, отличный от предыдущего номера, тогда у вас есть новый пользователь, и вам нужно начать новый набор выходных строк для нового пользователя и печать имени, адреса электронной почты и номера телефона (и изменение последнего обработанного идентификационного номера). Когда текущая запись имеет тот же идентификационный номер, все, что вы делаете, — это обрабатываете информацию о должности и отделе (а не имя, адрес электронной почты и номер телефона). «Перерыв» происходит при изменении идентификационного номера. С одним уровнем контроля-брейка это не сложно; если у вас 4 или 5 уровней, вам нужно проделать больше работы, поэтому для этого существуют пакеты отчетов.

Так что это не сложно - просто нужно немного осторожности.

person Jonathan Leffler    schedule 23.06.2010
comment
+1: побейте меня, поэтому я просто добавлю (эту ссылку о визуальном представлении JOIN)[codinghorror.com/blog/2007/10/ - person OMG Ponies; 24.06.2010
comment
Спасибо за быстрый ответ, это кажется хорошим началом, но вы получаете несколько строк на человека, например (вы должны представить, что это таблица, поскольку вы, похоже, не можете форматировать комментарии): id | Имя | адрес электронной почты | номер_телефона | job_role | отдел 1 | пол | [email protected] | 123456 | секретарь | HR 1 | пол | [email protected] | 123456 | помощник | СМИ 2 | боб | [email protected] | 567891 | менеджер | IT Я бы хотел, чтобы в идеале у каждого человека была одна строка со всеми их должностными обязанностями, если это возможно? - person AdrenalineJunky; 24.06.2010
comment
О боже, это звучит очень сложно, и это не то, что я мог бы легко запустить в базе данных mySQL на странице PHP? - person AdrenalineJunky; 24.06.2010
comment
Спасибо за информацию, она очень полезна, я использовал некоторые ваши идеи вместе с group_concat, предложенными Мартином Смитом. - person AdrenalineJunky; 22.07.2010

Выполнение этого так, как вы хотите, означало бы, что массивы наборов результатов могут иметь бесконечные столбцы, что было бы очень беспорядочно. например, вы можете оставить соединение с таблицей заданий 10 раз и получить задание1, задание2, .. задание10.

Я бы сделал одно соединение, а затем использовал PHP, чтобы проверить, совпадает ли идентификатор имени от 1 строки к следующей.

person dave1010    schedule 23.06.2010
comment
Спасибо, это вывод, к которому я пришел, но надеялся, что SQL может сделать что-то умное и красиво соединить строки, поэтому у меня был по существу столбец рабочих мест с этими рабочими местами в нем. - person AdrenalineJunky; 24.06.2010

Одним из способов может быть левое внешнее соединение таблиц, а затем загрузка их в массив с помощью

$people_array =array(); 
while($row1=mysql_fetch_assoc($extract1)){ 
$people_array[] = $row1;  
} 

а затем выполните цикл, используя

 for ($x=0;$x<=sizeof($people_array;) 
    {  
echo $people_array[$x][id]; 
echo $people_array[$x][name]; 

for($y=0;$y<=$number_of_roles;$y++) 
{ 
 echo $people_array[$x][email_address]; 
 echo $people_array[$x][phone_number]; 
    $x++; 
} 
     } 

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

person bsandrabr    schedule 23.06.2010
comment
Хорошая идея, но групповой concat оказался более чистым решением. - person AdrenalineJunky; 22.07.2010