MySql中的各种表的连接以及子查询、复杂查询

110 阅读8分钟

交叉连接

select * from emp,dept;

这是一个SQL查询语句,用于从两个数据库表中获取数据。假设两个表,一个是"emp"表,另一个是"dept"表,它们存储有关员工和部门的信息。这条查询语句的目的是从这两个表中检索所有的列和行,并将它们合并在一起。

这种写法只是一种简单的交叉连接(Cartesian Product)方法,会将"emp"表中的每一行与"dept"表中的每一行进行组合,得到一个巨大的结果集。通常情况下,应该在查询中指定如何连接这两个表,例如使用关联条件(JOIN),以便从相关的行中获取有意义的数据。否则,交叉连接会导致大量冗余的数据,并且可能不会提供你想要的结果。根据实际情况编写适当的连接条件和查询更好。

内连接

select * from emp e inner join dept d on e.dept_id = d.id;

用于从两个数据库表中获取数据并进行连接。这种写法使用了内连接(INNER JOIN)来连接"emp"表和"dept"表,并根据指定的关联条件将它们关联起来。

具体来说,这个查询会从"emp"表(用别名 "e" 表示)和 "dept"表(用别名 "d" 表示)中检索数据。通过使用关联条件 "e.dept_id = d.id",查询将会找到满足这个条件的行,并将两个表中的数据进行匹配。

这个查询将返回"emp"表中的每一行与"dept"表中的每一行匹配的结果。每一行将包含来自两个表的所有列的数据,形成一个合并后的结果集。这样你就可以获取包含员工和部门信息的联合数据,而不是简单的交叉连接。

需要注意的是,内连接(INNER JOIN)只会返回满足连接条件的行,如果没有匹配的行,则这些行将不会在结果中出现。这有助于排除不相关的数据,只返回有意义的关联数据

左外连接

select * from emp e left join dept d on e.dept_id = d.id;

SQL查询语句使用了左连接(LEFT JOIN)来连接"emp"表和"dept"表,并根据指定的关联条件将它们关联起来

具体来说,左连接会从"emp"表(用别名 "e" 表示)中的每一行开始,然后尝试在"dept"表(用别名 "d" 表示)中查找与之匹配的行,使用关联条件 "e.dept_id = d.id"。如果在"dept"表中找到了匹配的行,那么查询结果中将会包括这两个表中的数据合并在一起。如果没有找到匹配的行,对应的"emp"表中的行仍然会出现在结果中,但是"dept"表中的列将会显示为 NULL 值。

换句话说,左连接会保留左侧表(这里是"emp"表)的所有行,而无论是否在右侧表(这里是"dept"表)中找到匹配的行。这样做可以确保即使某些员工没有分配到部门,他们的信息仍然会在结果中显示,只是部门相关的列将会显示为 NULL。

这种连接类型常用于想要获取所有左侧表的数据,并且可能附带一些相关联数据的情况,即使在关联条件中没有找到匹配的情况下也是如此。

右外连接

select * from emp e right join dept d on e.dept_id = d.id;

这个SQL查询语句,使用了右连接(RIGHT JOIN)来连接"emp"表和"dept"表,并根据指定的关联条件将它们关联起来。

具体来说,右连接会从"dept"表(用别名 "d" 表示)中的每一行开始,然后尝试在"emp"表(用别名 "e" 表示)中查找与之匹配的行,使用关联条件 "e.dept_id = d.id"。如果在"emp"表中找到了匹配的行,那么查询结果中将会包括这两个表中的数据合并在一起。如果没有找到匹配的行,对应的"dept"表中的行仍然会出现在结果中,但是"emp"表中的列将会显示为 NULL 值。

换句话说,右连接会保留右侧表(这里是"dept"表)的所有行,而无论是否在左侧表(这里是"emp"表)中找到匹配的行。这样做可以确保即使某些部门没有对应的员工信息,部门的信息仍然会在结果中显示,只是员工相关的列将会显示为 NULL。

需要注意的是,相对于常见的左连接和内连接,右连接在实际使用中较少见。因为在绝大多数情况下,更关心保留左侧表的数据,而右连接则保留右侧表的数据。

子查询

子查询(Subquery)是嵌套在主查询中的查询语句。它是一个在其他查询语句内部的查询,用于从数据库中检索更具体的数据,或者根据外部查询的结果来限制内部查询的数据。

子查询可以出现在SQL语句的不同部分,例如:

  1. WHERE 子句中的子查询: 子查询可以用作主查询的条件,以限制从表中检索的数据。例如,你可以使用子查询来筛选出满足特定条件的行。

  2. FROM 子句中的子查询: 子查询可以用作主查询的一个数据源。这种情况下,子查询会在主查询之前运行,并将结果作为一个临时表供主查询使用。

  3. SELECT 子句中的子查询: 子查询可以用作主查询中的一部分,以获取更具体的数据,例如计算一个列的值。

  4. HAVING 子句中的子查询: 类似于WHERE子查询,子查询也可以用于HAVING子句,用于在分组查询中过滤结果。

