MySQL LEFT JOIN 两列之一
MySQL LEFT JOIN on either of two columns
我想在任意两列之一上加入两个 table。我不知道哪一个会匹配。当从列表 table 中找到匹配行时,我想要来自 feed_REIN 的所有记录和附加数据。我认为 LEFT JOIN 会起作用。
当我只有一个条件(即 RETS.list_number = listings.CVMLS)时,它运行正常。一旦我在 LEFT JOIN 中添加了额外的 OR 条件,它就需要永远完成查询。
feed_RETS 有大约 125k 条记录,listings 有大约 12k 条记录。
我做错了什么?
SELECT
COUNT(*)
FROM feed_RETS AS RETS LEFT JOIN listings ON listings.statusID IN (1,2,3) AND (RETS.list_number = listings.CVMLS OR RETS.list_number = listings.REIN)
WHERE RETS.public_status NOT LIKE '%Sold%'
试试这个:
SELECT
COUNT(*)
FROM feed_RETS AS RETS
LEFT JOIN listings on
RETS.list_number = listings.CVMLS
OR RETS.list_number = listings.REIN
WHERE public_status NOT LIKE '%Sold%' and statusID IN (1,2,3)
查看解释计划以更好地理解带有 OR 子句的查询。此查询唯一可能的连接算法很可能是嵌套循环连接,这对您的表来说效率非常低。您可以将查询重写为:
SELECT
COUNT(*)
FROM(
(SELCT * FROM feed_RETS AS RETS LEFT JOIN listings ON statusID IN (1,2,3) AND RETS.list_number = listings.CVMLS WHERE public_status NOT LIKE '%Sold%')
UNION
(SELCT * FROM feed_RETS AS RETS LEFT JOIN listings ON statusID IN (1,2,3) AND RETS.list_number = listings.REIN WHERE public_status NOT LIKE '%Sold%'))T
如果您打算 EITHER cvmls OR rein(异或),并且应用程序确保其中一个可以为真但不能同时为真,那么逻辑上 LEFT JOIN
将是不必要的,并且查询将始终产生相同的结果行数。但是,如果两者都可以在同一行上匹配,那么请考虑您是想要 COUNT(*) [所有可能的匹配项,包括连接左侧的重复项] 还是 COUNT(DISTINCT r.list_number) [仅不同的列表]:
-- Query 1
SELECT COUNT(*)
FROM feed_RETS AS RETS LEFT JOIN listings
ON listings.statusID IN (1,2,3)
AND ( RETS.list_number = listings.CVMLS
OR RETS.list_number = listings.REIN
)
WHERE RETS.public_status NOT LIKE '%Sold%'
;
-- Query 2 - Is the count the same?
SELECT COUNT(*)
FROM feed_RETS
WHERE public_status NOT LIKE '%Sold%'
;
如果查询 2 returns 的计数不同,请注意列表 table 中的某些行被计算了多次。如果您不想这样,那么您需要一个不同的计数——或者可能是以下改进之一。
如果查询旨在限制此连接的所有条件返回的行,那么您需要一个 INNER JOIN
(为了清楚起见,还可以移动 ON
条件进入 WHERE
子句):
SELECT COUNT(*)
FROM feed_RETS AS RETS INNER JOIN listings
ON ( RETS.list_number = listings.CVMLS
OR RETS.list_number = listings.REIN
)
WHERE listings.statusID IN (1,2,3)
AND RETS.public_status NOT LIKE '%Sold%'
;
您的查询可能仍然很慢,原因有两个(尽我所能根据一般假设进行诊断):
JOIN
条件中的 OR
强制进行完整的 table 扫描,因为优化器根本不知道要使用哪个索引或是否使用任何索引。
- 匹配字符串
'%Sold%
开头的通配符%
强制进行全table扫描,因为正常类型的索引是通过将列内容从左开始划分来构建的-向右。将索引想象成按字母顺序排列的姓名列表:如果您在姓名的开头 ("names beginning with 'Jo'") 进行匹配,则可以使用有序列表快速找到匹配的姓名;相比之下,如果您要查找名称中间的内容 ("names with 'nat' in them"),那么您的索引对您毫无用处。
这个查询实际上可能更快:
SELECT SUM(CASE
WHEN l_cvmls.cvmls IS NOT NULL OR l_rein.REIN IS NOT NULL
THEN 1
ELSE 0
END
) listing_count
FROM ( feed_RETS AS r LEFT JOIN listings l_cvmls
ON l_cvmls.statusID IN (1,2,3)
AND r.list_number = l_cvmls.CVMLS
) LEFT JOIN listings l_rein ON l_rein.statusID IN (1,2,3)
AND r.list_number = l_rein.REIN
WHERE r.public_status NOT LIKE '%Sold%'
;
如果您可以避免 '%Sold%'
并改用 'Sold%'
,查询可能会更快。
我想在任意两列之一上加入两个 table。我不知道哪一个会匹配。当从列表 table 中找到匹配行时,我想要来自 feed_REIN 的所有记录和附加数据。我认为 LEFT JOIN 会起作用。
当我只有一个条件(即 RETS.list_number = listings.CVMLS)时,它运行正常。一旦我在 LEFT JOIN 中添加了额外的 OR 条件,它就需要永远完成查询。
feed_RETS 有大约 125k 条记录,listings 有大约 12k 条记录。
我做错了什么?
SELECT
COUNT(*)
FROM feed_RETS AS RETS LEFT JOIN listings ON listings.statusID IN (1,2,3) AND (RETS.list_number = listings.CVMLS OR RETS.list_number = listings.REIN)
WHERE RETS.public_status NOT LIKE '%Sold%'
试试这个:
SELECT
COUNT(*)
FROM feed_RETS AS RETS
LEFT JOIN listings on
RETS.list_number = listings.CVMLS
OR RETS.list_number = listings.REIN
WHERE public_status NOT LIKE '%Sold%' and statusID IN (1,2,3)
查看解释计划以更好地理解带有 OR 子句的查询。此查询唯一可能的连接算法很可能是嵌套循环连接,这对您的表来说效率非常低。您可以将查询重写为:
SELECT
COUNT(*)
FROM(
(SELCT * FROM feed_RETS AS RETS LEFT JOIN listings ON statusID IN (1,2,3) AND RETS.list_number = listings.CVMLS WHERE public_status NOT LIKE '%Sold%')
UNION
(SELCT * FROM feed_RETS AS RETS LEFT JOIN listings ON statusID IN (1,2,3) AND RETS.list_number = listings.REIN WHERE public_status NOT LIKE '%Sold%'))T
如果您打算 EITHER cvmls OR rein(异或),并且应用程序确保其中一个可以为真但不能同时为真,那么逻辑上 LEFT JOIN
将是不必要的,并且查询将始终产生相同的结果行数。但是,如果两者都可以在同一行上匹配,那么请考虑您是想要 COUNT(*) [所有可能的匹配项,包括连接左侧的重复项] 还是 COUNT(DISTINCT r.list_number) [仅不同的列表]:
-- Query 1
SELECT COUNT(*)
FROM feed_RETS AS RETS LEFT JOIN listings
ON listings.statusID IN (1,2,3)
AND ( RETS.list_number = listings.CVMLS
OR RETS.list_number = listings.REIN
)
WHERE RETS.public_status NOT LIKE '%Sold%'
;
-- Query 2 - Is the count the same?
SELECT COUNT(*)
FROM feed_RETS
WHERE public_status NOT LIKE '%Sold%'
;
如果查询 2 returns 的计数不同,请注意列表 table 中的某些行被计算了多次。如果您不想这样,那么您需要一个不同的计数——或者可能是以下改进之一。
如果查询旨在限制此连接的所有条件返回的行,那么您需要一个 INNER JOIN
(为了清楚起见,还可以移动 ON
条件进入 WHERE
子句):
SELECT COUNT(*)
FROM feed_RETS AS RETS INNER JOIN listings
ON ( RETS.list_number = listings.CVMLS
OR RETS.list_number = listings.REIN
)
WHERE listings.statusID IN (1,2,3)
AND RETS.public_status NOT LIKE '%Sold%'
;
您的查询可能仍然很慢,原因有两个(尽我所能根据一般假设进行诊断):
JOIN
条件中的OR
强制进行完整的 table 扫描,因为优化器根本不知道要使用哪个索引或是否使用任何索引。- 匹配字符串
'%Sold%
开头的通配符%
强制进行全table扫描,因为正常类型的索引是通过将列内容从左开始划分来构建的-向右。将索引想象成按字母顺序排列的姓名列表:如果您在姓名的开头 ("names beginning with 'Jo'") 进行匹配,则可以使用有序列表快速找到匹配的姓名;相比之下,如果您要查找名称中间的内容 ("names with 'nat' in them"),那么您的索引对您毫无用处。
这个查询实际上可能更快:
SELECT SUM(CASE
WHEN l_cvmls.cvmls IS NOT NULL OR l_rein.REIN IS NOT NULL
THEN 1
ELSE 0
END
) listing_count
FROM ( feed_RETS AS r LEFT JOIN listings l_cvmls
ON l_cvmls.statusID IN (1,2,3)
AND r.list_number = l_cvmls.CVMLS
) LEFT JOIN listings l_rein ON l_rein.statusID IN (1,2,3)
AND r.list_number = l_rein.REIN
WHERE r.public_status NOT LIKE '%Sold%'
;
如果您可以避免 '%Sold%'
并改用 'Sold%'
,查询可能会更快。