MySQL基础及SQL语句练习

目录

SQL语法基础知识总结

分组

函数

文本处理

 日期和时间处理

数值处理

汇总

连接

组合

子查询

视图(VIEW)

索引(INDEX)

约束

存储过程

触发器

创建触发器

查看触发器

删除触发器

SQL语句练习

构建最经典的“教师-学生-成绩”表练习

构建表插入数据

相关练习

使用函数处理数据

顾客登录名

返回 2020 年 1 月的所有订单的订单号和订单日期

使用子查询

返回每个顾客不同订单的总金额

连接表

返回顾客名称和相关订单号以及每个订单的总价

创建高级连接

返回产品名称和每一项产品的总订单数


SQL语法基础知识总结

分组

group by

  • group by 子句将记录分组到汇总行中。
  • group by 为每个组返回一个记录。
  • group by 通常还涉及聚合countmaxsumavg 等。
  • group by 可以按一列或多列进行分组。
  • group by 按分组字段进行排序后,order by 可以以汇总字段来进行排序。

having

  • having 用于对汇总的 group by 结果进行过滤。
  • having 一般都是和 group by 连用。
  • where 和 having 可以在相同的查询中。

having vs where

  • where:过滤过滤指定的行,后面不能加聚合函数(分组函数)。wheregroup by 前。
  • having:过滤分组,一般都是和 group by 连用,不能单独使用。havinggroup by 之后。

分组聚合一定不要忘记加上 group by ,不然只会有一行结果。

函数

文本处理

函数说明
LEFT()RIGHT()左边或者右边的字符
LOWER()UPPER()转换为小写或者大写
LTRIM()RTRIM()去除左边或者右边的空格
LENGTH()长度
SOUNDEX()转换为语音值

其中, SOUNDEX() 可以将一个字符串转换为描述其语音表示的字母数字模式。

SELECT *
FROM mytable
WHERE SOUNDEX(col1) = SOUNDEX('apple')

 日期和时间处理

  • 日期格式:YYYY-MM-DD
  • 时间格式:HH:MM:SS
函 数说 明
AddDate()增加一个日期(天、周等)
AddTime()增加一个时间(时、分等)
CurDate()返回当前日期
CurTime()返回当前时间
Date()返回日期时间的日期部分
DateDiff()计算两个日期之差
Date_Add()高度灵活的日期运算函数
Date_Format()返回一个格式化的日期或时间串
Day()返回一个日期的天数部分
DayOfWeek()对于一个日期,返回对应的星期几
Hour()返回一个时间的小时部分
Minute()返回一个时间的分钟部分
Month()返回一个日期的月份部分
Now()返回当前日期和时间
Second()返回一个时间的秒部分
Time()返回一个日期时间的时间部分
Year()返回一个日期的年份部分

数值处理

函数说明
SIN()正弦
COS()余弦
TAN()正切
ABS()绝对值
SQRT()平方根
MOD()余数
EXP()指数
PI()圆周率
RAND()随机数

汇总

函 数说 明
AVG()返回某列的平均值
COUNT()返回某列的行数
MAX()返回某列的最大值
MIN()返回某列的最小值
SUM()返回某列值之和

count(*),count(列名)都可以,区别在于,count(列名)是统计非 NULL 的行数

AVG() 会忽略 NULL 行。

使用 DISTINCT 可以让汇总函数值汇总不同的值。

SELECT AVG(DISTINCT col1) AS avg_col
FROM mytable

连接

SELECT * 
FROM TEACHER JOIN COURSE
ON TEACHER.`TNO` = COURSE.`TNO`
WHERE TEACHER.tno = '825';

ONWHERE 的区别

  • 连接表时,SQL 会根据连接条件生成一张新的临时表。ON 就是连接条件,它决定临时表的生成。
  • WHERE 是在临时表生成以后,再对临时表中的数据进行过滤,生成最终的结果集,这个时候已经没有 JOIN-ON 了。

