MySQL - 多表连接查询

连接查询:将多张表(>=2)进行 记录的连接(按照某个指定的条件进行数据拼接)。

连接查询的意义:在用户查看数据的时候,数据往往来源于多张表。

连接查询的真谛:根据从左表拿出一条记录到右表匹配根据条件进行过滤。多表如是。


【1】连接查询的分类

SQL 中将连接查询分为四类:内连接,外连接,自然连接和交叉连接。其中外连接又分为左外连接和右外连接。


【2】交叉连接

交叉连接:cross join。

从一张表中循环取出每一条记录,每条记录都去另外一张表进行匹配。匹配一定保留(没有条件匹配),而连接本身字段就会增加(保留),最终形成的结果叫做:笛卡尔积。

  • 笛卡尔积没有意义,交叉连接只是保证连接这种结构的完整性。

语法如下:

左表 cross join 右表 === from 左表,右表。

示例如下:

-- 被称为显示交叉连接
select * from c_user cross join p_user;

-- 等价于如下,又被称为隐式交叉连接
select * from c_user,p_user;

【3】内连接

内连接:[inner]join ;

从左表中取出每一条记录,去右表中与所有的记录进行匹配。匹配必须是某个条件在左表中与右表中都相同最终才会保留结果,否则不保留。

  • 要求连接两边记录都存在,null将会被过滤掉 ;
  • 内连接可以没有on条件,这时候系统会保留所有结果(笛卡尔积) ;
  • 内连接还可以使用where代替on关键字,但是没有on效率高。

语法如下:

左表 [inner] join 右表 on 左表.字段 = 右表.字段;
on表示连接条件,条件字段就是代表相同的业务含义。

示例如下:

select c.name,p.age from c_user c INNER JOIN p_user p on c.id = p.id;

-- 等价于如下
select c.name,p.age from c_user c ,p_user p where c.id = p.id;

可以尝试使用表别名和字段别名简洁查询结果。


【4】外连接

外连接:outer join。

以某张表为主,取出里面的所有记录,然后每条与另外一张表进行连接。不管能不能匹配上条件,最终都会保留:能匹配,正确保留;不能匹配,其他表的字段都置空NULL。

外连接分为两种:以某张表为主–有主表 :

left join(left outer join) : 左外连接(左连接),以左表为主表;
right join(right outer join) : 右外连接(右连接),以右表为主表。

语法如下:

左表 left/right join 右表 on 左表.字段 = 右表.字段;
on表示连接条件,条件字段就是代表相同的业务含义。

示例如下:

① left [outer] join

select c.id ,c.name,p.age from c_user c LEFT OUTER JOIN p_user p on c.id = p.id;

MySQL - 多表连接查询_第1张图片

以左表为主,左表所有字段都会列出,右表无的将会置为null。最终的记录数至少不少于左表的记录数。


② right [outer] join

select c.id ,c.name,p.age from c_user c right OUTER JOIN p_user p on c.id = p.id;

MySQL - 多表连接查询_第2张图片

以右表为主,右表所有字段都会列出,左表无的将会置为null。最终的记录数至少不少于右表的记录数。


【5】自然连接

自然连接:natural join。

自然连接:就是自动匹配条件。系统以字段名字作为匹配模式(同名字段就作为条件,如果有多个同名字段,都作为条件)。

SQL会检查两个表中是否相同名称的列,且假设他们在连接条件中使用,并且在连接条件中仅包含一个连接列。不允许使用ON语句。

自然连接可分为自然内连接和自然外连接。

自然内连接:左表 natural join 右表。

select * from s_user natural join p_user ;

自然外连接:左表 natural left/right join 右表。

select * from s_user natural left join p_user ;

其实,内连接和外连接都可以模拟自然连接。交叉连接不可以使用自然连接。

左表 left/right/inner 右表 using(字段名);– 使用同名字段作为条件,自动合并条件。

select * from p_user left join p_user_2 USING(name,age) ;

MySQL - 多表连接查询_第3张图片


【6】全连接

全连接:full join;

MySQL不支持全连接,Oracle支持。

语法如下:

左表 full[outer] join 右表 on 左表.字段 = 右表.字段

示例如下:

