左连接替代

时间:2017-10-06 09:54:42

标签: sql oracle

/*Tables:*/
SELECT tid, SID FROM t_s_map;/*student id whom teacher has access*/
SELECT sgid FROM sg;/* student_group table*/
SELECT sgid, SID FROM sgm;/*student group members table(info of students under student group)*/
/*Note: All columns are id and have values > 0*/

示例数据:

Table: sg
sgid
101
201

Table: sgm  
sgid    sid
101     1
101     2
101     3
201     1
201     2

Table: t_s_map  
tid sid
11  1
11  2
11  3
22  1
22  2

问题陈述:

获取教师可以访问的学生组列表(如果教师可以访问学生组下的所有学生,那么只有该学生组可以访问)。 因此,tid = 11可访问的学生组是sgid = 101和sgid = 201,而tid = 22则是sgid = 201。

SELECT sg.sgid
FROM   sg
       JOIN sgm
       ON (sg.sgid = sgm.sgid)
       LEFT OUTER JOIN t_s_map
       ON (sgm.SID       = t_s_map.SID)
WHERE  t_s_map.tid = 22
GROUP BY sg.sgid
HAVING MAX (NVL(t_s_map.SID, 0)) > 0 AND MIN (NVL(t_s_map.SID, 0)) > 0;

现在上面的查询工作正常,但速度很慢,有没有其他(更快)方法可以做到。

当前版本:

SELECT sgm.sgid
FROM   sgm           
       LEFT OUTER JOIN t_s_map
       ON (sgm.SID       = t_s_map.SID)
WHERE  t_s_map.tid = 22
GROUP BY sgm.sgid
HAVING MIN (NVL(t_s_map.SID, 0)) > 0;

4 个答案:

答案 0 :(得分:1)

您没有发布样本数据,所以我无法尝试。 你能看到这个查询吗?它应该等同于您在上面发布的那个。

由于您使用了WHERE t_s_map.id,因此LEFT JOIN相当于INNER JOIN。

此外,在我看来,你的HAVING条件不是必需的(SID应该总是有一个值> 0。我最后一点错了吗?)

SELECT DISTINCT sg.sgid
FROM sg
INNER JOIN sgm ON sg.sgid = sgm.sgid
INNER JOIN t_s_map ON sgm.SID= t_s_map.SID
WHERE t_s_map.tid = 1234;

<强>更新

关注“问题陈述”我认为你的(上面也是我的)是被问到的。 下一个查询可以。

SELECT TID, C.SGID
FROM t_s_map B
LEFT JOIN SGM C ON B.SID = C.SID
LEFT JOIN (SELECT sgid, COUNT(*) AS RC FROM SGM GROUP BY sgid) D ON C.SGID = D.SGID
GROUP BY TID, C.SGID, RC
HAVING COUNT(*) = RC;

第二次更新 我想您可以很容易地发现只获取SGID,您可以将上述查询修改为:

SELECT DISTINCT C.SGID
FROM t_s_map B
LEFT JOIN SGM C ON B.SID = C.SID
LEFT JOIN (SELECT sgid, COUNT(*) AS RC FROM SGM GROUP BY sgid) D ON C.SGID = D.SGID
GROUP BY TID, C.SGID, RC
HAVING COUNT(*) = RC;

答案 1 :(得分:1)

你没有做left join。您的where子句将其转换为inner join。所以,从:

开始
SELECT sg.sgid
FROM sg JOIN
     sgm
     ON sg.sgid = sgm.sgid JOIN
     t_s_map
     ON sgm.SID = t_s_map.SID
WHERE t_s_map.tid = 1234
GROUP BY sg.sgid
HAVING MAX(t_s_map.SID) > 0 AND MIN(COALESCE(t_s_map.SID, 0)) > 0;

接下来,不需要joinsg

SELECT sgm.sgid
FROM sgm JOIN
     t_s_map
     ON sgm.SID = t_s_map.SID
WHERE t_s_map.tid = 1234
GROUP BY sgm.sgid
HAVING MAX(t_s_map.SID) > 0 AND MIN(COALESCE(t_s_map.SID, 0)) > 0;

此查询可以利用t_s_map(tid, sid)sgm(sgid)上的索引。

答案 2 :(得分:0)

根据SQL查询的执行顺序,首先执行From和Joins,然后执行Where子句。尝试使用以下查询来减少过滤器的行数。你可以相应地添加你的小组并拥有条款。

SELECT sg.sgid
FROM sg
JOIN sgm
ON (sg.sgid = sgm.sgid)
LEFT OUTER JOIN (select SID from t_s_map
                 where t_s_map.tid = 1234) tmp
on  (sgm.SID = tmp.SID) 

答案 3 :(得分:-1)

您无需包含sg表格,WHEREHAVING条款有效地将LEFT OUTER JOIN转换为INNER JOIN,您不需要检查自MAX后的MAX >= MIN值以及MIN > 0然后MAX >= MIN > 0NVL0 > 0都不需要NULL > 0不是真的。

SELECT sgm.sgid
FROM   sgm
       INNER JOIN t_s_map
       ON ( sgm.SID = t_s_map.SID )
WHERE  t_s_map.tid = 1234
GROUP BY sgm.sgid
HAVING MIN(t_s_map.SID) > 0;