【数据库】sql语句的基本书写规范,数据库基本书写方法和注意事项(基于mysql)

导读:本篇文章讲解 【数据库】sql语句的基本书写规范,数据库基本书写方法和注意事项(基于mysql),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

序幕

简单范例: select * from courses where name = “李明”;
上面这句sql的作用就是查询courses表中姓名为李明的人的所有信息(未关联表的情况下的信息)

注意所有的sql语句以分号;结束

注意下面的

sql语句的大小写问题

这个我查询的文档:原文链接:http://c.biancheng.net/view/7178.html
简单总结就是:

  • 我们常用的sql的关键字,函数名,表的列名 不区分大小写

至于另外的视图名,数据库名,表名要根据使用的系统来进行具体的情况划分,详情点开上面的链接

下面这些语句都是等价的:

SELECT name FROM student ;
SELECT NAME FROM student ;
SELECT nAmE FROM student ;
select name from student ;

开篇 sql基础

查询语句 select

关于select查询语句中的反引号的疑问:
反引号的疑问

打印字符

select "Hello SQL!";
直接将要打印的内容写在sql语句的引号中就是要打印的字符串

多列查询:

select name,student from courses
从courses表中查询name 和 student 两列

筛选条件 where

where name ="李明"
其中where的操作符有 等于= 大于> 小于< 不等于!=或<>
注意没有 == 这个符号

DISTICT关键字防重复

这个distict的采用例题的方式更容易让人明白
比如:查询如下所示的teacher表中所有的国籍
老师国籍
如果直接写为select country from teacher
那么查询到的结果将会显示为表最后一列:
查询结果
如若查询的sql语句改为:select DISTINCT country from teacher
那么它就会自动去重 得到的结果为
结果2
这种去重的查询在对确定有哪些地方,哪些人,需要考虑重复的情况下比较有效

插入语句insert into

作用就是向表中添加新的数据

  • 第一种插入方式:(插入的数据为表的一行的数据,且valuen的数据是表对应的列的顺序)
    INSERT INTO `table_name` VALUES (value1, value2, value3,...);
    例如:已有的数据库表:
    已有的数据库表
    插入一条新的数据的书写方式:
INSERT INTO courses
VALUES (13, "Python", 400, "2021-05-23", 3);

需要注意的是设计的表中对应的integer类型的valuen可直接写整数,其他字符类型 如String类型 要写成""或者‘’括起来的形式

  • 第二种插入方式:(插入指定列名的数据)
    INSERT INTO `table_name` (`column1`, `column2`, `column3`,...) VALUES (value1, value2, value3,...);
    相应的column的名称对应value要插入的数据
    例如:向teachers表中插入一条数据
INSERT INTO teachers (name,email,age,country) 
 values ("XiaoFu","XiaoFu@lintcode.com",20,"CN");

插入的效果(第一列为自增列可不用插入数据,数据库自己增加序列):
表格的信息

注意:不管是哪种插入方式它都不能插入一个不存在的列

更新语句update

插入语句的语法:

UPDATE `table_name`
SET `column1`=value1,`column2`=value2,...
WHERE `some_column`=some_value;

where一般作为筛选条件,可根据筛选条件具体判定是哪一列的数据
例如:修改课程表courses选择Artificial Intelligence的人数为500(若表中没有这个名称的课程那么表不会被修改)

update courses 
set student_count = 500
where name = "Artificial Intelligence"; 

删除语句

标准格式删除某一行的数据(通过where来进行筛选):

DELETE FROM `table_name`
WHERE `some_column` = `some_value`;

例如:删除courses表中2020年之前所有的信息

delete from courses
where created_at < "2020-1-1";

若是不加where的判定条件,直接执行delete from courses后将会清空这个表的数据

注意删除表的定义和整个表采用的是 drop table_name

其中关于删除表,删除表中数据,参考这个网站或百度(关于drop,truncate和delete对数据库表的相关操作):https://www.cnblogs.com/1312mn/p/4422396.html

操作符

比较运算符

< > = <>或!= >= <=
大于,小于,等于,不等于(<>或!=),大于等于,小于等于
WHERE A operator B
如:where age > 20where name = "小张
当b是字符串时记得用引号括起来

例如:查询teachers表中所有的国籍为中国的姓名

select name from teachers
where country = "CN"

逻辑运算符 and or not

  • and
    WHERE condition1 AND condition2;
    用于连接多个条件,condition为每一个单独的条件 要求同时满足多个条件
    例如:查询courses表中教师id为4 学生上课大于500的课程信息:
SELECT * FROM courses 
WHERE teacher_id = 4 and student_count > 500;
  • or
    连接多个条件 要求满足这几个条件中的一个
    例如:查询courses表中名为’Web’或‘Big Data’的课程信息
SELECT * FROM courses
WHERE name = 'Web' or name = 'Big Data';
  • not
    过滤不满足条件的数据
    例如:查询教师 id 不为 3 且人数大于 800 的课程
SELECT *
FROM courses
WHERE not teacher_id = 3 and student_count > 800

特殊符号(in、not in、between…and…、is NULL 、LIKE)

  • in
    用法:
SELECT *
FROM `table_name`
WHERE `column_name` IN `value`;

可用于查询多条件 value 可以是一个集合,也可嵌套一个sql的查询语句得到的集合
例如:查询课程表 courses 中开课日期为 2021-01-01 或者 2021-01-03 的所有课程信息

SELECT * FROM courses
WHERE created_at in ('2021-01-01','2021-01-03');
  • not in 排除

语法:

SELECT *
FROM `table_name`
WHERE `column_name` NOT IN value;

例如:查询课程表 courses 中所有教师 id teacher_id 不为 1 或 3 的所有课程名称

SELECT name FROM courses
WHERE teacher_id not in (1,3);
  • between…and… 查询两值间的数据范围

语法:

SELECT *
FROM `table_name`
WHERE `column_name` BETWEEN `value` AND `value`;

例如:查询课程表 courses 中学生数量在 50 到 55 之间的所有课程信息

SELECT * from courses
where student_count BETWEEN 50 and 55;
  • IS NULL 查询空数据
    NULL 值代表遗漏的未知数据。默认的,表的列可以存放 NULL 值。