所以总结来说就是:SQL 先根据 ON 生成一张临时表,然后再根据 WHERE 对临时表进行筛选

SQL 允许在 JOIN 左边加上一些修饰性的关键词,从而形成不同类型的连接,如下表所示:

连接类型说明
INNER JOIN 内连接(默认连接方式)只有当两个表都存在满足条件的记录时才会返回行。
LEFT JOIN / LEFT OUTER JOIN 左(外)连接返回左表中的所有行,即使右表中没有满足条件的行也是如此。
RIGHT JOIN / RIGHT OUTER JOIN 右(外)连接返回右表中的所有行,即使左表中没有满足条件的行也是如此。
FULL JOIN / FULL OUTER JOIN 全(外)连接只要其中有一个表存在满足条件的记录,就返回行。
SELF JOIN将一个表连接到自身,就像该表是两个表一样。为了区分两个表,在 SQL 语句中需要至少重命名一个表。
CROSS JOIN交叉连接,从两个或者多个连接表中返回记录集的笛卡尔积。

组合

UNION 运算符将两个或更多查询的结果组合起来,并生成一个结果集,其中包含来自 UNION 中参与查询的提取行。

UNION 基本规则:

  • 所有查询的 列数和列顺序必须相同
  • 每个查询中涉及表的列的数据类型必须相同或兼容
  • 通常返回的列名取自第一个查询。

默认地,UNION 操作符选取不同的值( 默认去重 )。如果允许重复的值,请使用 UNION ALL

SELECT column_name(s) FROM table1
UNION ALL
SELECT column_name(s) FROM table2;

UNION 结果集中的列名总是等于 UNION 中第一个 SELECT 语句中的列名。

JOIN vs UNION

  • JOIN 中连接表的列可能不同,但在 UNION 中,所有查询的列数和列顺序必须相同。
  • UNION 将查询之后的行放在一起(垂直放置),但 JOIN 将查询之后的列放在一起(水平放置),即它构成一个笛卡尔积。

子查询

子查询可以嵌入 SELECTINSERTUPDATE 和 DELETE 语句中,也可以和比较运算符(如 =<><> 等)或逻辑运算符(如 INNOT IN,BETWEEN, EXISTSNOT EXISTS 等)运算符一起使用。

子查询常用在 WHERE 子句和 FROM 子句后边:

  • 当用于 WHERE 子句时,根据不同的运算符,子查询可以返回单行单列、多行单列、单行多列数据。子查询就是要返回能够作为 WHERE 子句查询条件的值。
  • 当用于 FROM 子句时,一般返回多行多列数据,相当于返回一张临时表,所以需要使用 AS 关键字为该临时表起一个名字,这样才符合 FROM 后面是表的规则。这种做法能够实现多表联合查询。

视图(VIEW)

定义:

  • 视图是基于 SQL 语句的结果集的可视化的表。
  • 视图是虚拟的表,本身不包含数据,也就不能对其进行索引操作。对视图的操作和对普通表的操作一样。

作用:

  • 简化复杂的 SQL 操作,比如复杂的联结;
  • 只使用实际表的一部分数据;
  • 通过只给用户访问视图的权限,保证数据的安全性;
  • 更改数据格式和表示。

索引(INDEX)

索引是一种用于快速查询和检索数据的数据结构,其本质可以看成是一种排序好的数据结构。

优点

  • 使用索引可以大大加快 数据的检索速度(大大减少检索的数据量), 这也是创建索引的最主要的原因。
  • 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。

缺点

  • 创建索引和维护索引需要耗费许多时间。当对表中的数据进行增删改的时候,如果数据有索引,那么索引也需要动态的修改,会降低 SQL 执行效率。
  • 索引需要使用物理文件存储,也会耗费一定空间。

但是,使用索引一定能提高查询性能吗?

大多数情况下,索引查询都是比全表扫描要快的。但是如果数据库的数据量不大,那么使用索引也不一定能够带来很大提升。

约束