select c.id ,c.name,p.age from c_user c full  JOIN p_user p on c.id = p.id;

MySQL中可以使用下面语句进行等价:

select c.id ,c.name,p.age from c_user c right OUTER JOIN p_user p on c.id = p.id

union 

select c.id ,c.name,p.age from c_user c left OUTER JOIN p_user p on c.id = p.id;

【7】left join on where

这里需要说明一点 :

  • 如果需要左表展示所有数据,那么条件放在on后面;

  • 如果条件放在了 where后面,那么和inner join 效果一样。

  • 不管on上的条件是否为真都会返回left或right表中的记录,full则具有left和right的特性的并集。 而inner jion没这个特殊性,则条件放在on中和where中,返回的结果集是相同的。


在使用left join时,on和where条件的区别如下:

1、 on条件是在生成临时表时使用的条件,它不管on中的条件是否为真,都会返回左边表中的记录。

2、where条件是在临时表生成好后,再对临时表进行过滤的条件。这时已经没有left join的含义(必须返回左边表的记录)了,条件不为真的就全部过滤掉。

【示例一】展示所有数据:

MySQL - 多表连接查询_第4张图片


【示例二】展示有效数据:

SELECT
    user_id,
    login_code,
    real_name,
    nick_name,
    DATE_FORMAT(
        create_time,
        '%Y-%m-%d %H:%i:%S'
    ) AS create_time,
    GROUP_CONCAT(power_code) AS 'power_code'
FROM
    (
        SELECT
            p.id AS power_id,
            p.power_code,
            p.power_type,
            su.id AS user_id,
            su.login_code,
            su.nick_name,
            su.real_name,
            DATE_FORMAT(
                su.create_time,
                '%Y-%m-%d %H:%i:%S'
            ) AS create_time
        FROM
            sys_user su
        LEFT JOIN sys_user_role ru ON su.id = ru.user_id
        LEFT JOIN sys_role_power rp ON rp.role_id = ru.role_id
        LEFT JOIN sys_power p ON p.id = rp.power_id
        WHERE
            ru.if_del = 0
        AND rp.if_del = 0
        AND p.if_del = 0
        UNION ALL
            SELECT
                p.id,
                p.power_code,
                p.power_type,
                su.id AS user_id,
                su.login_code,
                su.nick_name,
                su.real_name,
                DATE_FORMAT(
                    su.create_time,
                    '%Y-%m-%d %H:%i:%S'
                ) AS create_time
            FROM
                sys_user su
            LEFT JOIN sys_user_power pu ON pu.user_id = su.id
            LEFT JOIN sys_power p ON p.id = pu.power_id
            WHERE
                pu.if_del = 0
            AND p.if_del = 0
    ) t
WHERE
    1 = 1
GROUP BY
    user_id

MySQL - 多表连接查询_第5张图片


【8】on、where和having条件字句

on、where、having这三个都可以加条件的子句中,on是最先执行,where次之,having最后。有时候如果这先后顺序不影响中间结果的话,那最终结果是相同的。但因为on是先把不符合条件的记录过滤后才进行统计,它就可以减少中间运算要处理的数据,按理说应该速度是最快的。

根据上面的分析,可以知道where也应该比having快点的,因为它过滤数据后才进行sum,所以having是最慢的。但也不是说having没用,因为有时在步骤3还没出来都不知道那个记录才符合要求时,就要用having了。

在两个表联接时才用on的,所以在一个表的时候,就剩下where跟having比较了。在这单表查询统计的情况下,如果要过滤的条件没有涉及到要计算字段,那它们的结果是一样的,只是where可以使用rushmore技术,而having就不能,在速度上后者要慢。

如果要涉及到计算的字段,就表示在没计算之前,这个字段的值是不确定的,根据上篇写的工作流程,where的作用时间是在计算之前就完成的,而having就是在计算后才起作用的,所以在这种情况下,两者的结果会不同。

在多表联接查询时,on比where更早起作用。系统首先根据各个表之间的联接条件,把多个表合成一个临时表后,再由where进行过滤,然后再计算,计算完后再由having进行过滤。由此可见,要想过滤条件起到正确的作用,首先要明白这个条件应该在什么时候起作用,然后再决定放在那里

你可能感兴趣的:(MySQL)