用于检查记录是否存在于/或表中的最佳单个查询

时间:2011-11-23 20:56:32

标签: mysql

这是一个奇怪的问题,所以问一个最好的方法就是举个例子。我有一份客户名单。我希望任何在CourseHistory表或Access表(或两者)中都有相应条目的客户。

我想要获取这些客户的最佳单个查询(无子查询)。我想出了

SELECT
   c.cusid
FROM
   Customers c
   CROSS JOIN Realms r
   LEFT JOIN Course.CourseHistory ch ON (c.cusid = ch.cusid)
   LEFT JOIN Access a ON (c.cusid = a.cusid AND r.realmid = a.realmid)
WHERE
   realmname = 'Course'
   AND COALESCE(chid, accid)

这有效,但速度明显很慢,可能是因为它必须对Customers进行全面扫描。由于CourseHistoryAccess可以为null且结果仍然有效,因此必须将它们保持连接状态。有没有更正确的方法来进行此查询?

4 个答案:

答案 0 :(得分:4)

摆脱那个CROSS JOIN到Realms和INNER JOIN将该表改为Access。

SELECT
   c.cusid
FROM
   Customers c
   LEFT JOIN Course.CourseHistory ch ON (c.cusid = ch.cusid)
   LEFT JOIN Access a 
       INNER JOIN realms r
           ON a.realmid = r.realmid
               AND r.realmname = 'Course'
       ON c.cusid = a.cusid
WHERE
   COALESCE(chid, accid)

答案 1 :(得分:3)

这是您的原始查询

SELECT  
   c.cusid  
FROM  
   Customers c  
   CROSS JOIN Realms r  
   LEFT JOIN Course.CourseHistory ch ON (c.cusid = ch.cusid)  
   LEFT JOIN Access a ON (c.cusid = a.cusid AND r.realmid = a.realmid)  
WHERE  
   realmname = 'Course'  
   AND COALESCE(chid, accid)  
; 

从你的评论中,我现在意识到这一点

  • Realm可以访问Access
  • 访问到达客户,但您不需要
  • 访问权限可以通过cusid
  • 到达Course.CourseHistory

鉴于此路径,这里是重构的查询

SELECT r.cusid
FROM
(SELECT realmid FROM Realms WHERE realmname = 'Course') r
LEFT JOIN
(SELECT realmid,cusid,accid FROM Access) a ON r.realmid=a.realmid
LEFT JOIN
(SELECT cusid FROM Course.CourseHistory) ch ON a.cusid=ch.cusid
WHERE COALESCE(chid, accid);

您需要以下索引

ALTER TABLE Realms ADD INDEX realmname_realmid_ndx (realmname,realmid);
ALTER TABLE Access ADD INDEX realmid_cusid_accid_ndx (realmid,cusid,accid);

试一试!!!

答案 2 :(得分:1)

您当前的查询以Customers x Realms的笛卡尔积开头,并且应该返回太多重复项。如果每个客户拥有多条记录,那么Course.CourseHistoryAccess上的联接也应该有很多重复项。

以下查询应该更有效。查询中的UNION应用隐式DISTINCT,即所有ID都是唯一的。此外,该查询允许MySQL优化器从表统计中受益,并以最佳顺序排列连接。

SELECT cuid
FROM Customers c
JOIN Course.CourseHistory ch USING (cuid)
UNION
SELECT cuid
FROM Realms r
JOIN Access a USING (realmid)
JOIN Customers c USING (cuid)
WHERE r.realmname = 'Course';

答案 3 :(得分:0)

您似乎想要从CourseHistory和Access(在“课程”领域内)收集所有客户ID,并检索与这些ID匹配的客户。这是一个完全相同的查询。

SELECT
    c.cusid
FROM
    Customers c
    INNER JOIN (
        SELECT ch.cusid FROM Course.CourseHistory AS ch
        UNION
        SELECT a.cusid FROM Access a
        INNER JOIN Realms r ON r.realmid = a.realmid AND r.realmname = 'Course'
    ) AS ids ON c.cusid = ids.cusid

如果您确实只需要客户ID,并且保证客户表包含所有现有客户ID,那么您可以省去外部选择并只使用内部UNION。

如果你真的需要一个查询,那么你将不得不接受更糟糕的事情。这至少是对原始查询的改进:

SELECT
    c.cusid
FROM
    Customers c
    LEFT JOIN Course.CourseHistory ch ON (c.cusid = ch.cusid)
    LEFT JOIN Access a ON (c.cusid = a.cusid)
    LEFT JOIN Realms r ON (r.realmid = a.realmid AND realmname = 'Course')
WHERE
    AND chid IS NOT NULL OR realmname IS NOT NULL