SQL 约束用于规定表中的数据规则。

如果存在违反约束的数据行为,行为会被约束终止。

约束可以在创建表时规定(通过 CREATE TABLE 语句),或者在表创建之后规定(通过 ALTER TABLE 语句)。

约束类型:

  • NOT NULL - 指示某列不能存储 NULL 值。
  • UNIQUE - 保证某列的每行必须有唯一的值。
  • PRIMARY KEY - NOT NULL 和 UNIQUE 的结合。确保某列(或两个列多个列的结合)有唯一标识,有助于更容易更快速地找到表中的一个特定的记录。
  • FOREIGN KEY - 保证一个表中的数据匹配另一个表中的值的参照完整性。
  • CHECK - 保证列中的值符合指定的条件。
  • DEFAULT - 规定没有给列赋值时的默认值。

存储过程

存储过程可以看成是对一系列 SQL 操作的批处理。存储过程可以由触发器,其他存储过程以及 Java, Python,PHP 等应用程序调用。

使用存储过程的好处:

  • 代码封装,保证了一定的安全性;
  • 代码复用;
  • 由于是预先编译,因此具有很高的性能。

创建存储过程:

  • 命令行中创建存储过程需要自定义分隔符,因为命令行是以 ; 为结束符,而存储过程中也包含了分号,因此会错误把这部分分号当成是结束符,造成语法错误。
  • 包含 inoutinout 三种参数。
  • 给变量赋值都需要用 select into 语句。
  • 每次只能给一个变量赋值,不支持集合的操作。

  • delimiter //
    
    create procedure myprocedure( out ret int )
        begin
            declare y int;
            select sum(col1)
            from mytable
            into y;
            select y*y into ret;
        end //
    
    delimiter ;
    
    call myprocedure(@ret);
    select @ret;

需要注意的是:阿里巴巴《Java 开发手册》强制禁止使用存储过程。因为存储过程难以调试和扩展,更没有移植性。

至于到底要不要在项目中使用,还是要看项目实际需求,权衡好利弊即可!

触发器

触发器是一种与表操作有关的数据库对象,当触发器所在表上出现指定事件时,将调用该对象,即表的操作事件触发表上的触发器的执行。

我们可以使用触发器来进行审计跟踪,把修改记录到另外一张表中。

使用触发器的优点:

  • SQL 触发器提供了另一种检查数据完整性的方法。
  • SQL 触发器可以捕获数据库层中业务逻辑中的错误。
  • SQL 触发器提供了另一种运行计划任务的方法。通过使用 SQL 触发器,您不必等待运行计划任务,因为在对表中的数据进行更改之前或之后会自动调用触发器。
  • SQL 触发器对于审计表中数据的更改非常有用。

使用触发器的缺点:

  • SQL 触发器只能提供扩展验证,并且不能替换所有验证。必须在应用程序层中完成一些简单的验证。例如,您可以使用 JavaScript 在客户端验证用户的输入,或者使用服务器端脚本语言(如 JSP,PHP,ASP.NET,Perl)在服务器端验证用户的输入。
  • 从客户端应用程序调用和执行 SQL 触发器是不可见的,因此很难弄清楚数据库层中发生了什么。
  • SQL 触发器可能会增加数据库服务器的开销。

MySQL 不允许在触发器中使用 CALL 语句 ,也就是不能调用存储过程。

注意:在 MySQL 中,分号 ; 是语句结束的标识符,遇到分号表示该段语句已经结束,MySQL 可以开始执行了。因此,解释器遇到触发器执行动作中的分号后就开始执行,然后会报错,因为没有找到和 BEGIN 匹配的 END。

这时就会用到 DELIMITER 命令(DELIMITER 是定界符,分隔符的意思)。它是一条命令,不需要语句结束标识,语法为:DELIMITER new_delemiternew_delemiter 可以设为 1 个或多个长度的符号,默认的是分号 ;,我们可以把它修改为其他符号,如 $ - DELIMITER $ 。在这之后的语句,以分号结束,解释器不会有什么反应,只有遇到了 $,才认为是语句结束。注意,使用完之后,我们还应该记得把它给修改回来。