例如:查询教师表 teachers 中,国籍为 ‘CN’ 或 ‘JP’ 且 email 信息不为空的所有教师信息

SELECT * FROM teachers
WHERE country in ('CN','JP') and email is not  NULL;
  • LIKE 模糊查询

语法:

SELECT *
FROM `table_name`
WHERE `column_name` LIKE  `value`;

关于value中的使用规范:
通配符

例如:查询教师表 teachers 中,所有使用 qq 邮箱的教师名字和邮箱

SELECT name,email FROM teachers
WHERE email like '%@qq.com'

上面的这个%就代表了前面有0个或多个字符,最后以@qq.com结尾,有点类似于python中的正则表达式

排序 order by和 限制输出 limit

  • order by 对数据进行排序 默认升序
    ASC :按升序排列,ORDER BY 默认按照升序对记录进行排序,因此升序的关键字 ASC 可以省去不写。
    DESC:按降序排列,如果需要按照降序对记录进行排序,可以使用 DESC 关键字。

语法:

SELECT `column_name`, `column_name`
FROM `table_name`
ORDER BY `column_name`, `column_name` ASC|DESC;

例如:查询教师表 teachers 中的中国教师,并按照年龄降序排序

SELECT * FROM teachers 
WHERE country = 'CN'
order by age DESC
  • limit 对输出结果集的行数进行约束
    offset :是返回集的初始标注,起始点是0,不是1哦
    count :制定返回的数量

语法:

SELECT `column_name`, `column_name`
FROM `table_name`
LIMIT `offset` , `count`;

例如:从教师表(teachers)中查询一条年龄最大的中国教师的信息

SELECT * FROM teachers
WHERE country = 'CN'
order by age DESC
limit 1;

函数

算术函数(1)(avg、max、min、sum)

  • AVG() 函数
    平均函数 它用于求数值列的平均值。它可以用来返回所有列的平均值,也可以用来返回特定列和行的平均值, 列中的数据均为空时,结果会返回 NULL

语法:

SELECT AVG(`student_count`) AS `average_student_count`
FROM `courses`;

as 后面的是给计算的结果这个新的列去的一个名字
例如:查询教师表 teachers 中教师邮箱为 ‘@qq.com’ 结尾的年龄的平均值,最后返回结果列名显示为 ‘average_teacher_age’ 。

SELECT avg(age) as average_teacher_age FROM teachers
WHERE email like '%@qq.com';
  • MAX() 函数
    用于返回指定列中的最大值 列中的数据均为空时,结果会返回 NULL

语法:

SELECT MAX(`column_name`) 
FROM `table_name`;

例如:从教师表 teachers 中,查询最年长的中国教师信息,并返回该教师的年龄。

SELECT MAX(age) as max_age from teachers 
WHERE country = 'CN'
  • MIN() 函数
    用于返回指定列中的最小值 与MAX相同 就不特此举例了

  • SUM() 函数
    用于统计数值列的总数并返回其值

语法:

SELECT SUM(`column_name`) 
FROM `table_name`;

例如:统计课程表 courses 中 teacher_id 为 3 的教师所教授的学生总数,并用select_student_sum 作为结果集列名。

SELECT SUM(student_count) as select_student_sum FROM courses
WHERE teacher_id = 3

算术函数 (2) (ROUND、NULL、COUNT)

  • ROUND () 函数
    用于把数值字段舍入(四舍五入)为指定的小数位数

语法:

SELECT ROUND(`column_name`, `decimals`) 
FROM `table_name`;

例如:查询教师表 teachers 中,20 岁(不包含 20 岁)以上教师的平均年龄,返回的字段为 avg_teacher_age ,结果保留四舍五入后的整数

SELECT ROUND(AVG(age)) as avg_teacher_age FROM teachers
where age > 20;
  • NULL () 函数 讲述不是很详细,请参考:跳转
    • ISNULL() 函数用于判断字段是否为 NULL,它只有一个参数 column_name 为列名,根据column_name 列中的字段是否为 NULL 值返回 0 或 1。
      语法:
SELECT ISNULL(`column_name`)
FROM `table_name`;

如果 column_name 列中的某个字段是 NULL 则返回 1,不是则返回 0

  • IFNULL() 函数也用于判断字段是否为NULL,但是与 ISNULL() 不同的是它接收两个参数,第一个参数 column_name 为列名,第二个参数 value 相当于备用值。
    语法:
SELECT IFNULL(`column_name`, `value`)
FROM `table_name`; 

如果 column_name 列中的某个字段是 NULL 则返回 value 值,不是则返回对应内容。
COALESCE(column_name, value) 函数也用于判断字段是否为NULL,其用法和 IFNULL() 相同

例如:从 teachers 表中找出没有邮箱并且年龄大于20岁的教师信息

SELECT * FROM teachers 
WHERE isnull(email) and age > 20

isnull函数的返回值可用于判断条件

  • count() 函数

    • COUNT( column_name ) 会对指定列具有的行数进行计数,但是会除去值为 NULL 的行
    • COUNT(*) 会对表中行的数目进行计数,包括值为 NULL 所在行和重复项所在行

例如:统计教师表中年龄在 20 到 28 岁之间,且国籍为中国或英国的教师人数,最后返回统计值,结果列名显示为 teacher_count

SELECT COUNT(*) AS teacher_count 
FROM teachers
WHERE age between 20 and 28 and country in ('CN','UK')

注意: COUNT(column_name) 与 COUNT(*) 的区别

COUNT(column_name) 中,如果 column_name 字段中的值为 NULL,则计数不会增加,而如果字段值为空字符串””,则字段值会加 1

COUNT() 中,除非整个记录全为 NULL,则计数不会增加,如果存在某一个记录不为 NULL,或者为空字符串””,计数值都会加 1。正常来说,表都会有主键,而主键不为空,所以 COUNT() 在有主键的表中等同于 COUNT(PRIMARY_KEY),即查询有多少条记录。

  • COUNT(DISTINCT column_name)
    COUNT(DISTINCT column_name) 函数返回指定列的不同值的数目

例如:统计课程表 courses 中不同的教师 id teacher_id 的数量,最后返回统计值,结果列名显示为 teacher_count

SELECT COUNT(DISTINCT teacher_id) as teacher_count
FROM courses

