Вывод одной строки на каждое имя с учетом метки

370
28 января 2017, 10:10

Есть таблица stuff_id с полями id и name.
Есть таблица stuff_phone с полями id, phone, preference.

У одного сотрудника может быть несколько телефонов. У одного из его телефонов может быть предпочтение, в таком случае для этого телефона в поле preference стоит какая-то метка, скажем, 'p'.

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

Как написать такой запрос?

UPD: Для связи таблиц используется поле id. Можно считать поле preference битовым - не суть. Хотелось бы какое-то стандартное ANSI решение, которое работало бы везде, а так всё равно на какой реализации. Главное - суть вопроса, а не нюансы. Как сделать такое объединение таблиц, чтобы имени ставился в соответствие один номер из множества?

Answer 1

Можно подзапросом в поле:

SELECT stuff_id.Id, stuff_id.name,
   (select top 1 stuff_phone.phone from stuff_phone where stuffid = stuff_id.Id 
  order by  stuff_phone.preference) phone
FROM stuff_id 

(для MSSQL работать будет)

Для MySQL и других СУБД, поддерживающих LIMIT, вместо top:

SELECT stuff_id.Id, stuff_id.name,
   (select stuff_phone.phone from stuff_phone where stuffid = stuff_id.Id 
     order by stuff_phone.preference
     limit 1) phone
FROM stuff_id 

Раз уж затронута тема первая строка из выборки, то приведу как звучит это в разных субд:

  • MSSQL,SYBASE,db2: select top 1 stuff_phone.phone from stuff_phone where stuffid = stuff_id.Id
  • informix,firebird select first 1 stuff_phone.phone from stuff_phone where stuffid=stuff_id.Id
  • oracle <12 select stuff_phone.phone from stuff_phone where stuffid = stuff_id.Id and rownum=1
  • oracle 12 select stuff_phone.phone from stuff_phone where stuffid = stuff_id.Id fetch first 1 rows only
  • MySql select stuff_phone.phone from stuff_phone where stuffid = stuff_id.Id limit 1
Answer 2

ANSI решение:

SELECT st.Id, st.name, coalesce(max(ph1.phone),max(ph2.phone)) as phone
  FROM stuff as st
  LEFT JOIN stuff_phone ph1 ON st.id = ph1.id and ph1.preference='p'
  LEFT JOIN stuff_phone ph2 ON st.id = ph2.id and ph2.preference!='p'
 GROUP BY st.Id, st.name

Или:

SELECT st.Id, st.name,
       coalesce(max(case when ph1.preference='p' then ph1.phone end),
                max(ph1.phone)) as phone
  FROM stuff as st
  LEFT JOIN stuff_phone ph1 ON st.id = ph1.id
 GROUP BY st.Id, st.name

Но конечно, практически в любой современной СУБД найдется более быстрое решение.

Answer 3

Попробуйте такой вариант, но может будет предложен лучше

SELECT st.Id, st.name, ph.phone FROM stuff_id as st
INNER JOIN  stuff_phone as ph ON ph.stuffid = st.Id 
WHERE ph.Id = 
(SELECT Top 1 id from stuff_phone 
WHERE st.Id = stuff_phone.stuffId 
Order By stuff_phone.preference)
GROUP BY st.Id, st.name, ph.phone
READ ALSO
Ошибки в работе триггера MySQL [дубликат]

Ошибки в работе триггера MySQL [дубликат]

Данный вопрос уже был задан и имеет решение:

404
В боевом режиме виснет сортировка по дате

В боевом режиме виснет сортировка по дате

В локале полный дамп базы - все летаетНа хостинге висим в глушняк (504я)

302
Как скрыть консоль обновления mysql?

Как скрыть консоль обновления mysql?

Каждый день при обновлении mysql вижу как она при этом распахивает окно и запускает терминал с выводом своего логаПросмотрел её настройки ничего...

437