在 MySQL 5.7.2 版之前,可以为每个表定义最多六个触发器。

  • BEFORE INSERT - 在将数据插入表格之前激活。
  • AFTER INSERT - 将数据插入表格后激活。
  • BEFORE UPDATE - 在更新表中的数据之前激活。
  • AFTER UPDATE - 更新表中的数据后激活。
  • BEFORE DELETE - 在从表中删除数据之前激活。
  • AFTER DELETE - 从表中删除数据后激活。

但是,从 MySQL 版本 5.7.2+开始,可以为同一触发事件和操作时间定义多个触发器。

NEWOLD

  • MySQL 中定义了 NEWOLD 关键字,用来表示触发器的所在表中,触发了触发器的那一行数据。
  • INSERT 型触发器中,NEW 用来表示将要(BEFORE)或已经(AFTER)插入的新数据;
  • UPDATE 型触发器中,OLD 用来表示将要或已经被修改的原数据,NEW 用来表示将要或已经修改为的新数据;
  • DELETE 型触发器中,OLD 用来表示将要或已经被删除的原数据;
  • 使用方法:NEW.columnName (columnName 为相应数据表某一列名)

创建触发器

DELIMITER $
CREATE TRIGGER `trigger_insert_user`
AFTER INSERT ON `user`
FOR EACH ROW
BEGIN
    INSERT INTO `user_history`(user_id, operate_type, operate_time)
    VALUES (NEW.id, 'add a user',  now());
END $
DELIMITER ;

注意使用 DELIMITER 命令修改语句结束符

查看触发器

SHOW TRIGGERS;

删除触发器

DROP TRIGGER IF EXISTS trigger_insert_user;

SQL语句练习

构建最经典的“教师-学生-成绩”表练习

构建表插入数据

-- MySQL dump 10.13  Distrib 5.7.17, for macos10.12 (x86_64)
--
-- Host: localhost    Database: learn_sql_pdai_tech
-- ------------------------------------------------------
-- Server version	5.7.28

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Table structure for table `COURSE`
--