相当于统计有多少个teacher_id (有多少个教师)

时间函数 (1) (NOW、CURDATE、CURTIME、DATE、TIME、EXTRACT、DATE_FORMAT)

  • NOW() CURDATE() CURTIME() 获取当前时间
    • NOW() 可以用来返回当前日期和时间 格式:YYYY-MM-DD hh:mm:ss
    • CURDATE() 可以用来返回当前日期 格式:YYYY-MM-DD
    • CURTIME() 可以用来返回当前时间 格式:hh:mm:ss
在使用 NOW() 和 CURTIME() 时,如果要精确的秒以后的时间的话,
可以在()中加数字,加多少,就表示精确到秒后多少位
比如 NOW(3) 就是精确到毫秒,表示为: 2021-03-31 15:27:20.645

例如:向记录表 records 中插入当前的日期

INSERT INTO records
VALUES(CURDATE())
  • DATE() 、TIME() 获取日期和时间

例如:查询课程表中课程的创建日期

SELECT `name`,DATE(`created_at`) AS `created_date`
FROM `courses`;

得到结果:
在这里插入图片描述
例如2:分别查询出课程表的课程创建时间中的日期与时间

SELECT `name`, `created_at`, 
	DATE_FORMAT(DATE(`created_at`),"%Y-%m-%d") AS `created_date`, 
	DATE_FORMAT(TIME(`created_at`),"%H:%i:%s") AS `created_time`
FROM `courses`;

结果如下:
查询结果

  • EXTRACT() 函数提取指定的时间信息

语法:

SELECT EXTRACT(unit FROM date)
FROM `table`

date 参数是合法的日期表达式。
unit 参数是需要返回的时间部分,如 YEAR 、MONTH 、 DAY 、 HOUR 、MINUTE 、SECOND 等。

例如:从课程表 courses 中查询所有课程的课程名称( name )和课程创建时间( created_at )的小时数,将提取小时数的列名起别名为 created_hour。

SELECT name,EXTRACT(hour FROM created_at) as created_hour
FROM courses
  • DATE_FORMAT() 格式化输出日期

语法:

SELECT DATE_FORMAT(date,format);

其中
date 一个有效日期。
format 是 date/time 的输出格式。 其中 %m 表示月份,%d 表示日期,%Y 表示年份,%w 表示星期。
关于format的格式要求:参考
%i、%l(小写L)、%I(大写i)每一个所表示的格式都不同,参考上面的这个链接
例如:查询 courses 表,查询课程创建时间,按照 ’年-月-日 时:分:秒’ 的格式返回结果,返回列名显示为 DATE_FORMAT

SELECT DATE_FORMAT(created_at,'%Y-%m-%d %H:%i:%S') AS DATE_FORMAT FROM courses

时间函数2 (2)(DATE_ADD、DATE_SUB、DATEDIFF、TIMESTAMPDIFF)

  • DATE_ADD() 增加时间
    常用的时间函数之一,用于向日期添加指定的时间间隔

语法:

SELECT DATE_ADD(date, INTERVAL expr type)
FROM table_name

date 指代希望被操作的有效日期,为起始日期
expr 是希望添加的时间间隔的数值(expr 是一个字符串,对于负值的间隔,可以以 ”-“ 开头)
type 是具体的数据类型,表示加上时间间隔的单位(可以是 MICROSECOND , SECOND , MINUTE , HOUR , DAY , WEEK , MONTH , QUARTER , YEAR 等)

例如:修改 courses 表中课程的课程创建日期,将课程创建日期均推迟一天,最后返回课程名称 name 及修改后的课程创建时间,修改后的课程创建时间命名为 new_created

SELECT name, DATE_ADD(created_at, INTERVAL 1 day) as new_created
FROM courses
  • DATE_SUB() 函数
    用于从日期减去指定的时间间隔。它与 DATE_ADD() 函数具有相似的用法。

语法:

SELECT DATE_SUB(date, INTERVAL expr type)
FROM table_name

expr和type与上面DATE_ADD()函数相同
注意:date_add 本身为加,expr为负数时,为减,date_SUB 本身为减,expr为负数时,为加

  • DATEDIFF() 函数
    常用的日期差,在 MySQL 中默认只能计算天数差

语法:

SELECT DATEDIFF(时间1,时间2) AS date_diff FROM courses;

DATEDIFF() 差值计算规则:时间 1 – 时间 2

例如:查询 courses 表,计算从 2019 年 03 月 26 日到创建时间(created_at)相差的天数,结果列名以 date_diff 显示

select  DATEDIFF(created_at,'2019-03-26') AS date_diff from courses
  • TIMESTAMPDIFF() 函数
    查询功能多的 MySQL 自带的日期函数,可以计算两个日期相差的年(YEAR,时间1,时间2),月(MONTH,时间1,时间2),周(WEEK,时间1,时间2),日(DAY,时间1,时间2),小时(HOUR,时间1,时间2)。

语法:

SELECT TIMESTAMPDIFF (类型,时间1,时间2) AS year_diff;

TIMESTAMPDIFF() 差值计算规则:时间 2 – 时间 1
例如:查询 courses 表,计算课程创建时间与 ‘2020-04-22’ 的月数差,返回列名显示为 MonthDiff

select TIMESTAMPDIFF(month,created_at,'2020-04-22') as MonthDiff from courses

注意两个时间相减的方向

约束与连结

约束(not null、UNIQUE、PRIMARY KEY、CHECK、DEFAULT)

在 SQL 中,约束是规定表中的数据规则。若存在违反约束的行为,行为就会阻止。它能帮助管理员更好地管理数据库,并且确保数据库中数据的正确性和有效性。例如在后台的数据库设计中对要输入的数据进行核查或判断,再决定是否写入数据库,这都是约束的应用。

  • not null 非空约束
    NOT NULL 约束强制列不接受 NULL 值,强制字段始终包含值,这意味着,如果不向字段添加值,就无法插入新纪录或者更新记录。

例如

CREATE TABLE `Persons` (
    `ID` int NOT NULL,
    `LastName` varchar(255) NOT NULL,
    `FirstName` varchar(255) NOT NULL,
    `Age` int
);

在一个已创建的表的 Age 字段中添加 NOT NULL 约束如下所示:

