爲了更好地解釋着問題,如果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'