关于left join的一些事

先做个题吧

我们先建两个表吧。一个叫classes,一个叫students。

DROP TABLE IF EXISTS `classes`;
CREATE TABLE `classes`  (
  `id` int(11) NOT NULL,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

INSERT INTO `classes` VALUES (1, '一班');
INSERT INTO `classes` VALUES (2, '二班');
INSERT INTO `classes` VALUES (3, '三班');

CREATE TABLE `students`  (
  `id` int(11) NOT NULL,
  `class_id` int(11) NULL DEFAULT NULL,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `gender` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

INSERT INTO `students` VALUES (1, 1, '小明', 'M');
INSERT INTO `students` VALUES (2, 1, '小红', 'F');
INSERT INTO `students` VALUES (3, 1, '小军 ', 'M');
INSERT INTO `students` VALUES (4, 1, '小米', 'F');
INSERT INTO `students` VALUES (5, 2, '小白', 'F');
INSERT INTO `students` VALUES (6, 2, '小兵', 'M');
INSERT INTO `students` VALUES (7, 2, '小林', 'M');
INSERT INTO `students` VALUES (8, 3, '小新', 'F');
INSERT INTO `students` VALUES (9, 3, '小王', 'M');
INSERT INTO `students` VALUES (10, 3, '小丽', 'F');

现在有两个需求:

  1. 找出每个班级的名称及其对应的女同学数量
  2. 找出一班的同学总数

第一题正确答案是:

SELECT c.name, count(s.name) as num 
    FROM classes c left join students s 
    on s.class_id = c.id 
    and s.gender = 'F'
    group by c.name

第二题正确答案是:

SELECT c.name, count(s.name) as num 
    FROM classes c left join students s 
    on s.class_id = c.id 
    where c.name = '一班' 
    group by c.name

你写对了吗?我们注意到,第一个过滤条件是放在on后面,第二个是放在where后面,为什么呢?

left join的本质

left join 就是以左表为基准,遍历每一行,然后根据on后面的条件,去匹配右表的数据,有几行就加几行,一行都没有,就只放一个左表的数据,右表值为空。这样就生成了一张临时表数据,然后再通过where后面的条件来筛选临时表中的数据,最后返回。下面用伪代码表示一下

for (int i = 0 ; i < classes.size(); ++i){
    int b = false;
    for(int j= 0; j < students.size(); ++j){
        //处理on后面的条件
        if(classes[i].id = students.[j].id ){
            //往temp放一条数据
            temp.add();
            //处理过了,就后面就不放空数据了
            b = true;
        }
    }
    
    //如果一条数据都没匹配上,就放一条以左表为基准的空数据
    if(!b){
        temp.add();
    }
}

上面就是生成临时表,然后再根据where条件过滤数据。
由此我们可知,如果当左表数据的某个字段有严格限制时,就放在where后面,否则就放在on后面。比如说我们要查所有班级的女同学的数据,那么我们的重点是班级,女同学这个点是有可能为0的,所以放在on后面。

添加新评论