在SQL查询中,关键字SELECT, FROM, WHERE, GROUP BY, HAVING, 和 ORDER BY的组合使用定义了数据的筛选、分组、排序等操作。理解这些关键字的执行顺序对于构建有效且高效的查询至关重要。以下是这些关键字的执行顺序详解及案例说明:
1.笛卡尔积的定义
从数学层面来看,两个集合 A 和 B 的笛卡尔积(记作 A × B),是由所有可能的有序对 (a, b) 构成的集合,其中 a 属于集合 A,b 属于集合 B。
举个例子,假设有集合 A = {1, 2} 和集合 B = {3, 4},那么它们的笛卡尔积 A × B 就是 {(1, 3), (1, 4), (2, 3), (2, 4)}。
2.SQL 中的笛卡尔积
在 SQL 中,当你把多个表进行 JOIN 操作时,数据库系统一开始会算出这些表的笛卡尔积。也就是把第一个表的每一行和第二个表的每一行组合起来,从而形成一个新的结果集。
下面通过一个简单的 SQL 示例来理解:
-- 创建表A
CREATE TABLE table_a (
id INT,
name VARCHAR(50)
);
-- 插入数据到表A
INSERT INTO table_a (id, name) VALUES
(1, 'Alice'),
(2, 'Bob');
-- 创建表B
CREATE TABLE table_b (
id INT,
city VARCHAR(50)
);
-- 插入数据到表B
INSERT INTO table_b (id, city) VALUES
(1, 'New York'),
(2, 'Los Angeles');
-- 执行 CROSS JOIN 操作(CROSS JOIN 会直接返回两个表的笛卡尔积)
SELECT * FROM table_a CROSS JOIN table_b;
在这个例子里,table_a 有 2 行数据,table_b 也有 2 行数据。执行 CROSS JOIN 时,会生成一个包含 2 * 2 = 4 行数据的结果集,这就是这两个表的笛卡尔积。
3.结合 JOIN 条件和 ON 子句进行筛选
在实际的 SQL 查询中,通常会使用 JOIN 条件和 ON 子句来筛选出符合要求的行,从而减少笛卡尔积的结果集大小。比如:
SELECT * FROM table_a JOIN table_b ON table_a.id = table_b.id;
在这个查询中,数据库系统会先算出 table_a 和 table_b 的笛卡尔积,接着根据 ON 子句中的条件 table_a.id = table_b.id 来筛选出符合条件的行,最终得到的结果集就会比笛卡尔积小很多。
案例1:查询每个部门的员工数量,并按员工数量降序排序
SELECTdepartment,COUNT(*)ASemployee_count
FROMemployees
GROUPBYdepartment
ORDERBYemployee_countDESC;
案例2:查询销售额超过1000的订单,按客户ID分组,计算每个客户的总销售额,并按总销售额升序排序,只返回前5条记录
SELECTcustomer_id,SUM(sales_amount)AStotal_sales
FROMorders
WHEREsales_amount>1000
GROUPBYcustomer_id
HAVINGSUM(sales_amount)>0-- 这一步其实可以省略,因为SUM(sales_amount) > 0总是成立的,但为了展示HAVING的用法,这里保留
ORDERBYtotal_salesASC
LIMIT5;
案例3:找出女生在每门课程中的平均成绩大于75分的课程名称及其平均成绩,并按照平均成绩降序排列。
SELECTcourse,AVG(score)ASaverage_score
FROMstudents
JOINscoresONstudents.student_id=scores.student_id
WHEREgender='Female'
GROUPBYcourse
HAVINGAVG(score)>75
ORDERBYaverage_scoreDESC;
在实际的 SQL 查询中,执行计划的确是决定查询如何执行的关键依据,而不是按照 SQL 语句的书写顺序来执行。
通常,SQL 优化器会根据表的大小、索引情况、筛选条件等多方面因素来生成最优的执行计划。例如,当存在合适的索引时,数据库可能直接通过索引来获取数据,而无需访问实际的表数据,这就是所谓的 “索引扫描”。另外,关于JOIN和WHERE的执行顺序,优化器会优先筛选出尽可能少的数据,以减少后续JOIN操作的工作量,所以可能会先执行WHERE条件过滤主表,再处理JOIN操作。