繁体   English   中英

MySQL JOIN查询-右表中的每一行,左表中的每一行,对包含的数据进行优先级排序

[英]MySQL JOIN Query - one row from right table for each row left table with prioritizing contained data

我有两张桌子,房屋和房客。 每个单位有多个房客。 我需要的是一个联合列表,其中最多包含两个承租人,具有完整数据集的优先行,然后是电话号码和最后一个电子邮件地址。

我还想避免使用临时表和子查询,因为有大量数据。 谢谢!

例:

桌屋

-------------
| 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@..|
-----------------------------------------------------------------

在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:

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的行感兴趣,因为您只需要name1和name2等。

但是此数据仍在单个列中:

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

为了将列变成一行,我们使用了透视操作。 执行此操作的标准方法是在CASE WHEN rn = 1 or 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丢弃了空值,因此Bill和Cloe被保留并变成一行。 RN已经完成工作并被丢弃:

FlatID, Name1,  Name2
1,      Bill,   Cloe

底部查询(mysql5.x)使用​​相同的技术,它仅使用变量来模仿row_number()


对于以后的问题,请确保您发布了MySQL版本,并尝试在db-fiddle.com上制作示例数据集(如我上面所做的)或类似内容-如果他们不这样做,它将吸引更多的人来帮助您必须弄乱创建表并向其加载数据以测试其理论

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM