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'

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章