DELIMITER //CREATEPROCEDURE`add_num`(IN n INT)BEGINDECLARE i INT;DECLARE sum INT;SET i =1;SET sum =0;WHILE i <= n DOSET sum = sum + i;SET i = i +1;ENDWHILE;SELECT sum;END//DELIMITER ;CALL add_num(50);
# 查询某些列SELECTname, create_time FROM tag;# 查询所有列SELECT*from tag;# 取别名SELECTnameAS n, create_time AS ct FROM tag;# 查询常数列,这一列的取值是我们指定的,不是从数据库中动态获取,适合整合不同的数据源SELECT'标签'AS`type`, nameFROM tag;# 去除重复行# DISTINCT 需要放到所有列名前# DISTINCT 是对后面所有列的组合进行去重SELECT DISTINCTnameFROM tag;# 排序# ORDER BY 后面可以有多个列# ORDER BY 后面可以著名排序规则,ASC(default)、DESC,# ORDER BY 后面的列可以不在选择列里面# ORDER BY 通常位于 SELECT 语句的最后SELECTnameFROM tag ORDER BY create_time;# 返回结果数量SELECTnameFROM tag LIMIT5;
所以当使用 ORDER BY 时,应尽量使用 IndexSort,可以使用 explain 查看是否使用索引排序。
所以有如下优化建议:
WHERE 和 ORDER BY 都使用索引,WHERE 使用时为了避免全表扫描,ORDER BY 使用时为了避免 FileSort。
若 WHERE 和 ORDER BY 是相同的列,就使用单索引列;若不同,就使用联合索引。
GROUP BY
我们可以对数据进行分组,并使用聚集函数统计每组数据的值。
SELECTCOUNT(*) AS num, role_main, role_assist FROM heros GROUP BY role_main, role_assist ORDER BY num DESC;
若 GROUP BY 后面还有 ORDER BY,那么排序实际上是对分组后的数据排序,因为分组已经把多条数据聚合成了一条记录。
HAVING
当我们使用 GROUP BY 创建分组之后,可能需要对分组数据进行过滤,此时就需要使用 HAVING。WHERE 是作用于数据行,而 HAVING 是作用于分组。
SELECTCOUNT(*) as num, role_main, role_assistFROM herosWHERE hp_max >6000GROUP BY role_main, role_assistHAVING num >5ORDER BY num;
The SQL standard requires that HAVING must reference only columns in the GROUP BY clause or columns used in aggregate functions. However, MySQL supports an extension to this behavior, and permits HAVING to refer to columns in the SELECT list and columns in outer subqueries as well.
SQL 中关键字是有顺序的:
SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ...
# 查询每个球队中大于平均身高的球员,查询球队的平均身高需要执行多次。SELECT player_name, height, team_idFROM player AS aWHERE height > (SELECTavg(height) FROM player AS b WHERE a.team_id = b.team_id);
子查询还能作为主查询的计算字段:
# 查询每个球队的球员数量SELECT team_name, (SELECTcount(*) FROM player WHERE player.team_id = team.team_id) AS player_numFROM team;
operand comparison_operator ANY (subquery)operand IN (subquery)operand comparison_operator SOME (subquery)operand comparison_operator ALL (subquery)comparison_operator:=><>=<=<>!=
IN: 是否在集合中。
ANY: 与子查询返回的任意一个值比较为真,则整体为真。
ALL: 与子查询返回的所有值比较。
SOME: 是 ANY 的别名,一般用 ANY。
如上文 EXISTS 查询有出场纪录的球员也可写成:
SELECT player_id, team_id, player_nameFROM playerWHERE player_id IN (SELECT player_id FROM player_score);
即 EXISTS 和 IN 的查询结果可等价:
SELECT*FROM A WHERE cc IN (SELECT cc FROM B);# 先执行子句,在执行外层 SQLfor i in Bfor j in Aif j.cc == i.cc then ...SELECT*FROM A WHEREEXISTS (SELECT cc FROM B WHERE B.cc=A.cc)# 对 A 表遍历,每条记录执行子句进行判断for i in Afor j in Bif j.cc == i.cc then ...
查询结果等价,但是查询效率不等价。在上面的例子中,假设 cc 列建立的索引。若 A 表较大,那么 IN 的效率较高;反之,EXISTS 效率较高。
# 查询比(球队 1002 中任意一个球员身高)高的球员SELECT player_id, player_name, heightFROM playerWHERE height > ANY (SELECT height FROM player WHERE team_id =1002);# 查询比(球队 1002 中所有球员身高)都高的球员SELECT player_id, player_name, heightFROM playerWHERE height > ALL (SELECT height FROM player WHERE team_id =1002);
# SQL99SELECT player_id, team_id, player_name, height, team_nameFROM playerNATURAL JOIN team;# SQL92SELECT player_id, a.team_id, player_name, height, team_nameFROM player AS a, team AS bWHERE a.team_id = b.team_id;
若表使用了别名,那么查询字段中只能使用别名,不能使用原有的表名。
ON
用来指定连接条件。
# SQL99SELECT player_id, player.team_id, player_name, height, team_nameFROM playerJOIN team ON player.team_id = team.team_id;SELECT p.player_name, p.height, h.height_levelFROM player as pJOIN height_grades as h ON height BETWEEN h.height_lowest AND h.height_highest;# SQL92 SELECT p.player_name, p.height, h.height_levelFROM player AS p, height_grades AS hWHERE p.height BETWEEN h.height_lowest AND h.height_highest;
USING
用于指定数据表中的同名字段进行等值连接。可以简化 JOIN ON 的等值连接。
# SQL99SELECT player_id, team_id, player_name, height, team_nameFROM playerJOIN team USING (team_id);
LEFT [OUTER] JOIN
左边是主表,需要显示左边表的全部行。
# SQL99SELECT*from playerLEFT JOIN team USING (team_id);# SQL92, MySQL 不支持SELECT*FROM player, teamwhere player.team_id = team.team_id(+);
RIGHT [OUTER] JOIN
右边是主表,需要显示右边表的全部行。
# SQL99SELECT*FROM playerRIGHT JOIN team USING (team_id);# SQL92, MySQL 不支持SELECT*FROM player, teamwhere player.team_id(+) = team.team_id;