I have two tables, house and renter. in each flat there are multiple renter. What I need is a joined list, containing at most two renter, prioritized rows with a full data set, then phone number and last e-mail address.
I'd also like to avoid temporary tables and subqueries, as there is a large amount of data. Thanks!
Example:
table house
-------------
| id | flat|
-------------
| 1 | 011 |
| 2 | 012 |
| 3 | 111 |
-------------
table renter
------------------------------
| 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@..|
------------------------------
expected output:
-----------------------------------------------------------------
| 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@..|
-----------------------------------------------------------------
In 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
In MySQL < 8 you'll have to fake ROW_NUMBER/PARTITION BY using this undocumented (might suddenly stop working) technique:
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
Your renter table has a row number applied, that counts the rows in priority order. If a row has a phone, it scores -2, if a row has an email it scores -1. added together these come to -3 if a row has both. When ordered ascending, it means that -3 has higher priority (is the first row out of the sort) than a -2 or -1. Row number puts a number on the row like 1,2,3.. it restarts everytime the flat id number changes.
We take our augmented data set and join it onto the flats, abd we say we are only interested in rows <=2 on the rownumber because you only want name1 and name2 etc.
But this data is still in a single column:
FlatID, Name, RN
1, Bill, 1
1, Cloe, 2
To turn the column into a row, we use a pivoting operation. The standard way to do this is to use CASE WHEN rn = 1 or 2 ...
:
SELECT *, case when rn = 1 then name end as name, case when rn = 2 then name end
Produces
FlatID, Name1, Name2, RN
1, Bill, null, 1
1, null, Cloe, 2
Now we use MAX() to group up onto a single row for the FlatID, and because MAX discards nulls, the Bill and Cloe are perserved and become one row. RN has done its job and is discarded:
FlatID, Name1, Name2
1, Bill, Cloe
The bottom query (mysql5.x) uses the same technique, it just uses variables to imitate row_number()
For future questions, please ensure you post the MySQL version and also attempt to make an example set of data on db-fiddle.com (like i did above) or similar - it will get more people interested in helping you if they don't have to mess around creating tables and loading them with data to test their theories
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.