MySQL JOIN Query - одна строка из правой таблицы для каждой строки левой таблицы с приоритезацией содержащихся данных - PullRequest
0 голосов
/ 28 января 2019

У меня есть два стола, дом и арендатор.в каждой квартире несколько арендаторов.Что мне нужно, так это объединенный список, содержащий не более двух арендаторов, приоритетные строки с полным набором данных, затем номер телефона и последний адрес электронной почты.

Я также хотел бы избежать временных таблиц и подзапросов,так как есть большое количество данных.Спасибо!

Пример:

столовый домик

-------------
| id | flat|
-------------
| 1  | 011 |
| 2  | 012 |
| 3  | 111 |
-------------

арендатор стола

------------------------------
| fid | name | phone | mail   |
------------------------------
| 1   | pete | NULL  | NULL   |
| 1   | cloe | NULL  | cloe@..|
| 1   | bill | 555.. | bill@..|
| 2   | john | 555.. | john@..|
| 3   | paul | 555.. | NULL   |
| 3   | mary | NULL  | mary@..|
------------------------------

ожидаемый результат:

-----------------------------------------------------------------
| id | flat | name1 | phone1 | mail1  | name2 | phone2 | mail2  |
-------------
| 1  | 011  | bill  | 555..  | bill@..| cloe  | NULL   | cloe@..|
| 2  | 012  | john  | 555..  | john@..| NULL  | NULL   | NULL   |
| 3  | 111  | paul  | 555..  | NULL   | mary  | NULL   | mary@..|
-----------------------------------------------------------------

1 Ответ

0 голосов
/ 28 января 2019

В MySQL8:

SELECT 
  f.id,
  f.flat,
  MAX(CASE WHEN rr.rn = 1 THEN rr.`name` END) AS name1,
  MAX(CASE WHEN rr.rn = 1 THEN rr.phone END) AS phone1,
  MAX(CASE WHEN rr.rn = 1 THEN rr.mail END) AS email1,
  MAX(CASE WHEN rr.rn = 2 THEN rr.`name` END) AS name2,
  MAX(CASE WHEN rr.rn = 2 THEN rr.phone END) AS phone2,
  MAX(CASE WHEN rr.rn = 2 THEN rr.mail END) AS email2
FROM
house f
LEFT JOIN
(
 SELECT 
  r.*, 
  ROW_NUMBER() OVER(PARTITION BY r.fid ORDER BY 
    (CASE WHEN r.phone IS NOT NULL THEN -2 ELSE 0 END + CASE WHEN r.mail IS NOT NULL THEN -1 ELSE 0 END), r.fid
  ) rn
 FROM
  renter r
) rr
ON rr.fid = f.id and rr.rn <= 2
GROUP BY f.id, f.flat

В MySQL <8 вам придется подделывать ROW_NUMBER / PARTITION BY, используя эту недокументированную (может внезапно перестать работать) технику: </p>

SELECT 
  f.id,
  f.flat,
  MAX(CASE WHEN rr.rn = 1 THEN rr.`name` END) AS name1,
  MAX(CASE WHEN rr.rn = 1 THEN rr.phone END) AS phone1,
  MAX(CASE WHEN rr.rn = 1 THEN rr.mail END) AS email1,
  MAX(CASE WHEN rr.rn = 2 THEN rr.`name` END) AS name2,
  MAX(CASE WHEN rr.rn = 2 THEN rr.phone END) AS phone2,
  MAX(CASE WHEN rr.rn = 2 THEN rr.mail END) AS email2
FROM
house f
LEFT JOIN
(
 SELECT 
  r.*, 
  @rn:=CASE WHEN r.fid=@previd THEN @rn+1 ELSE 1 END as rn,
  @previd:=r.fid
 FROM
  (select @rn:=0,@previd:=-1) x,
  renter r
  ORDER BY r.fid, (CASE WHEN r.phone IS NOT NULL THEN -2 ELSE 0 END + CASE WHEN r.mail IS NOT NULL THEN -1 ELSE 0 END)
) rr
ON rr.fid = f.id and rr.rn <= 2
GROUP BY f.id, f.flat

https://www.db -fiddle.com / f / dYS68AFFGTxZxfia1UtJEK / 0

Как это работает:

В вашей таблице арендатора применяется номер строки, который считает строки в порядке приоритета,Если в строке есть телефон, он получает -2, если в строке есть электронное письмо, он получает -1.суммируя, они приходят к -3, если в строке есть и то, и другое.Когда упорядочено по возрастанию, это означает, что -3 имеет более высокий приоритет (это первая строка из сортировки), чем -2 или -1.Номер строки помещает число в строку, например 1,2,3 .., оно перезапускается каждый раз, когда меняется плоский номер идентификатора.

Мы берем наш расширенный набор данных и присоединяем его к квартирам, и мы говорим, что мы толькоинтересуется строками <= 2 в строке имени, потому что вам нужны только имя1, имя2 и т. д. </p>

Но эти данные все еще находятся в одном столбце:

FlatID, Name,  RN
1,      Bill,  1
1,      Cloe,  2

Чтобы превратить столбец в строкуМы используем операцию поворота.Стандартный способ сделать это - использовать CASE WHEN rn = 1 or 2 ...:

SELECT *, case when rn = 1 then name end as name, case when rn = 2 then name end

Производит

FlatID, Name1,  Name2,  RN
1,      Bill,   null,   1
1,      null,   Cloe,   2

Теперь мы используем MAX () для группировки в одну строку для FlatID, ипоскольку MAX сбрасывает нули, Билл и Кло остаются в одном ряду.RN выполнил свою работу и отбрасывается:

FlatID, Name1,  Name2
1,      Bill,   Cloe

Нижний запрос (mysql5.x) использует ту же технику, он просто использует переменные для имитации row_number ()


Длябудущие вопросы, пожалуйста, убедитесь, что вы опубликуете версию MySQL, а также попытаетесь создать примерный набор данных на db-fiddle.com (как я делал выше) или аналогичный - это привлечет больше людей, помогающих вам, если у них их нетвозиться с созданием таблиц и загрузкой их с данными, чтобы проверить их теории

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...