DROP TABLE IF EXISTS `COURSE`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `COURSE` (
  `CNO` varchar(5) NOT NULL,
  `CNAME` varchar(10) NOT NULL,
  `TNO` varchar(10) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `COURSE`
--

LOCK TABLES `COURSE` WRITE;
/*!40000 ALTER TABLE `COURSE` DISABLE KEYS */;
INSERT INTO `COURSE` VALUES ('3-105','计算机导论','825'),('3-245','操作系统','804'),('6-166','数据电路','856'),('9-888','高等数学','100');
/*!40000 ALTER TABLE `COURSE` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `SCORE`
--

DROP TABLE IF EXISTS `SCORE`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `SCORE` (
  `SNO` varchar(3) NOT NULL,
  `CNO` varchar(5) NOT NULL,
  `DEGREE` decimal(10,1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `SCORE`
--

LOCK TABLES `SCORE` WRITE;
/*!40000 ALTER TABLE `SCORE` DISABLE KEYS */;
INSERT INTO `SCORE` VALUES ('103','3-245',86.0),('105','3-245',75.0),('109','3-245',68.0),('103','3-105',92.0),('105','3-105',88.0),('109','3-105',76.0),('101','3-105',64.0),('107','3-105',91.0),('101','6-166',85.0),('107','6-106',79.0),('108','3-105',78.0),('108','6-166',81.0);
/*!40000 ALTER TABLE `SCORE` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `STUDENT`
--

DROP TABLE IF EXISTS `STUDENT`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `STUDENT` (
  `SNO` varchar(3) NOT NULL,
  `SNAME` varchar(4) NOT NULL,
  `SSEX` varchar(2) NOT NULL,
  `SBIRTHDAY` datetime DEFAULT NULL,
  `CLASS` varchar(5) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `STUDENT`
--

LOCK TABLES `STUDENT` WRITE;
/*!40000 ALTER TABLE `STUDENT` DISABLE KEYS */;
INSERT INTO `STUDENT` VALUES ('108','曾华','男','1977-09-01 00:00:00','95033'),('105','匡明','男','1975-10-02 00:00:00','95031'),('107','王丽','女','1976-01-23 00:00:00','95033'),('101','李军','男','1976-02-20 00:00:00','95033'),('109','王芳','女','1975-02-10 00:00:00','95031'),('103','陆君','男','1974-06-03 00:00:00','95031');
/*!40000 ALTER TABLE `STUDENT` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `TEACHER`
--

DROP TABLE IF EXISTS `TEACHER`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `TEACHER` (
  `TNO` varchar(3) NOT NULL,
  `TNAME` varchar(4) NOT NULL,
  `TSEX` varchar(2) NOT NULL,
  `TBIRTHDAY` datetime NOT NULL,
  `PROF` varchar(6) DEFAULT NULL,
  `DEPART` varchar(10) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `TEACHER`
--

LOCK TABLES `TEACHER` WRITE;
/*!40000 ALTER TABLE `TEACHER` DISABLE KEYS */;
INSERT INTO `TEACHER` VALUES ('804','李诚','男','1958-12-02 00:00:00','副教授','计算机系'),('856','张旭','男','1969-03-12 00:00:00','讲师','电子工程系'),('825','王萍','女','1972-05-05 00:00:00','助教','计算机系'),('831','刘冰','女','1977-08-14 00:00:00','助教','电子工程系');
/*!40000 ALTER TABLE `TEACHER` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2020-02-06 18:18:25

相关练习

1.查询Score表中的最高分的学生学号和课程号。

select
  sno,
  CNO
from SCORE
where DEGREE = (
  select max(DEGREE)
  from SCORE
);

2.查询Score表中至少有5名学生选修的并以3开头的课程的平均分数。

SELECT
  AVG(DEGREE) ,
  CNO
FROM SCORE
WHERE cno LIKE '3%'
GROUP BY  CNO
HAVING COUNT(*)> 5;

3.查询最低分大于70,最高分小于90的Sno列。

SELECT SNO 
FROM SCORE
GROUP BY SNO
HAVING MIN(DEGREE) > 70 AND MAX(DEGREE) < 90;

4.查询所有学生的Sname、Cname和Degree列。

SELECT
  A.SNAME,
  B.CNAME,
  C.DEGREE
FROM STUDENT A
  JOIN (COURSE B, SCORE C)
    ON A.SNO = C.SNO AND B.CNO = C.CNO;

假设使用如下命令建立了一个grade表:

CREATE TABLE `grade` (
  `low` NUMERIC(3, 0) NOT NULL,
  `upp`  NUMERIC(3) NOT NULL,
  `rank` VARCHAR(1) NOT NULL
) ENGINE=INNODB DEFAULT CHARSET=utf8;


INSERT INTO grade VALUES (90, 100, 'A');
INSERT INTO grade VALUES (80, 89, 'B');
INSERT INTO grade VALUES (70, 79, 'C');
INSERT INTO grade VALUES (60, 69, 'D');
INSERT INTO grade VALUES (0, 59, 'E');

5.现查询所有同学的Sno、Cno和rank列。

SELECT
  A.SNO,
  A.CNO,
  B.RANK
FROM SCORE A, grade B
WHERE A.DEGREE BETWEEN B.LOW AND B.UPP
ORDER BY B.RANK;

6.查询score中选学一门以上课程的同学中分数为非最高分成绩的学生记录

SELECT * 
FROM STUDENT WHERE SNO IN (
SELECT SNO 
FROM SCORE 
WHERE DEGREE < (SELECT MAX(DEGREE) FROM SCORE )
GROUP BY SNO
HAVING COUNT(*) > 1);

7.查询和学号为108的同学同年出生的所有学生的Sno、Sname和Sbirthday列。

SELECT
  SNO,
  SNAME,
  SBIRTHDAY
FROM STUDENT
WHERE YEAR(SBIRTHDAY) = (
  SELECT YEAR(SBIRTHDAY)
  FROM STUDENT
  WHERE SNO = '108'
);

8.查询“张旭“教师任课的学生成绩。

SELECT *
FROM SCORE
WHERE cno = (
  SELECT CNO
  FROM COURSE
    INNER JOIN TEACHER ON COURSE.TNO = TEACHER.TNO AND TNAME = '张旭'
);
/* 两种方式*/
SELECT *
FROM SCORE
WHERE cno = (
  SELECT CNO
  FROM COURSE
  WHERE TNO =( SELECT TNO FROM TEACHER WHERE TNAME = '张旭')
);

9.查询“计算机系”与“电子工程系“不同职称的教师的Tname和Prof

SELECT
  tname,
  prof
FROM TEACHER
WHERE depart = '计算机系' AND prof NOT IN (
  SELECT prof
  FROM TEACHER
  WHERE depart = '电子工程系'
);

10.查询选修编号为“3-105“课程且成绩至少高于选修编号为“3-245”的同学的Cno、Sno和Degree,并按Degree从高到低次序排序。

SELECT
  CNO,
  SNO,
  DEGREE
FROM SCORE
WHERE CNO = '3-105' AND DEGREE > ANY (
  SELECT DEGREE
  FROM SCORE
  WHERE CNO = '3-245'
)
ORDER BY DEGREE DESC;

11.查询选修编号为“3-105”且成绩高于选修编号为“3-245”课程的同学的Cno、Sno和Degree.

SELECT *
FROM SCORE
WHERE CNO = '3-105' AND DEGREE > ALL (
  SELECT DEGREE
  FROM SCORE
  WHERE CNO = '3-245'
)
ORDER BY DEGREE DESC;

12.查询所有教师和同学的name、sex和birthday.

SELECT
  TNAME     NAME,
  TSEX      sex,
  TBIRTHDAY birthday
FROM TEACHER
UNION
SELECT
  sname     NAME,
  SSEX      sex,
  SBIRTHDAY birthday
FROM STUDENT;

13.查询成绩比该课程平均成绩低的同学的成绩表。

SELECT A.*
FROM SCORE A
WHERE DEGREE < (SELECT AVG(DEGREE)
                FROM SCORE B
                WHERE A.CNO = B.CNO);
                
   /* 下面这种写法是错误的,注意是课程成绩比该课程平均成绩,不是所有课程平均成绩*/             
SELECT A.*
FROM SCORE A
WHERE DEGREE < (SELECT AVG(DEGREE)
                FROM SCORE B);              
                

14.  查询所有任课教师的Tname和Depart.

/*所有任课教师,需要在课程表里有对应的教师号*/
  SELECT
  TNAME,
  DEPART
FROM TEACHER a
WHERE EXISTS(SELECT *
             FROM COURSE b
             WHERE a.TNO = b.TNO);
   /*或者*/       
 SELECT
  TNAME,
  DEPART
FROM TEACHER 
WHERE tno IN (SELECT tno FROM COURSE);

15.查询所有未讲课的教师的Tname和Depart.

SELECT
  TNAME,
  DEPART
FROM TEACHER a
WHERE tno NOT IN (SELECT tno
  
                  FROM COURSE);

16.查询至少有2名男生的班号。

SELECT CLASS
FROM STUDENT
WHERE SSEX = '男'
GROUP BY CLASS
HAVING COUNT(SSEX) > 1;

17.查询Student表中每个学生的姓名和年龄

SELECT
  SNAME,
  YEAR(NOW()) - YEAR(SBIRTHDAY)
FROM STUDENT;

18.查询Student表中最大和最小的Sbirthday日期值。

/* 这样返回的才是两行两个日期类型数据,否则是一行两个,是什么数据类型呢*/
SELECT MIN(SBIRTHDAY) birthday
FROM STUDENT
UNION
SELECT MAX(SBIRTHDAY) birthday
FROM STUDENT;

19.查询和“李军”同性别并同班的同学Sname.

SELECT sname
FROM STUDENT
WHERE (SSEX, CLASS) = (SELECT
                         SSEX,
                         CLASS
                       FROM STUDENT
                       WHERE SNAME = '李军');

使用函数处理数据

顾客登录名

给出 Customers 表 如下:

cust_idcust_namecust_contactcust_city
a1Andy LiAndy LiOak Park
a2Ben LiuBen LiuOak Park
a3Tony DaiTony DaiOak Park
a4Tom ChenTom ChenOak Park
a5An LiAn LiOak Park
a6Lee ChenLee ChenOak Park
a7Hex LiuHex LiuOak Park

【问题】编写 SQL 语句,返回顾客 ID(cust_id)、顾客名称(cust_name)和登录名(user_login),其中登录名全部为大写字母,并由顾客联系人的前两个字符(cust_contact)和其所在城市的前三个字符(cust_city)组成。提示:需要使用函数、拼接和别名。

答案:

SELECT cust_id, cust_name, UPPER(CONCAT(SUBSTRING(cust_contact, 1, 2), SUBSTRING(cust_city, 1, 3))) AS user_login
FROM Customers

知识点:

  • 截取函数SUBSTRING():截取字符串,substring(str ,n ,m)(n 表示起始截取位置,m 表示要截取的字符个数)表示返回字符串 str 从第 n 个字符开始截取 m 个字符;

  • 拼接函数CONCAT():将两个或多个字符串连接成一个字符串,select concat(A,B):连接字符串 A 和 B。

  • 大写函数 UPPER():将指定字符串转换为大写。

返回 2020 年 1 月的所有订单的订单号和订单日期

Orders 订单表如下:

order_numorder_date
a00012020-01-01 00:00:00
a00022020-01-02 00:00:00
a00032020-01-01 12:00:00
a00042020-02-01 00:00:00
a00052020-03-01 00:00:00

【问题】编写 SQL 语句,返回 2020 年 1 月的所有订单的订单号(order_num)和订单日期(order_date),并按订单日期升序排序

答案:

SELECT order_num, order_date
FROM Orders
WHERE month(order_date) = '01' AND YEAR(order_date) = '2020'
ORDER BY order_date

也可以用通配符来做:

SELECT order_num, order_date
FROM Orders
WHERE order_date LIKE '2020-01%'
ORDER BY order_date

知识点:

  • 日期格式:YYYY-MM-DD
  • 时间格式:HH:MM:SS

日期和时间处理相关的常用函数:

函 数说 明
ADDDATE()增加一个日期(天、周等)
ADDTIME()增加一个时间(时、分等)
CURDATE()返回当前日期
CURTIME()返回当前时间
DATE()返回日期时间的日期部分
DATEDIFF计算两个日期之差
DATE_FORMAT()返回一个格式化的日期或时间串
DAY()返回一个日期的天数部分
DAYOFWEEK()对于一个日期,返回对应的星期几
HOUR()返回一个时间的小时部分
MINUTE()返回一个时间的分钟部分
MONTH()返回一个日期的月份部分
NOW()返回当前日期和时间
SECOND()返回一个时间的秒部分
TIME()返回一个日期时间的时间部分
YEAR()返回一个日期的年份部分

使用子查询

返回每个顾客不同订单的总金额

我们需要一个顾客 ID 列表,其中包含他们已订购的总金额。

OrderItems 表代表订单信息,OrderItems 表有订单号:order_num 和商品售出价格:item_price、商品数量:quantity

order_numitem_pricequantity
a000110105
a000211100
a00021200
a001321121
a0003510
a0003119
a000375

Orders 表订单号:order_num、顾客 id:cust_id

order_numcust_id
a0001cust10
a0002cust1
a0003cust1
a0013cust2

【问题】

编写 SQL 语句,返回顾客 ID(Orders 表中的 cust_id),并使用子查询返回 total_ordered 以便返回每个顾客的订单总数,将结果按金额从大到小排序。

答案:

# 写法 1:子查询
SELECT o.cust_id AS cust_id, tb.total_ordered AS total_ordered
FROM (SELECT order_num, Sum(item_price * quantity) AS total_ordered
    FROM OrderItems
    GROUP BY order_num) AS tb,
  Orders o
WHERE tb.order_num = o.order_num
ORDER BY total_ordered DESC

# 写法 2:连接表
SELECT b.cust_id, Sum(a.quantity * a.item_price) AS total_ordered
FROM OrderItems a,Orders b
WHERE a.order_num = b.order_num
GROUP BY cust_id
ORDER BY total_ordered DESC

连接表

返回顾客名称和相关订单号以及每个订单的总价

Customers 表有字段,顾客名称:cust_name、顾客 id:cust_id

cust_idcust_name
cust10andy
cust1ben
cust2tony
cust22tom
cust221an
cust2217hex

Orders 订单信息表,含有字段,订单号:order_num、顾客 id:cust_id

order_numcust_id
a1cust10
a2cust1
a3cust2
a4cust22
a5cust221
a7cust2217

OrderItems 表有字段,商品订单号:order_num、商品数量:quantity、商品价格:item_price

order_numquantityitem_price
a1100010
a220010
a31015
a42550
a51525
a777

【问题】除了返回顾客名称和订单号,返回 Customers 表中的顾客名称(cust_name)和 Orders 表中的相关订单号(order_num),添加第三列 OrderTotal,其中包含每个订单的总价,并按顾客名称再按订单号对结果进行升序排序。

# 简单的等连接语法
SELECT c.cust_name, o.order_num, SUM(quantity * item_price) AS OrderTotal
FROM Customers c,Orders o,OrderItems oi
WHERE c.cust_id = o.cust_id AND o.order_num = oi.order_num
GROUP BY c.cust_name, o.order_num
ORDER BY c.cust_name, o.order_num

注意,可能有小伙伴会这样写:

SELECT c.cust_name, o.order_num, SUM(quantity * item_price) AS OrderTotal
FROM Customers c,Orders o,OrderItems oi
WHERE c.cust_id = o.cust_id AND o.order_num = oi.order_num
GROUP BY c.cust_name
ORDER BY c.cust_name,o.order_num

这是错误的!只对 cust_name 进行聚类确实符合题意,但是不符合 GROUP BY 的语法。

select 语句中,如果没有 GROUP BY 语句,那么 cust_nameorder_num 会返回若干个值,而 sum(quantity _ item_price) 只返回一个值,通过 group by cust_name 可以让 cust_namesum(quantity _ item_price) 一一对应起来,或者说聚类,所以同样的,也要对 order_num 进行聚类。

一句话,select 中的字段要么都聚类,要么都不聚类

创建高级连接

返回产品名称和每一项产品的总订单数

Products 表为产品信息表含有字段 prod_id 产品 id、prod_name 产品名称

prod_idprod_name
a0001egg
a0002sockets
a0013coffee
a0003cola
a0023soda

OrderItems 表为订单信息表含有字段 order_num 订单号和产品 id prod_id

prod_idorder_num
a0001a105
a0002a1100
a0002a200
a0013a1121
a0003a10
a0003a19
a0003a5

【问题】

使用 OUTER JOIN 联结 Products 表和 OrderItems 表,返回产品名称(prod_name)和每一项产品的总订单数(不是订单号),并按产品名称升序排序。

SELECT prod_name, COUNT(order_num) AS orders
FROM Products
LEFT JOIN OrderItems
USING(prod_id)
GROUP BY prod_name
ORDER BY prod_name

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值