ALTER TABLE `Persons`
MODIFY `Age` int NOT NULL;

在一个已创建的表的 Age 字段中删除 NOT NULL 约束如下所示:

ALTER TABLE `Persons`
MODIFY `Age` int NULL;
注意
不要把 NULL 值与空串相混淆。NULL 值是没有值,

它不是空串。如果指定’ ‘(两个单引号,其间没有字符),这在 NOT NULL 列中是允许的。空串是一个有效的值,它不是无值。NULL 值用关键字 NULL 而不是空串指定

  • UNIQUE 唯一约束 写作参考
    三个特点:
    • UNIQUE 约束唯一标识数据库表中的每条记录
    • UNIQUE 和 主键约束均为列或列集合提供了唯一性的保证(同列唯一)
    • 主键约束会自动定义一个 UNIQUE 约束,或者说主键约束是一种特殊的 UNIQUE 约束。但是二者有明显的区别:每个表可以有多个 UNIQUE 约束,但只能有一个主键约束。

create table的时候 unique的约束创建
下面的 SQL 在 Persons 表创建时在 P_Id 列上创建 UNIQUE 约束:
MYSQL中

CREATE TABLE `Persons`
(
`P_Id` int NOT NULL,
`LastName` varchar(255) NOT NULL,
`FirstName` varchar(255),
`Address` varchar(255),
`City` varchar(255),
UNIQUE (`P_Id`)
)

SQL Server / Oracle / MS Access中:

CREATE TABLE `Persons`
(
`P_Id` int NOT NULL UNIQUE,
`LastName` varchar(255) NOT NULL,
`FirstName` varchar(255),
`Address` varchar(255),
`City` varchar(255)
)

命名 UNIQUE 约束,并定义多个列的 UNIQUE 约束:
MYSQL / SQL Server / Oracle / MS Access 所有都是

CREATE TABLE `Persons`
(
`P_Id` int NOT NULL,
`LastName` varchar(255) NOT NULL,
`FirstName` varchar(255),
`Address` varchar(255),
`City` varchar(255),
CONSTRAINT uc_PersonID UNIQUE (`P_Id`,`LastName`)
)

ALTER TABLE 时的 UNIQUE 约束:
当表已被创建时,在 P_Id 列创建 UNIQUE 约束:
MySQL / SQL Server / Oracle / MS Access中,增加约束

ALTER TABLE `Persons`
ADD UNIQUE`P_Id`

例如:当表已被创建时,需命名 UNIQUE 约束,并定义多个列的 UNIQUE 约束:
MySQL / SQL Server / Oracle / MS Access

ALTER TABLE `Persons`
ADD CONSTRAINT uc_PersonID UNIQUE (`P_Id`,`LastName`)

constraint 是给取别名

如需撤销 UNIQUE 约束 :
MySQL中:

ALTER TABLE `Persons`
DROP INDEX uc_PersonID

SQL Server / Oracle / MS Access中:

ALTER TABLE `Persons`
DROP CONSTRAINT uc_PersonID
  • PRIMARY KEY 主键约束
    唯一标识数据库表中的每条记录 ,简单的说,PRIMARY KEY = UNIQUE + NOT NULL ,从技术的角度来看,PRIMARY KEY 和 UNIQUE 有很多相似之处。但还是有以下区别:
    • NOT NULL UNIQUE 可以将表的一列或多列定义为唯一性属性,而 PRIMARY KEY 设为多列时,仅能保证多列之和是唯一的,具体到某一列可能会重复。
    • PRIMARY KEY 可以与外键配合,从而形成主从表的关系,而 NOT NULL UNIQUE 则做不到这一点
    • 更大的区别在逻辑设计上。 PRIMARY KEY 一般在逻辑设计中用作记录标识,这也是设置 PRIMARY KEY 的本来用意,而 UNIQUE 只是为了保证域/域组的唯一性。
如:
表一:用户 id (主键),用户名
表二: 银行卡号 id (主键),用户 id (外键)
则表一为主表,表二为从表。

CREATE TABLE 时 添加 PRIMARY KEY 约束
下面的 SQL 在 个人信息表 Persons 创建时,在 P_Id 列上添加 PRIMARY KEY 约束:
方式1:

CREATE TABLE `Persons`
(
    `P_Id` int NOT NULL,
    `LastName` varchar(255) NOT NULL,
    `FirstName` varchar(255),
    `Address` varchar(255),
    `City` varchar(255),
    PRIMARY KEY (`P_Id`)
);

方式2:

CREATE TABLE `Persons`
(
    `P_Id` int NOT NULL PRIMARY KEY,
    `LastName` varchar(255) NOT NULL,
    `FirstName` varchar(255),
    `Address` varchar(255),
    `City` varchar(255)
)

由多个值一起构成主键:

CREATE TABLE `Persons`
(
    `P_Id` int NOT NULL,
    `LastName` varchar(255) NOT NULL,
    `FirstName` varchar(255),
    `Address` varchar(255),
    `City` varchar(255),
    CONSTRAINT pk_PersonID PRIMARY KEY (`P_Id`,`LastName`)
)

在已创建的表中添加主键:

ALTER TABLE `Persons`
ADD CONSTRAINT pk_PersonID PRIMARY KEY (`P_Id`,`LastName`)
注释:
如果您使用 ALTER TABLE 语句添加主键,必须把主键列声明为不包含 NULL 值(在表首次创建时)

撤销主键:

ALTER TABLE `Persons`
DROP PRIMARY KEY

另一种方式(取别名的主键,上一种方法其实也可以去除):

ALTER TABLE `Persons`
DROP CONSTRAINT pk_PersonID
  • FOREIGN KEY 外键约束
    • 一个表中的 FOREIGN KEY 指向另一个表中的 UNIQUE KEY 。
    • 外键最根本的作用:保证数据的完整性和一致性。
    • 外键约束是指用于在两个表之间建立关系,需要指定引用主表的哪一列。

CREATE TABLE 时的 SQL FOREIGN KEY 约束
在 “Orders” 表创建时在 “P_Id” 列上创建 FOREIGN KEY 约束:

CREATE TABLE `Orders`
(
`O_Id` int NOT NULL,
`OrderNo` int NOT NULL,
`P_Id` int,
PRIMARY KEY (O_Id),
FOREIGN KEY (P_Id) REFERENCES Persons(P_Id)
)

