2016-08-24 122 views
0

我想在两列之一上连接两个表。我不知道哪一个会成为比赛。当从列表表中找到匹配行时,我需要来自feed_REIN的所有记录和附加数据。我认为一个左连接将工作。MySQL LEFT JOIN在两列之一上

当我只有一个条件(即RETS.list_number = listings.CVMLS)时,它运行正常。只要我在LEFT JOIN中添加额外的OR条件,就需要永久完成查询。

feed_RETS有大约125k条记录和清单约有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%' 
+0

没有解释计划,没有模式细节,没有统计数据,没有示例数据和输出:downvote。 – symcbean

回答

0

看解释计划来更好地了解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 
+0

感谢您的建议。我曾经考虑过这个问题,但希望能够用LEFT JOIN和OR子句完成我所需要的。显然我不了解效果。 – user1723974

0

试试这个:

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) 
0

如果您打算无论是cvmls或收服(异或)和应用程序可以确保要么可能是真实的,但不是两者,那么在逻辑上的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返回一个不同的计数,那么请注意列表中有多行记录被多次计数。如果你不想这样做,那么你需要一个不同的计数 - 或者可能是下面的一个改进。

如果查询是为了限制由这个连接所有标准返回的行,那么你就需要一个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%' 
; 

您的查询仍可能有两个原因(尽我所能来诊断基于一般假设)慢:

  1. JOIN标准OR强制执行全表扫描,因为优化器不知道使用哪个索引或是否使用任何索引。
  2. 通配符%在比赛串'%Sold%开始强制执行全表扫描,因为指数的正常类型由从分栏的内容内置左到右。这样想按字母顺序排列的名单索引:如果你在一个名称的开头匹配(“与‘乔’开头的名称”),你可以用你的有序列表快速找到匹配的名称;相反,如果你正在寻找某个名字中间的东西(“名字中带有”nat“),那么你的索引对你来说就没用了。

该查询实际上可能会更快:

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%'相反,查询可能会更快依然。

+0

'逻辑上总是相同的行数'你确定吗? – Strawberry

+0

http://sqlfiddle.com/#!9/40880/1 – Strawberry

+0

@Strawberry好点。让我换一个说法:“如果你打算使用cvmls OR rein(exclusive或),并且应用程序确保两者都是真的,但不是两者兼而有之,那么逻辑上它总是相同的行数。然后请考虑是否要COUNT(*)[所有可能的匹配,包括来自连接左侧的重复]或COUNT(DISTINCT r.list_number)[只有不同的列表]。“根据我对这类查询的经验,用例总是调用不同的行(即XOR)。补充逻辑! +1为你:-) –