Mysql JOIN连接终极宝典

为了更好地解释着问题,如果A JOIN B,我们不妨称作:A表为连接表(也称左表),B表为被连接表(也称右表)。

总结一:

返回数据结果,永远是连接表A在左边,被连接表B在右边。

总结二:

       left   join (左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录 
  right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录
  inner join(等值连接) 只返回两个表中联结字段相等的行,即返回既在左表,又在右表中的记录。

总结三:

自连接表的查询,要注意:需要将关键条件放在左表中,连接的时候是使用左表去找右表。

(1)例如下面例子中, machine会在不同时间下产生多条记录,如何查询最新的记录呢?使用自连接。

由于CodeIgniter在左表不支持经过查询构造器get_compiled_select的子查询,而要使用sql的子查询。

public function get_readers_via_uln_room_machine_range($uln, $room_id, $machine_min, $machine_max)
{
 $sql = "(SELECT `id`, `ULN`, `LocationID`, `SerialNumber`, `LabelID`, MAX(SeverTime) as max_time
     FROM `n01`
     WHERE `uln` = '$uln'
     AND `LocationID` = '$room_id'
     GROUP BY `SerialNumber`
     ORDER BY `SeverTime` DESC) as `n01`";

 $range  = array(intval($machine_min), intval($machine_max));
 $n01_sql = "select * from `n01` where cast(LabelID AS SIGNED)>={$range[0]} AND cast(LabelID AS SIGNED)<={$range[1]}";

 $this->db->join('('.$n01_sql.') a','a.SerialNumber=n01.SerialNumber AND a.ULN=n01.ULN AND a.LocationID=n01.LocationID AND n01.max_time=a.SeverTime','inner');
 $this->db->order_by('(n01.LabelID+0)', 'ASC');
 $query = $this->db->get($sql);

 return $query->result();
}

解析一下:
对于左表,查询在当前uln和room下的不同机器SerialNumber码的最大SeverTime的记录;

对于右表,查询在$machine_min, $machine_max范围内的记录;

查询结果,用左表关联右表,关联关系为:

a.SerialNumber=n01.SerialNumber AND a.ULN=n01.ULN AND a.LocationID=n01.LocationID AND n01.max_time=a.SeverTime;

由于左表中的只有max_time是正确的数据信息,而SerialNumber是独一无二的,分组后也是正确的数据信息,而其它uln和LocationID是输入信息,所以他们也是正确数据信息,用他们去关联右边,就可以得到正确的所需要的信息。

----------------------------------------------------------------------------------------------------------------------------------------------

(2)还有一种折中方法,既可以使用CodeIgniter的查询构造器,又可以得到正确的数据。在下面的方法中,左表是正确数据,右表仅查询出和左表肯定相同的字段,所以右表不会查询出错误信息而覆盖左表,所有结果也是正确的。代码如下:

public function get_readers_via_uln_room_machine_range($uln, $room_id, $machine_min, $machine_max)
{
 $sql = $this->db
	->select("ULN, LocationID, SerialNumber, MAX(SeverTime) as max_time")
	->where("uln", $uln)
	->where("LocationID", $room_id)
    ->group_by("SerialNumber")
    ->order_by("SeverTime DESC")
	->get_compiled_select("n01");

 $range  = array(intval($machine_min), intval($machine_max));
 $n01_sql = "(select * from `n01` where cast(LabelID AS SIGNED)>={$range[0]} AND cast(LabelID AS SIGNED)<={$range[1]}) AS n01";

 $this->db->join('('.$sql.') a','a.SerialNumber=n01.SerialNumber AND a.ULN=n01.ULN AND a.LocationID=n01.LocationID AND a.max_time=n01.SeverTime','inner');
 $this->db->order_by('(n01.LabelID+0)', 'ASC');
 $query = $this->db->get($n01_sql);

 return $query->result();
}

SQL语句(无条件的)

SELECT *
FROM `n01`
INNER JOIN (
	SELECT ULN, LocationID, SerialNumber, MAX(SeverTime) as max_time FROM `n01` GROUP BY `SerialNumber` ORDER BY `SeverTime` DESC
) `a` ON a.SerialNumber=n01.SerialNumber AND a.ULN=n01.ULN AND a.LocationID=n01.LocationID AND a.max_time=n01.SeverTime
ORDER BY (n01.LabelID+0) DESC

 

为什么查询条件要分别使用在左右表中呢?其实LabelID范围的查询,也可以和uln、LocationID查询条件放在一起,结果都是正确的。

(3)最后,发现一种解决“MySQL不能分组内排序”的方法,具体可参考:https://www.cnblogs.com/userzf/p/10875470.html

SELECT * 
FROM (
	SELECT * FROM `n01` ORDER BY SeverTime DESC LIMIT 999999999
) `n01`
GROUP BY CoplSerialNumber

-- Added the sql statement 'LIMIT 999999999'

SELECT * 
FROM (
	SELECT * FROM `n01` GROUP BY SerialNumber, SeverTime ORDER BY SeverTime DESC
) `n01`
GROUP BY SerialNumber
ORDER BY (LabelID+0) DESC

-- Added the sql statement 'GROUP BY SerialNumber, SeverTime'

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值