写法2:

CREATE TABLE `Orders`
(
`O_Id` int NOT NULL PRIMARY KEY,
`OrderNo` int NOT NULL,
P_Id int FOREIGN KEY REFERENCES Persons(P_Id)
)
其中
NOT NULL 表示该字段不为空
REFERENCES 表示 引用一个表

命名外键约束:

CREATE TABLE `Orders`
(
`O_Id` int NOT NULL,
`OrderNo` int NOT NULL,
`P_Id` int,
PRIMARY KEY (O_Id),
CONSTRAINT fk_PerOrders FOREIGN KEY (P_Id)
REFERENCES Persons(P_Id)
)
其中
CONSTRAINT 表示约束,后面接约束名称,常用于创建约束和删除约束;

添加外键约束:
写法1:

ALTER TABLE `Orders`
ADD FOREIGN KEY (P_Id)
REFERENCES Persons(P_Id)

写法2:

ALTER TABLE `Orders`
ADD CONSTRAINT fk_PerOrders
FOREIGN KEY (P_Id)
REFERENCES Persons(P_Id)

撤销外键约束:

ALTER TABLE `Orders`
DROP FOREIGN KEY fk_PerOrders
  • check 检查约束
    用于限制列中的值的范围,评估插入或修改后的值。满足条件的值将会插入表中,否则将放弃插入操作。 可以为同一列指定多个 CHECK 约束。
    CHECK 约束既可以用于某一列也可以用于某张表:
    1、如果对单个列定义 CHECK 约束,那么该列只允许特定的值。
    2、如果对一个表定义 CHECK 约束,那么此约束会基于行中其他列的值在特定的列中对值进行限制。

    定义 CHECK 约束条件在某种程度上类似于编写查询的 WHERE 子句,使用不同的比较运算符(例如 AND、OR、BETWEEN、IN、LIKE 和 IS NULL)编写其布尔表达式,该布尔表达式将返回 TRUE、FALSE 或 UNKNOWN 。 当条件中存在 NULL 值时,CHECK约束将返回 UNKNOWN 值。

在创建课程表 courses 时,给学生总数 student_count 字段加上一个大于 0 的约束:

`id` int,
`name` varchar(255),
`student_count` int,
`created_at` date,
`teacher_id` int,
CHECK (`student_count` > 0)
)

为多个列添加 CHECK 约束:

CREATE TABLE `courses`
(
`id` int,
`name` varchar(255),
`student_count` int,
`created_at` date,
`teacher_id` int,
CHECK (`student_count` > 0 AND `teacher_id` > 0)
)

表已存在时添加 CHECK 约束:

ALTER TABLE `courses` 
ADD CHECK ( `student_count` > 0);

撤销 CHECK 约束:

ALTER TABLE `courses` 
DROP CHECK chk_courses
  • DEFAULT 默认约束
    1、DEFAULT 约束用于向列中插入默认值。
    2、如果没有规定其他的值,那么会将默认值添加到所有的新记录。
    例如女同学较多,性别就可以默认为“女”,如果插入一条新的记录时没有为这个字段赋值,那么系统会自动为这个字段赋值为“女”。

语法:
<字段名> <数据类型> DEFAULT <默认值>

CREATE TABLE 时的 DEFAULT 约束:

CREATE TABLE `Persons`
(
    `P_Id` int NOT NULL,
    `LastName` varchar(255) NOT NULL,
    `FirstName` varchar(255),
    `Address` varchar(255),
    `City` varchar(255) DEFAULT 'Sandnes'
)

ALTER TABLE 时的 DEFAULT 约束:

ALTER TABLE `Persons`
ALTER `City` SET DEFAULT 'SANDNES'

撤销 DEFAULT 约束:

ALTER TABLE `Persons`
ALTER `City` DROP DEFAULT

连结

联结是一种机制,用于在一条 SELECT 语句中关联多个表,返回一组输出。
这个时候就要说一下联结中的两大主角——主键(PRIMARY KEY)和外键(FOREIGN KEY)

JOIN 连接子句

  • INNER JOIN:内联结 如果表中有至少一个匹配,则返回行
  • LEFT JOIN:左联结 即使右表中没有匹配,也从左表返回所有的行
  • RIGHT JOIN:右联结 即使左表中没有匹配,也从右表返回所有的行
  • FULL JOIN:全联结 只要其中一个表中存在匹配,则返回行
  • CROSS JOIN:交叉联结 又称笛卡尔积,两个表数据一一对应,返回结果的行数等于两个表行数的乘积
  • . 内连接 INNER JOIN 简单的说,内连接就是取两个表的交集,返回的结果就是连接的两张表中都满足条件的部分

内连接 INNER JOIN

语法:

写法一:
SELECT `table1`.`column1`, `table2`.`column2`...
FROM `table1`
INNER JOIN `table2`
ON `table1`.`common_field` = `table2`.`common_field`;

写法二:
SELECT `table1`.`column1`, `table2`.`column2`...
FROM `table1`
JOIN `table2`
ON `table1`.`common_field` = `table2`.`common_field`;

需要注意的是,联结条件需使用特定的 ON 子句给出。

例如:请编写 SQL 语句,将课程表 courses 和教师表 teachers 进行内连接,查询 “Eastern Heretic” 老师所教的所有课程的课程名和课程编号 , 且结果列名分别以课程编号 id 、课程名称 course_name 和教师姓名 teacher_name 显示。

select c.id,c.name as course_name,t.name as teacher_name 
from courses c join teachers t on c.teacher_id = t.id
where t.name = 'Eastern Heretic'

外连接 OUTER JOIN

