基础概念
SQL 语法结构
SQL 语法结构包括:
- 子句 - 是语句和查询的组成成分。(在某些情况下,这些都是可选的。)
- 表达式 - 可以产生任何标量值,或由列和行的数据库表
- 谓词 - 给需要评估的 SQL 三值逻辑(3VL)(true/false/unknown)或布尔真值指定条件,并限制语句和查询的效果,或改变程序流程。
- 查询 - 基于特定条件检索数据。这是 SQL 的一个重要组成部分。
- 语句 - 可以持久地影响纲要和数据,也可以控制数据库事务、程序流程、连接、会话或诊断。
SQL 语法要点
- SQL 语句不区分大小写,但是数据库表名、列名和值是否区分,依赖于具体的 DBMS 以及配置。
- 例如:SELECT 与 select 、Select 是相同的。
- 多条 SQL 语句必须以分号(;)分隔。
- 处理 SQL 语句时,所有空格都被忽略。SQL 语句可以写成一行,也可以分写为多行。
-- 一行 SQL 语句UPDATE user SET username='robot', password='robot' WHERE username = 'root';-- 多行 SQL 语句UPDATE userSET username='robot', password='robot'WHERE username = 'root';
- SQL 支持三种注释
-- 注释2 | |
/* 注释3 */ |
增删改查
增删改查,又称为 CRUD,数据库基本操作中的基本操作。
插入数据
INSERT INTO 语句用于向表中插入新记录。
插入完整的行
INSERT INTO userVALUES (10, 'root', 'root', 'xxxx@163.com');
插入行的一部分
INSERT INTO user(username, password, email)VALUES ('admin', 'admin', 'xxxx@163.com');
插入查询出来的数据
INSERT INTO user(username)SELECT nameFROM account;
更新数据
UPDATE 语句用于更新表中的记录。
UPDATE userSET username='robot', password='robot'WHERE username = 'root';
删除数据
DELETE 语句用于删除表中的记录。
TRUNCATE TABLE 可以清空表,也就是删除所有行。
删除表中的指定数据
DELETE FROM userWHERE username = 'robot';
清空表中的数据
TRUNCATE TABLE user;
查询数据
SELECT 语句用于从数据库中查询数据。
DISTINCT 用于返回唯一不同的值。它作用于所有列,也就是说所有列的值都相同才算相同。
LIMIT 限制返回的行数。可以有两个参数,第一个参数为起始行,从 0 开始;第二个参数为返回的总行数。
ASC :升序(默认)
DESC :降序
查询单列
SELECT prod_nameFROM products;
查询多列
SELECT prod_id, prod_name, prod_priceFROM products;
查询所有列
ELECT *FROM products;
查询不同的值
SELECT DISTINCTvend_id FROM products;
限制查询结果
-- 返回前 5 行SELECT * FROM mytable LIMIT 5;SELECT * FROM mytable LIMIT 0, 5;-- 返回第 3 ~ 5 行SELECT * FROM mytable LIMIT 2, 3;
子查询
子查询是嵌套在较大查询中的 SQL 查询。子查询也称为内部查询或内部选择,而包含子查询的语句也称为外部查询或外部选择。
- 子查询可以嵌套在 SELECT,INSERT,UPDATE 或 DELETE 语句内或另一个子查询中。
- 子查询通常会在另一个 SELECT 语句的 WHERE 子句中添加。
- 您可以使用比较运算符,如 >,<,或 =。比较运算符也可以是多行运算符,如 IN,ANY 或 ALL。
- 子查询必须被圆括号 () 括起来。
- 内部查询首先在其父查询之前执行,以便可以将内部查询的结果传递给外部查询。执行过程可以参考下图:
子查询的子查询
SELECT cust_name, cust_contactFROM customersWHERE cust_id IN (SELECT cust_id FROM orders WHERE order_num IN (SELECT order_num FROM orderitems WHERE prod_id = 'RGAN01'));
WHERE
- WHERE 子句用于过滤记录,即缩小访问数据的范围。
- WHERE 后跟一个返回 true 或 false 的条件。
- WHERE 可以与 SELECT,UPDATE 和 DELETE 一起使用。
- 可以在 WHERE 子句中使用的操作符
- 运算符 描述 = 等于 <> 不等于。注释:在 SQL 的一些版本中,该操作符可被写成 != > 大于 < 小于 >= 大于等于 <= 小于等于 BETWEEN 在某个范围内 LIKE 搜索某种模式 IN 指定针对某个列的多个可能值
SELECT 语句中的 WHERE 子句
SELECT * FROM CustomersWHERE cust_name = 'Kids Place';
UPDATE 语句中的 WHERE 子句
UPDATE CustomersSET cust_name = 'Jack Jones'WHERE cust_name = 'Kids Place';
DELETE 语句中的 WHERE 子句
DELETE FROM CustomersWHERE cust_name = 'Kids Place';
IN 和 BETWEEN
- IN 操作符在 WHERE 子句中使用,作用是在指定的几个特定值中任选一个值。
- BETWEEN 操作符在 WHERE 子句中使用,作用是选取介于某个范围内的值。
IN 示例
SELECT *FROM productsWHERE vend_id IN ('DLL01', 'BRS01');
BETWEEN 示例
SELECT *FROM productsWHERE prod_price BETWEEN 3 AND 5;
AND、OR、NOT
- AND、OR、NOT 是用于对过滤条件的逻辑处理指令。
- AND 优先级高于 OR,为了明确处理顺序,可以使用 ()。
- AND 操作符表示左右条件都要满足。
- OR 操作符表示左右条件满足任意一个即可。
- NOT 操作符用于否定一个条件。
AND 示例
SELECT prod_id, prod_name, prod_priceFROM productsWHERE vend_id = 'DLL01' AND prod_price <= 4;
OR 示例
SELECT prod_id, prod_name, prod_priceFROM productsWHERE vend_id = 'DLL01' OR vend_id = 'BRS01';
NOT 示例
SELECT *FROM productsWHERE prod_price NOT BETWEEN 3 AND 5;
LIKE
- LIKE 操作符在 WHERE 子句中使用,作用是确定字符串是否匹配模式。
- 只有字段是文本值时才使用 LIKE。
- LIKE 支持两个通配符匹配选项:% 和 _。
- 不要滥用通配符,通配符位于开头处匹配会非常慢。
- % 表示任何字符出现任意次数。
- _ 表示任何字符出现一次。
% 示例
SELECT prod_id, prod_name, prod_priceFROM productsWHERE prod_name LIKE '%bean bag%';
_ 示例
SELECT prod_id, prod_name, prod_priceFROM productsWHERE prod_name LIKE '__ inch teddy bear';
连接与组合
连接(JOIN)
如果一个 JOIN 至少有一个公共字段并且它们之间存在关系,则该 JOIN 可以在两个或多个表上工作。
连接用于连接多个表,使用 JOIN 关键字,并且条件语句使用 ON 而不是 WHERE。
JOIN 保持基表(结构和数据)不变。
JOIN 有两种连接类型:内连接和外连接。
内连接又称等值连接,使用 INNER JOIN 关键字。在没有条件语句的情况下返回笛卡尔积。
- 自连接可以看成内连接的一种,只是连接的表示自身而已。
自然连接是把同名列通过 = 测试连接起来的,同名列可以有多个。
内连接 vs 自然连接
- 内连接提供连接的列,而自然连接自动连接所有同名列。
外连接返回一个表中的所有行,并且仅返回来自次表中满足连接条件的那些行,即两个表中的列是相等的。外连接分为左外连接、右外连接、全外连接(Mysql 不支持)。
- 左外连接就是保留左表没有关联的行。
- 右外连接就是保留右表没有关联的行。
连接 vs 子查询
- 连接可以替换子查询,并且比子查询的效率一般会更快。
内连接(INNER JOIN)
SELECT vend_name, prod_name, prod_priceFROM vendors INNER JOIN productsON vendors.vend_id = products.vend_id;
自连接
SELECT c1.cust_id, c1.cust_name, c1.cust_contactFROM customers c1, customers c2WHERE c1.cust_name = c2.cust_nameAND c2.cust_contact = 'Jim Jones';
自然连接(NATURAL JOIN)
SELECT *FROM ProductsNATURAL JOIN Customers;
左连接(LEFT JOIN)
SELECT customers.cust_id, orders.order_numFROM customers LEFT JOIN ordersON customers.cust_id = orders.cust_id;
右连接(RIGHT JOIN)
SELECT customers.cust_id, orders.order_numFROM customers RIGHT JOIN ordersON customers.cust_id = orders.cust_id;
组合(UNION)
UNION 运算符将两个或更多查询的结果组合起来,并生成一个结果集,其中包含来自 UNION 中参与查询的提取行。
UNION 基本规则
- 所有查询的列数和列顺序必须相同。
- 每个查询中涉及表的列的数据类型必须相同或兼容。
- 通常返回的列名取自第一个查询。
默认会去除相同行,如果需要保留相同行,使用 UNION ALL。
只能包含一个 ORDER BY 子句,并且必须位于语句的最后。
应用场景
- 在一个查询中从不同的表返回结构数据。
- 对一个表执行多个查询,按一个查询返回数据。
组合查询
SELECT cust_name, cust_contact, cust_emailFROM customersWHERE cust_state IN ('IL', 'IN', 'MI')UNIONSELECT cust_name, cust_contact, cust_emailFROM customersWHERE cust_name = 'Fun4All';
JOIN vs UNION
- JOIN 中连接表的列可能不同,但在 UNION 中,所有查询的列数和列顺序必须相同。
- UNION 将查询之后的行放在一起(垂直放置),但 JOIN 将查询之后的列放在一起(水平放置),即它构成一个笛卡尔积。
函数
注意:不同数据库的函数往往各不相同,因此不可移植。本节主要以 Mysql 的函数为例。
文本处理
函数 说明 LEFT()、RIGHT() 左边或者右边的字符 LOWER()、UPPER() 转换为小写或者大写 LTRIM()、RTIM() 去除左边或者右边的空格 LENGTH() 长度 SOUNDEX() 转换为语音值
其中, SOUNDEX() 可以将一个字符串转换为描述其语音表示的字母数字模式。
SELECT *FROM mytableWHERE SOUNDEX(col1) = SOUNDEX('apple')
日期和时间处理
日期格式:YYYY-MM-DD
时间格式:HH
SS
函 数 说 明 AddDate() 增加一个日期(天、周等) AddTime() 增加一个时间(时、分等) CurDate() 返回当前日期 CurTime() 返回当前时间 Date() 返回日期时间的日期部分 DateDiff() 计算两个日期之差 Date_Add() 高度灵活的日期运算函数 Date_Format() 返回一个格式化的日期或时间串 Day() 返回一个日期的天数部分 DayOfWeek() 对于一个日期,返回对应的星期几 Hour() 返回一个时间的小时部分 Minute() 返回一个时间的分钟部分 Month() 返回一个日期的月份部分 Now() 返回当前日期和时间 Second() 返回一个时间的秒部分 Time() 返回一个日期时间的时间部分 Year() 返回一个日期的年份部分
mysql> SELECT NOW();2018-4-14 20:25:11
数值处理
函数 说明 SIN() 正弦 COS() 余弦 TAN() 正切 ABS() 绝对值 SQRT() 平方根 MOD() 余数 EXP() 指数 PI() 圆周率 RAND() 随机数
汇总
函 数 说 明 AVG() 返回某列的平均值 COUNT() 返回某列的行数 MAX() 返回某列的最大值 MIN() 返回某列的最小值 SUM() 返回某列值之和
AVG() 会忽略 NULL 行。
使用 DISTINCT 可以让汇总函数值汇总不同的值。
SELECT AVG(DISTINCT col1) AS avg_colFROM mytable
排序与分组
ORDER BY
ORDER BY 用于对结果集进行排序。
- ASC :升序(默认)
- DESC :降序
可以按多个列进行排序,并且为每个列指定不同的排序方式
指定多个列的排序方向
SELECT * FROM productsORDER BY prod_price DESC, prod_name ASC;
GROUP BY
- GROUP BY 子句将记录分组到汇总行中。
- GROUP BY 为每个组返回一个记录。
- GROUP BY 通常还涉及聚合:COUNT,MAX,SUM,AVG 等。
- GROUP BY 可以按一列或多列进行分组。
- GROUP BY 按分组字段进行排序后,ORDER BY 可以以汇总字段来进行排序。
分组
SELECT cust_name, COUNT(cust_address) AS addr_numFROM Customers GROUP BY cust_name;
分组后排序
SELECT cust_name, COUNT(cust_address) AS addr_numFROM Customers GROUP BY cust_nameORDER BY cust_name DESC;
HAVING
HAVING 用于对汇总的 GROUP BY 结果进行过滤。
HAVING 要求存在一个 GROUP BY 子句。
WHERE 和 HAVING 可以在相同的查询中。
HAVING vs WHERE
- WHERE 和 HAVING 都是用于过滤。
- HAVING 适用于汇总的组记录;而 WHERE 适用于单个记录。
使用 WHERE 和 HAVING 过滤数据
SELECT cust_name, COUNT(*) AS numFROM CustomersWHERE cust_email IS NOT NULLGROUP BY cust_nameHAVING COUNT(*) >= 1;
数据定义
DDL 的主要功能是定义数据库对象(如:数据库、数据表、视图、索引等)。
数据库(DATABASE)
创建数据库
CREATE DATABASE test;
删除数据库
DROP DATABASE test;
选择数据库
USE test;
数据表(TABLE)
创建数据表
普通创建
CREATE TABLE user ( id int(10) unsigned NOT NULL COMMENT 'Id', username varchar(64) NOT NULL DEFAULT 'default' COMMENT '用户名', password varchar(64) NOT NULL DEFAULT 'default' COMMENT '密码', email varchar(64) NOT NULL DEFAULT 'default' COMMENT '邮箱') COMMENT='用户表';
根据已有的表创建新表
CREATE TABLE vip_user ASSELECT * FROM user;
删除数据表
DROP TABLE user;
修改数据表
添加列
ALTER TABLE userADD age int(3);
删除列
ALTER TABLE userDROP COLUMN age;
修改列
ALTER TABLE `user`MODIFY COLUMN age tinyint;
添加主键
ALTER TABLE userADD PRIMARY KEY (id);
删除主键
ALTER TABLE userDROP PRIMARY KEY;
视图(VIEW)
定义
- 视图是基于 SQL 语句的结果集的可视化的表。
- 视图是虚拟的表,本身不包含数据,也就不能对其进行索引操作。对视图的操作和对普通表的操作一样。
作用
- 简化复杂的 SQL 操作,比如复杂的联结;
- 只使用实际表的一部分数据;
- 通过只给用户访问视图的权限,保证数据的安全性;
- 更改数据格式和表示。
创建视图
CREATE VIEW top_10_user_view ASSELECT id, usernameFROM userWHERE id < 10;
删除视图
DROP VIEW top_10_user_view;
索引(INDEX)
作用
- 通过索引可以更加快速高效地查询数据。
- 用户无法看到索引,它们只能被用来加速查询。
注意
- 更新一个包含索引的表需要比更新一个没有索引的表花费更多的时间,这是由于索引本身也需要更新。因此,理想的做法是仅仅在常常被搜索的列(以及表)上面创建索引。
唯一索引
- 唯一索引表明此索引的每一个索引值只对应唯一的数据记录。
创建索引
CREATE INDEX user_indexON user (id);
创建唯一索引
CREATE UNIQUE INDEX user_indexON user (id);
删除索引
ALTER TABLE userDROP INDEX user_index;
约束
SQL 约束用于规定表中的数据规则。
如果存在违反约束的数据行为,行为会被约束终止。
约束可以在创建表时规定(通过 CREATE TABLE 语句),或者在表创建之后规定(通过 ALTER TABLE 语句)。
约束类型
- NOT NULL - 指示某列不能存储 NULL 值。
- UNIQUE - 保证某列的每行必须有唯一的值。
- PRIMARY KEY - NOT NULL 和 UNIQUE 的结合。确保某列(或两个列多个列的结合)有唯一标识,有助于更容易更快速地找到表中的一个特定的记录。
- FOREIGN KEY - 保证一个表中的数据匹配另一个表中的值的参照完整性。
- CHECK - 保证列中的值符合指定的条件。
- DEFAULT - 规定没有给列赋值时的默认值。
创建表时使用约束条件:
CREATE TABLE Users ( | |
Id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增Id', | |
Username VARCHAR(64) NOT NULL UNIQUE DEFAULT 'default' COMMENT '用户名', | |
Password VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '密码', | |
Email VARCHAR(64) NOT NULL DEFAULT 'default' COMMENT '邮箱地址', | |
Enabled TINYINT(4) DEFAULT NULL COMMENT '是否有效', | |
PRIMARY KEY (Id) | |
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; |
事务处理
不能回退 SELECT 语句,回退 SELECT 语句也没意义;也不能回退 CREATE 和 DROP 语句。
MySQL 默认是隐式提交,每执行一条语句就把这条语句当成一个事务然后进行提交。当出现 START TRANSACTION 语句时,会关闭隐式提交;当 COMMIT 或 ROLLBACK 语句执行后,事务会自动关闭,重新恢复隐式提交。
通过 set autocommit=0 可以取消自动提交,直到 set autocommit=1 才会提交;autocommit 标记是针对每个连接而不是针对服务器的。
指令
- START TRANSACTION - 指令用于标记事务的起始点。
- SAVEPOINT - 指令用于创建保留点。
- ROLLBACK TO - 指令用于回滚到指定的保留点;如果没有设置保留点,则回退到 START TRANSACTION 语句处。
- COMMIT - 提交事务。
-- 开始事务 | |
START TRANSACTION; | |
-- 插入操作 A | |
INSERT INTO `user`VALUES (1, 'root1', 'root1', 'xxxx@163.com'); | |
-- 创建保留点 updateA | |
SAVEPOINT updateA; | |
-- 插入操作 B | |
INSERT INTO `user`VALUES (2, 'root2', 'root2', 'xxxx@163.com'); | |
-- 回滚到保留点 updateA | |
ROLLBACK TO updateA; | |
-- 提交事务,只有操作 A 生效COMMIT; |