子查询在SQL中的语法可以因数据库系统而异,但通常遵循一般的结构。以下是一个示例子查询的语法:

SELECT column(s)
FROM table
WHERE column OPERATOR (SELECT column(s) FROM another_table WHERE condition);

这里的子查询在主查询中用作条件,或者在主查询中获取数据。主查询将根据子查询的结果来进行过滤、限制或补充数据。

使用子查询可以让查询更灵活和精确,但过多或不适当使用子查询可能会影响查询性能。因此,在使用子查询时,需要根据具体情况进行权衡和优化。

个别示例代码

-- 1: 查询工资小于平均工资的员工有哪些?(子查询结果为一个值  标量子查询)
select * from emp where salary < (select avg(salary) from emp);

-- 2: 查询工资大于5000的员工,所在部门的名字 (子查询结果为多个值  列子查询)
select name from dept where id in(select distinct dept_id from emp where salary > 5000);

-- 3: 查询出2011年以后入职的员工信息,包括部门信息 (子查询结果为一张表   表子查询)
select * from (select * from emp where join_date >= '2011-01-01') as l
left join dept as d on l.dept_id = d.id;

复杂查询

复杂查询(Complex Query)是指包含多个关联的表、多个子查询、多个条件以及其他复杂条件的SQL查询语句。这些查询通常需要在一个查询中结合多个条件、连接多个表,以便从数据库中提取更特定、更有价值的数据。

复杂查询可以涉及以下一些方面:

  1. 多表连接: 复杂查询可能需要连接多个表,以便从不同的表中检索相关联的数据。

  2. 子查询的嵌套: 复杂查询中可能会嵌套使用多个子查询,每个子查询用于获取更具体的数据或进一步限制结果。

  3. 多个条件: 查询可能会涉及多个筛选条件、排序规则和聚合操作,以获得所需的结果。

  4. 表达式和函数: 复杂查询可能会包含各种SQL表达式、聚合函数、条件函数等,以对数据进行转换和计算。

  5. 分组和聚合: 复杂查询可能需要使用GROUP BY子句和聚合函数(如SUM、AVG、COUNT等)来对数据进行分组和汇总。

  6. 子查询的使用: 子查询可能用于在主查询中提供更具体的条件、计算或数据源。

  7. UNION 和 UNION ALL: 复杂查询可能需要使用UNION或UNION ALL操作符,将多个查询的结果合并。

复杂查询通常涉及多个查询语句的组合,以获得所需的结果。这些查询可以非常有用,因为它们允许您从大量数据中提取特定的、有关联的信息。然而,由于复杂查询涉及多个组件和条件,因此也可能更难编写和调试,同时可能影响查询性能。

在编写复杂查询时,建议将查询分解为更小的部分,逐步构建和测试每个部分,以确保查询的准确性和性能。使用注释来解释查询的不同部分也是一种良好的实践,有助于其他人理解查询的意图和逻辑。

# 菜品表:dish、分类表:category、套餐表:setmeal

-- 1. 查询价格低于 10元 的菜品的名称 、价格 及其 菜品的分类名称
select d.name,d.price,c.name from dish d
inner join category c on d.category_id = c.id where d.price <10;

-- 2. 查询所有价格在 10元(含)到50元(含)之间 且 状态为'起售'的菜品名称、价格 及其 菜品的分类名称
# (即使菜品没有分类, 也需要将菜品查询出来).
select d.name,d.price,c.name from dish d left join category c on d.category_id = c.id
where d.price between 10 and 50 and d.status = 1;

-- 3. 查询出 "商务套餐A" 中包含了哪些菜品 (展示出套餐名称、价格, 包含的菜品名称、价格、份数)
# 三表关联,重复使用join
select s.name,s.price,d.name,d.price,sd.copies
from setmeal s
join setmeal_dish sd on s.id = sd.setmeal_id
join dish d on d.id = sd.dish_id
where s.name = '商务套餐A';

-- 4. 查询出低于菜品平均价格的菜品信息 (展示出菜品名称、菜品价格)
select name,price from dish where price < (select avg(price) from dish) order by price;

-- 5. 查询每个分类下最贵的菜品, 展示出分类的名称、最贵的菜品的价格
select c.name,max(d.price)
from dish d
join category c on d.category_id = c.id
group by c.name;

-- 6. 查询各个分类下状态为'起售' , 并且该分类下 菜品总数量大于等于3的分类名称
select c.name,count(*) count
from category c
join dish d on c.id = d.category_id
where c.status = 1
group by c.name
having count >=3;