在MySQL中,外连接查询会返回所操作的表中至少一个表的所有数据记录。在MySQL中,数据查询通过SQL语句 “OUTER JOIN…ON” 来实现,外连接查询可以分为以下三类:

  • 左外连接
    左外连接的结果包括 LEFT OUTER 子句中指定的左表的所有行,而不仅仅是连接列所匹配的行,这就意味着,左连接会返回左表中的所有记录,加上右表中匹配到的记录。如果左表的某行在右表中没有匹配行,那么在相关联的结果行中,右表的所有选择列表均为空值。

  • 右外连接
    外连接查询中的右外连接是指新关系中执行匹配条件时,以关键字 RIGHT JOIN 右边的表为参考表,如果右表的某行在左表中没有匹配行,左表就返回空值。

  • 全外连接
    FULL OUTER JOIN 关键字只要左表(table1)和右表(table2)其中一个表中存在匹配,则返回行。FULL OUTER JOIN 关键字结合了 LEFT JOIN 和 RIGHT JOIN 的结果。
    mysql不支持全连接(full join),可以使用 left join+ union+right join
    (SELECT * from a left JOIN b on a.name=b.id) UNION (SELECT * from a RIGHT JOIN b on a.name=b.id );

查询语法:

SELECT column_name 1,column_name 2 ... column_name n
    FROM table1
        LEFT | RIGHT | FULL  (OUTER) JOIN table2
        ON CONDITION;

在上述语句中,参数 column_name 表示所要查询的字段名字,来源于所连接的表 table1 和 table2,关键字 OUTER JOIN 表示表进行外连接,参数 CONDITION 表示进行匹配的条件。

测试样例表:
测试表

例如1:
请编写 SQL 语句,将教师表 teachers 和课程表 courses 进行左连接,查询来自中国(讲师国籍 country =‘CN’ )的教师名称以及所教课程名称,结果列名请分别以课程名称 course_name ,教师名称 teacher_name 显示。

select c.name as course_name,t.name  as teacher_name
from teachers t left join courses c on t.id = c.teacher_id
where country ='CN'

例如2:
请编写 SQL 语句,将课程表 courses 和教师表 teachers 进行右连接,查询来自中国(讲师国籍 country =‘CN’ )的教师姓名,邮箱以及所教课程名称,结果列名请分别以课程名称 course_name ,教师名称 teacher_name ,教师邮箱 teacher_email 显示。

select c.name as course_name,t.name as teacher_name,t.email as teacher_email
from courses c right join teachers t on c.teacher_id = t.id
where country = 'CN'

例如3:
将课程表 courses 和教师表 teachers 进行外连接,查询所有课程名称以及与其相互对应的教师名称和教师国籍,结果列名请分别以课程名称 course_name 、教师名称 teacher_name 、教师国籍 teacher_country 显示。

select c.name as course_name, t.name as teacher_name,t.country as teacher_country
from courses c left join teachers t on c.teacher_id = t.id
union
select c.name as course_name, t.name as teacher_name,t.country as teacher_country
from courses c right join teachers t on c.teacher_id = t.id

交叉连接 CROSS JOIN

与内连接和外连接相比,交叉连接非常简单,因为它不存在 ON 子句
交叉连接:返回左表中的所有行,左表中的每一行与右表中的所有行组合。即将两个表的数据一一对应,其查询结果的行数为左表中的行数乘以右表中的行数。

CROSS JOIN(交叉连接)的结果也称作笛卡尔积
笛卡尔乘积是指在数学中,两个集合 X 和 Y 的笛卡尓积(Cartesian product),又称直积,表示为X × Y,第一个对象是 X 的成员而第二个对象是 Y 的所有可能有序对的其中一个成员。

定义方式:
交叉连接有两种定义方式,分为隐式连接和显式连接。两种定义方式的查询结果是相同的。
隐式交叉连接:不需要使用 CROSS JOIN 关键字,只要在 SELECT 语句的 FROM 语句后将要进行交叉连接的表名列出即可,这种方式基本上可以被任意数据库系统支持。
隐式语法:

SELECT  `c`.`name` AS `course_name`, `t`.`name` AS `teacher_name`
FROM `courses` `c`,`teachers` `t`;

显示语法:

SELECT  `c`.`name` AS `course_name`, `t`.`name` AS `teacher_name`
FROM `courses` `c`
CROSS JOIN `teachers` `t`;

以下为知识进阶(工作需掌握)

查询进阶(分组查询(group by,having),子查询)

分组查询 group by

语法:

SELECT `column_name`, aggregate_function(`column_name`)
FROM `table_name`
WHERE `column_name` operator value
GROUP BY `column_name`;

例如:查询教师表 teachers,统计不同年龄教师的人数,并将结果按照年龄从大到小排列,返回列名显示为 age_count:

select  age ,count(age) as age_count
from teachers 
group by age
order by age DESC

例如2:查询教师表 teachers 和课程表 courses,统计每个老师教授课程的数量,并将结果按课程数量从大到小排列,如果相同课程数量则按照教师姓名排列,返回列名老师姓名列名显示为 teacher_name ,课程数量列名显示为 course_count:

select t.name as teacher_name,IFNULL(count(c.name),0) as course_count
from courses c right join teachers t on c.teacher_id = t.id
group by teacher_name
order by course_count DESC,teacher_name

HAVING 子句

我们在使用 WHERE 条件子句时会发现其不能与聚合函数联合使用,为解决这一点,SQL 中提供了 HAVING 子句。在使用时, HAVING 子句经常与 GROUP BY 联合使用,HAVING 子句就是对分组统计函数进行过滤的子句
HAVING 子句对于 GROUP BY 子句设置条件的方式其实与 WHERE 子句与 SELECT 的方式类似,语法也相近,但 WHERE 子句搜索条件是在分组操作之前,而 HAVING 则是在之后
语法

SELECT   `column_name`, aggregate_function(`column_name`) 
FROM     `table_name` 
WHERE    `column_name` operator value 
GROUP BY `column_name` 
HAVING   aggregate_function(`column_name`) operator value;

例如:查询 teachers 表中,各个国家所有教师的平均年龄大于所有国家教师的平均年龄的教师信息:

待补充

简单子查询

语法

SELECT `column_name(s)`
FROM `table_name`
WHERE `column_name` OPERATOR (
    SELECT `column_name(s)`
    FROM `table_name`
);
1、子查询方法:
select name from teachers
where id = (
    select teacher_id from courses
    where name = 'Big Data'
)
2、表联结方法:
select t.name from
teachers t join courses c on t.id = c.teacher_id
where c.name = 'Big Data'

方法一耗时:
在这里插入图片描述
方法二耗时:
在这里插入图片描述

对于 INSERT 语句中的子查询来说,首先是使用子查询的 SELECT 语句找到需要插入的数据,之后将返回的数据插入到另一个表中。在子查询中所选择的数据可以用任何字符、日期或数字函数修改:
insert into 插入子查询:
语法:

INSERT INTO `table_name`
	SELECT `colnum_name(s)`
	FROM `table_name`
	[ WHERE VALUE OPERATOR ]

例如:备份teachers表的数据

INSERT INTO `teachers_bkp` 
SELECT * 
FROM `teachers`;

update 子查询用法:
语法:

UPDATE `table_name` 
SET `column_name` = `new_value`
WHERE `column_name` OPERATOR 
   (SELECT `column_name`
   FROM `table_name`
   [WHERE] )

DELETE语句的子查询
语法

DELETE FROM `table_name`
WHERE `column_name` OPERATOR 
   (SELECT `column_name`
   FROM `table_name`  
   [WHERE] )

例如:删除教师表 teachers 中在 2020 年前(不包括 2020 年)创建过课程的教师

delete from teachers 
where id in (
    select distinct teacher_id from courses 
    where created_at < '2020-01-01'
)

子查询进阶

内联视图子查询

范例:现需要查询国籍为美国(USA),且年龄最大的教师,请使用内联视图子查询实现。

SELECT *
FROM (
	SELECT *
	FROM `teachers`
	WHERE `country` = 'USA'
) `T`
WHERE `age` = (
	SELECT MAX(`age`)
	FROM `teachers`
);

IN操作符的多行子查询

范例:现需要查询国籍为美国(USA)的教师所开的所有课程,请使用 IN 操作符进行多行子查询。

SELECT `name`
FROM `courses`
WHERE `teacher_id` IN (
	SELECT `id`
	FROM `teachers`
	WHERE `country` = 'USA'
);

ANY操作符的多行子查询

范例:现需要查询学生上课人数超过 “Eastern Heretic” 的任意一门课的学生人数的课程信息,请使用 ANY 操作符实现多行子查询。

SELECT *
FROM `courses`
WHERE `student_count` > ANY (
		SELECT `student_count`
		FROM `courses`
		WHERE `teacher_id` = (
			SELECT `id`
			FROM `teachers`
			WHERE `name` = 'Eastern Heretic'
		)
	)
	AND `teacher_id` <> (
		SELECT `id`
		FROM `teachers`
		WHERE `name` = 'Eastern Heretic'
	);

ALL 操作符的多行子查询

范例:现需要查询学生人数超过 ”Western Venom“ 所有课程学生人数的课程信息,请使用 ALL 操作符实现多行子查询。

SELECT *
FROM `courses`
WHERE `student_count` > ALL (
	SELECT `student_count`
	FROM `courses`
	WHERE `teacher_id` = (
		SELECT `id`
		FROM `teachers`
		WHERE `name` = 'Western Venom'
	)
);

多列子查询

当是多行多列子查询时,主查询语句的条件语句中引用子查询结果时必须用多行比较符号(IN,ANY,ALL 等)来进行比较。

范例:现需要找到每个国家年龄最大的教师,请编写 SQL 语句实现多列子查询

SELECT `name`, `age`, `country` 
FROM `teachers` 
WHERE (`country`, `age`) IN ( 
        SELECT `country`, MAX(`age`) 
        FROM `teachers` 
        GROUP BY `country` 
);

having子句中的子查询

当子查询出现在 HAVING 子句中时,像 HAVING 子句中的任何表达式一样,表示要进行分组过滤,它被用作行组选择的一部分,一般返回单行单列的数据

范例:现需要计算每位教师所开课程的平均学生人数与全部课程的平均学生人数,比较其大小,最后返回超过全部课程平均学生人数的教师姓名,请编写相应的 SQL 语句实现。

SELECT `name`
FROM `teachers`
WHERE `id` IN (
	SELECT `teacher_id`
	FROM `courses`
	GROUP BY `teacher_id`
	HAVING AVG(`student_count`) > (
		SELECT AVG(`student_count`)
		FROM `courses`
	)
);

小结练习:

描述:请编写 SQL 语句,连接 courses 与 teachers 表,统计不同国籍教师所开课程的学生总数,对于没有任课的老师,学生总人数计为 0 。最后查询教师国籍是 ‘U’ 开头且这个国籍的所有教师的学生总数在 2000 到 5000 之间(包括 2000 和 5000)的教师国籍及学生总数 (别名为 student_count ),结果按照学生总数降序排列,如果学生总数相同,则按照教师国籍升序排列。
两个表字段如下所示:
在这里插入图片描述
先在这儿思考一下吧!!!

练习答案:

select country, sum(student_count) student_count
from courses c left join teachers t on c.teacher_id = t.id 
where t.id in (
    select id from teachers 
    where country like "U%"
) 
group by t.country
having sum(student_count) between 2000 and 5000
order by student_count desc , country asc

MySQL事务(transaction)

MySQL 为我们提供了以下重要语句来控制事务:

  • 为了启动一个事务,你使用 START TRANSACTION 语句。BEGIN 或 BEGIN WORK 是 START TRANSACTION 的别名。
  • 要提交当前事务并使其变化永久化,你要使用 COMMIT 语句。
  • 要回滚当前事务并取消其变化,你可以使用 ROLLBACK 语句。
  • 要禁用或启用当前事务的自动提交模式,你可以使用 SET autocommit 语句。

默认情况下,MySQL 自动将更改永久性地提交给数据库。要强迫 MySQL 不自动提交更改,你可以使用以下语句:

SET autocommit = 0;
-- OR --
SET autocommit = OFF

锁的认识

锁:计算机协调多个进程或线程并发访问某一资源的机制。

锁的重要性:
在数据库中,除了传统的计算资源(CPU、RAM、I\O等)的争抢,数据也是一种供多用户共享的资源。

因此,如何保证数据并发访问的一致性,有效性,是所有数据库必须要解决的问题。
锁冲突也是影响数据库并发访问性能的一个重要因素,因此锁对数据库尤其重要。

锁的缺点:
加锁是消耗资源的,锁的各种操作,包括:获得锁、检测锁是否已经解除、释放锁等,都会增加系统的开销。
Oracle 数据库的事务隔离级别基本都是读已提交(READ-COMMITTED),所有的锁也针对这个级别来总结

MySQL 默认隔离级别是 Repeatable read(可重复读)

查看自己的数据库的事务隔离级别
命令:

SHOW VARIABLES like '%isolation%';

InnoDB锁的种类之自增锁

查询当前数据库的自增锁模式:

show variables like '%innodb_autoinc_lock_mode%';

在这里插入图片描述

  • 0:traditonal 每次 insert 语句执行都会产生表锁

  • 1:consecutive simple insert 会获得批量的锁,保证一批插入自增序列的连续性,插入之前提前释放锁,在这个模式下你会发现当你 insert 多条数据回滚的时候虽然 DB 没有插入数据,但是自增 ID 已经增长了,也是数据库默认的级别

  • 2:interleaved 不会锁表,实时插入,并发最高,但是基于主从的复制是不安全的,感兴趣可以去查询 RBR 和 SBR 的优缺点

通过查询知道,我的数据库是开启 consecutive 级别,simple insert 表示通过分析 insert 语句可以确定插入的数量;如:insert, insert, insert … valies(), values(), values();相应的其他插入方式还有 Bulk inserts,Mixed-mode inserts 等。

案例可访问这个页面进行查看:https://www.lintcode.com/learn/84/285

InnoDB锁的种类之共享锁和排他锁

  • 并发控制
    提到共享锁和排它锁就不得不提并发控制(Concurrency Control),并发控制可以解决临界资源操作时不一致的情况产生,保证数据一致性常见的手段就是锁和数据多版本(Multi Version)

  • 直接加锁
    这种方式会导致被加锁的资源都被锁住,读取任务也无法执行直到锁释放,所有执行的任务相当于串行化方式,简单粗暴,不能并发

  • 共享锁(Shared Locks)简称为 S 锁
    读取数据时候可以加 S 锁。

共享 (S) 锁允许并发事务读取 (SELECT) 一个资源。资源上存在共享 (S) 锁时,任何其它事务都不能修改数据。一旦已经读取数据,便立即释放资源上的共享 (S) 锁,除非将事务隔离级别设置为可重复读或更高级别,或者在事务生存周期内用锁定提示保留共享 (S) 锁。

  • 排它锁 (Exclusive Locks)简称为 X 锁
    修改数据时候加 X 锁。

排它 (X) 锁可以防止并发事务对资源进行访问。其它事务不能读取或修改排它 (X) 锁锁定的数据。

共享锁之间可以并行,排它锁和共享锁之间互斥,也就是说只要共享锁开启没有释放掉的时候,更新锁是不能抢占的,此时其他读取同资源的操作可以进行读取不受限制;同理排它锁开启时候只要没有释放其他不管是排它锁还是共享锁都不可以抢占资源直到锁释放。

案例讲解:https://www.lintcode.com/learn/84/292

因为锁是使用可能会引发死锁的状态:
死锁
死锁在本文不详细进行讲解,只列举一些可能会造成死锁的诱因

  • 1、如上所述并发修改同一记录
  • 2、事务之间对资源访问(表)顺序的交替进行,与第一条大同小异,上升到锁表的级别
  • 3、数据量庞大时候索引建立机制不行,经常扫全表操作,也会造成资源阻塞死锁
  • 4、愿意在代码里开大事务然后对数据库一顿操作互相等待,容易引发死锁,所以代码里要节俭对事务的开销,以及事务开销时候尽可能有效合理利用资源

数据多版本
上面讲到 X 锁占领后其他 S 锁没法占用导致只要写没完成读就不能进行并发查询,InnoDB 引入了数据多版本概念去解决这一问题

核心原理简单讲就是 clone 了一个版本数据进行修改,比如原有的数据版本号是 Version0,这个时候进行写操作 clone 了一份版本号 V1,这个时候对 V1 版本数据进行修改写入操作;与此同时其他查询读任务并发进来一样可以读取 V0 版本数据不受任何影响,这样就解决了在数据更新回写之前不能读取的问题,进一步提高了数据库引擎处理并发的能力

InnoDB锁的种类之意向锁、记录锁和间隙锁

  • 意向锁(Intention Locks; table-level lock)

意向锁是一种特殊的表级锁,意向锁是为了让 InnoDB 多粒度的锁能共存而设计的。取得行的共享锁和排他锁之前需要先取得表的意向共享锁(IS)和意向排他锁(IX),意向共享锁和意向排他锁都是系统自动添加和自动释放的,整个过程无需人工干预。
意向锁就是指未来的某一个时刻事务可能要加共享锁或者排它锁,提前声明一个意向,分为两种:

  • 意向共享锁(Intention Shared Lock) IS
    事务有意向对表中的某些行加共享锁(S锁)
-- 事务要获取某些行的 S 锁,必须先获得表的 IS 锁。
SELECT column FROM table ... LOCK IN SHARE MODE;
  • 意向排它锁(Intention Exclusive Lock)IX
    事务有意向对表中的某些行加排他锁(X锁)
-- 事务要获取某些行的 X 锁,必须先获得表的 IX 锁。
SELECT column FROM table ... FOR UPDATE;

另:关于InnoDB锁的种类之临键锁和插入意向锁以及临键锁和插入意向锁的浏览地址:https://www.lintcode.com/learn/84/294

如何上锁

MySQL常用存储引擎有 MyISAM 和 InnoDB,而 InnoDB 是 MySQL 默认的引擎。MyISAM 不支持行锁,而 InnoDB 支持行锁和表锁。

  • 隐式上锁(默认,自动加锁、自动释放)
    MyISAM 在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁,这个过程并不需要用户干预,因此用户一般不需要直接用 LOCK TABLE; 命令给 MyISAM 表显式加锁。
-- 上读锁 --
SELECT 
-- 上写锁 --
INSERT, UPDATE, DELETE
  • 显式上锁(手动)
    上共享锁(读锁)的写法:lock in share mode,例如:
select  column_name from table_name where lock in share mode;
上排它锁(写锁)的写法:for update,例如:
select column_name from table_name where conditions for update
  • 解锁(手动)
-- 解锁所有锁表 --
UNLOCK TABLES;

如何加行锁

例如

待后续更新
to be continue。。。。。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/114563.html

(0)

相关推荐

发表回复

登录后才能评论
半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!