MySQL 学习笔记

存储引擎

MySQL 可以将数据以不同的技术存储在文件(内存)中,这种技术就称为存储引擎。
每种存储引擎使用不同的存储机制/索引技巧/锁定水平。

索引 是对数据表中一列或多列的值进行排序的一种结构。

常见引擎

  • MyISAM
  • InnoDB
  • Memory
  • CSV
  • Archive
  • BlackHole

各种存储引擎的特点

特点 MyISAM InnoDB Memory Archive
存储限制 256TB 64TB
事务 - 支持 - -
索引 支持 支持 支持 -
锁颗粒 表锁 行锁 表锁 行锁
压缩 支持 - - 支持
外键 - 支持 - -

  • 共享锁(读锁)
  • 排他锁(写锁)

锁颗粒

  • 表锁,一种开销最小的锁策略
  • 行锁,一种开销最大的锁策略

配置

修改存储引擎

  • 修改mysql.ini,default-storage-engine = engine
  • 创建数据表时通过 ENGINE = engine 指定,或ALTER TABLE tp1 ENGINE = engine

事务

原子性/ 一致性/ 隔离性/ 持久性 简称ACID
用于保证数据库的完整性 (常见的银行转账逻辑)

查看表创建语句

SHOW CREATE TBALE xxx

函数

  • 字符函数
-- CONCAT/CONCAT_WS  字符串连接
-- FORMAT 格式化
-- LOWER/UPPER  大小写
-- LEFT/RIGHT(str,len)  字符串左右截取
-- SUBSTRING(str, start, [len]) 字符串截取,索引从1开始
-- LENGTH  字符串长度
-- LTRIM/RTRIM  删除左/右前/后导空格 LEADING 前导,TRAILING 后序,BOTH 前后空格
SELECT TRIM(LEADING '?' FROM '??MySQL???');  -- MySQL???
-- LIKE 模糊查询 匹配带%字段时候 1后面表示不在是通配符
SELECT * FROM username WHERE name LIKE '%1%%' ESCAPE '1';
SELECT * FROM username WHERE name LIKE '%\%%';
-- REPLACE  替换
SELECT REPLACE('??My??SQL???', '??', '!'); -- !My!SQL!?
  • 数值运算符与函数
-- CEIL 向上取整
-- DIV 除法取整
-- FLOOR 向下取整
-- MOD 取余
-- POWER 幂运算
-- ROUND 四舍五入
-- TRUNCATE 数字截取
SELECT TRUNCATE(123.89, -1) -- 120
  • 比较运算符与函数
[NOT] BETWEEN ... AND ... 在范围内
[NOT] IN() 在给出的范围内
IS [NOT] NULL 为空
  • 日期时间函数
-- NOW() 当前日期和时间
-- CURDATE/CURTIME 当前日期/当前时间
-- DATE_ADD() 日期变化
SELECT DATE_ADD('2017-4-1', INTERVAL -365 DAY);
-- DATEDIFF() 日期差值
SELECT DATE_ADD('2017-4-1', '2017-5-1');
-- DATE_FORMAT() 日期格式化
SELECT DATE_FORMAT('2017-4-1', '%m/%d/%Y');
-- UNIX_TIMESTAMP() date类型转换为timestamp形式整数
-- FROM_UNIXTIME() 跟上面相反
  • 信息函数
-- CONNECTION_ID() 连接ID
-- DATEBASE() 当前数据库
-- LAST_INSERT_ID 最后插入的数据的id,多条插入只返回第一条最后操作id
-- USER() 当前登陆用户
-- VERSION() mysql版本号
  • 聚合函数
-- AVG 平均值
-- COUNT 计数
-- MIN/MAX 最小/大值
-- SUM 求和
  • 加密函数
-- MD5() 信息摘要算法, 常用于保存密码
-- PASSWORD() 修改用户密码
SET PASSWORD=PASSWORD('xxxxx');

自定义函数

-- 不带参数
CREATE FUNCTION fun1() RETURNS VARCHAR(30) RETURN DATE_FORMAT(NOW(), '%Y年%m月%d日 %H点%i分%s秒');
SELECT fun1();
-- 带参数函数
CREATE FUNCTION fun2(num1 SMALLINT UNSIGNED, num2 SMALLINT UNSIGNED) RETURNS FLOAT(10, 2) UNSIGNED RETURN (num1 + num2)/2;
SELECT fun2(5, 6);
-- 符合结构, 多个参数
CREATE FUNCTION adduser(username VARCHAR(20)) RETURNS INT UNSIGNED RETURN INSERT test(username) VALUES(username) RETURN LAST_INSERT_ID();END;
-- 删除函数
DROP FUNCTION xxx;

EXPLAIN 性能分析语句

  • type = const 表示通过索引一次就找到了;
  • key = primary 的话,表示使用了主键;
  • key = null 表示没用到索引。
  • type = all 表示为全表扫描;
  • type = ref 因为这时认为是多个匹配行,在联合查询中,一般为REF

比较查询

SELECT round(AVG(goods_price),2) FROM `tdb_goods`
SELECT goods_id,goods_name,goods_price from tdb_goods where goods_price >= 5391.30;

-- 精简为
SELECT goods_id,goods_name,goods_price from `tdb_goods` WHERE goods_price >= (SELECT ROUND(AVG(goods_price), 2) FROM `tdb_goods`);

ANY SOME 满足其中一个就行
ALL 满足所有结果
比如 goods_price 子查询结果最低 2899

SELECT goods_id,goods_name,goods_price from tdb_goods where goods_price >= ANY(SELECT goods_price FROM tdb_goods WHERE goods_cate='台式机');

其他 [NOT] IN,[NOT] EXISTS

-- 查询分类个数
SELECT goods_cate FROM tdb_goods GROUP BY goods_cate;

-- 将查询的结果存储到表中
INSERT INTO tdb_goods_cates(cate_name) SELECT goods_cate FROM tdb_goods GROUP BY goods_cate;

多表查询

更新

UPDATE tdb_goods INNER JOIN tdb_goods_cates ON goods_cate = cate_name SET goods_cate = cate_id;

查询并新建表,一步到位

CREATE TABLE IF NOT EXISTS tdb_goods_brands(
    brand_id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    brand_name VARCHAR(40) NOT NULL
) SELECT brand_name FROM tdb_goods GROUP BY brand_name;

UPDATE tdb_goods INNER JOIN tdb_goods_brands ON tdb_goods.brand_name=tdb_goods_brands.brand_name SET tdb_goods.brand_name=tdb_goods_brands.brand_id;

-- 修改表字段名和类型
AlTER TABLE tdb_goods CHANGE goods_cate cate_id SMALLINT UNSIGNED NOT NULL,CHANGE brand_name brand_id SMALLINT UNSIGNED NOT NULL;

连接

ON后接条件,而 WHERE 一般用于结果集的过滤

  • 内连接 INNER JOIN (JOIN / CROSS JOIN)

仅显示左表及右表符合连接条件的记录

SELECT
    goods_id,
    goods_name,
    cate_name
FROM
    tdb_goods
INNER JOIN tdb_goods_cates ON tdb_goods.cate_id = tdb_goods_cates.cate_id;
  • 外连接

左外连接 LEFT JOIN
左表全部,右表符合条件的部分
右外连接 LEFT JOIN
右表全部,左表符合条件的部分

  • 多表连接
SELECT
    goods_id,
    goods_name,
    cate_name,
    brand_name,
    goods_price
FROM
    tdb_goods AS g
INNER JOIN tdb_goods_cates AS c ON g.cate_id = c.cate_id
INNER JOIN tdb_goods_brands AS b ON g.brand_id = b.brand_id

无限分类逻辑

一般是这样的结构: 分类id 分类名字 父类id

CREATE TABLE tdb_goods_types(
    type_id   SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    type_name VARCHAR(20) NOT NULL,
    parent_id SMALLINT UNSIGNED NOT NULL DEFAULT 0
); 
-- 插入数据
INSERT tdb_goods_types(type_name,parent_id) VALUES('家用电器',DEFAULT);
INSERT tdb_goods_types(type_name,parent_id) VALUES('电脑、办公',DEFAULT);
INSERT tdb_goods_types(type_name,parent_id) VALUES('大家电',1);
INSERT tdb_goods_types(type_name,parent_id) VALUES('生活电器',1);
INSERT tdb_goods_types(type_name,parent_id) VALUES('平板电视',3);
INSERT tdb_goods_types(type_name,parent_id) VALUES('空调',3);
INSERT tdb_goods_types(type_name,parent_id) VALUES('电风扇',4);
INSERT tdb_goods_types(type_name,parent_id) VALUES('饮水机',4);
INSERT tdb_goods_types(type_name,parent_id) VALUES('电脑整机',2);
INSERT tdb_goods_types(type_name,parent_id) VALUES('电脑配件',2);
INSERT tdb_goods_types(type_name,parent_id) VALUES('笔记本',9);
INSERT tdb_goods_types(type_name,parent_id) VALUES('超级本',9);
INSERT tdb_goods_types(type_name,parent_id) VALUES('游戏本',9);
INSERT tdb_goods_types(type_name,parent_id) VALUES('CPU',10);
INSERT tdb_goods_types(type_name,parent_id) VALUES('主机',10);

-- 自连接,数据表自己连接自己

-- 查找所有分类及其父类(以子类为基础找父类)
SELECT s.type_id,s.type_name,p.type_name FROM tdb_goods_types AS s LEFT JOIN tdb_goods_types AS  p ON s.parent_id = p.type_id;
   
-- 查找所有分类及其子类(以父类为基础找子类)
SELECT p.type_id,p.type_name,s.type_name FROM tdb_goods_types AS p LEFT JOIN tdb_goods_types AS  s ON s.parent_id = p.type_id;

-- 查找所有分类及其子类的数目
SELECT p.type_id,p.type_name,count(s.type_name) AS children_count FROM tdb_goods_types AS p LEFT JOIN tdb_goods_types AS s ON s.parent_id = p.type_id GROUP BY p.type_name ORDER BY p.type_id;

面试题:

CREATE TABLE IF NOT EXISTS province(
    id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    province VARCHAR(32) NOT NULL
);
INSERT province(id, province) VALUES (NULL,"广东"),(NULL,"湖南"),(NULL,"湖北");

CREATE TABLE IF NOT EXISTS city(
    id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    city VARCHAR(32) NOT NULL,
    province_id SMALLINT UNSIGNED NOT NULL
);
INSERT city(id, city, province_id) VALUES (NULL,"广州",1),(NULL,"深圳",1),(NULL,"惠州",1),(NULL,"长沙",2),(NULL,"武汉",3),(NULL,"黄冈",3);

-- 1. 写一条sql语句关联两个表,实现:显示城市基本信息,显示字段:城市id,城市名,所属省份 
SELECT
    c.id AS '城市Id',
    c.city AS '城市名',
    p.province AS '所属省份'
FROM
    city AS c
LEFT JOIN province AS p ON c.province_id = p.id;

-- 2. 统计每个省会有多少个城市,从多到少。
SELECT
    p.province AS '省会名',
    count(c.city)  AS '城市数'
FROM
    province AS p
LEFT JOIN city AS c ON c.province_id = p.id
GROUP BY
    p.province
ORDER BY
    count(c.city) DESC;

多表删除

-- 分组查看可以看到重复的数据
SELECT goods_id, goods_name FROM tdb_goods GROUP BY goods_name HAVING COUNT(goods_name) > 1 ORDER BY goods_id;

-- 查询重复的数据
SELECT t1.goods_id, t1.goods_name
FROM tdb_goods AS t1
LEFT JOIN (
    SELECT goods_id, goods_name ROM tdb_goods
    GROUP BY goods_name
    HAVING COUNT(goods_name) > 1
) AS t2 ON t1.goods_name = t2.goods_name
WHERE t1.goods_id > t2.goods_id;

存储过程

存储过程 是SQL语句和控制语句的预编译集合,以一个名称存储并最为一个单元处理。

创建存储过程
CREATE ... PROCEDURE 过程名() 过程体;
调用存储过程
CALL 过程名
查看状态
SHOW STATUS
查看存储过程内容
SHOW CREATE {PROCEDURE|FUNCTION} sp_name

  • 增强了SQL语句功能和灵活性
  • 实现较快的执行速度
  • 减少网络流量

注意: 在命令行创建存储过程的是hi需要通过DELIMITER 语句修改定界符。

参数

  • IN 表示该参数的值必须在调用存储过程时指定
  • OUT 表示该参数的值可以被存储过程改变,且可以返回,类似php方法的引用参数
  • INOUT 表示该参数的调用时制定,且可以被改变和返回

过程体
过程提由合法的SQL语句构成
可以是任意SQL语句(不包括创建表)
如果是复合机构,需使用BEGIN ... END语句
复合结构可以包含声明,循环,控制结构

-- 数据准备
CREATE TABLE users(
    id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(32) NOT NULL,
    password VARCHAR(32) NOT NULL,
    age SMALLINT UNSIGNED DEFAULT 0,
    sex SMALLINT UNSIGNED DEFAULT 0
);
insert users(username,password,age,sex) values('A',md5('A'),20,0);
insert users(username,password,age,sex) values('B',md5('B'),23,1);
insert users(username,password,age,sex) values('C',md5('C'),23,1);
insert users(username,password,age,sex) values('D',md5('D'),24,1);
insert users(username,password,age,sex) values('E',md5('E'),24,0);
insert users(username,password,age,sex) values('F',md5('F'),23,0);
insert users(username,password,age,sex) values('G',md5('G'),22,0);
insert users(username,password,age,sex) values('H',md5('H'),23,0);
insert users(username,password,age,sex) values('I',md5('I'),23,0);
insert users(username,password,age,sex) values('J',md5('J'),22,1);
insert users(username,password,age,sex) values('K',md5('K'),22,1);
insert users(username,password,age,sex) values('L',md5('L'),22,0);
insert users(username,password,age,sex) values('M',md5('M'),24,1);
insert users(username,password,age,sex) values('N',md5('N'),21,0);
insert users(username,password,age,sex) values('O',md5('O'),20,0);
insert users(username,password,age,sex) values('P',md5('P'),20,1);
insert users(username,password,age,sex) values('Q',md5('Q'),24,1);
insert users(username,password,age,sex) values('R',md5('R'),24,1);

-- 不带参数
CREATE PROCEDURE p1() SELECT VERSION();
CALL p1;
-- 带参数 根据id删除数据,注意参数避免与字段名重复!
CREATE PROCEDURE removeUser(IN removeId INT UNSIGNED)
BEGIN
    DELETE FROM users WHERE id = removeId;
END
CALL removeUser(10); -- 成功
-- 删除并返回影响的行数和当前数据条数
DROP PROCEDURE IF EXISTS removeUser2;
CREATE PROCEDURE removeUser2(IN removeId SMALLINT UNSIGNED, OUT affected SMALLINT UNSIGNED, OUT num SMALLINT UNSIGNED)
BEGIN
    DELETE FROM users WHERE id = removeId;
    SELECT ROW_COUNT() INTO affected;
    SELECT COUNT(id) FROM users INTO num;
END
CALL removeUser2(12, @affected, @num);
SELECT @affected, @num;

存储过程和自定义函数的区别

  • 存储过程适合更复杂的功能,而函数的针对性更强。
  • 存储过程可返回多个值,而函数只有一个
  • 存储过程一般独立执行,而函数可以作为其他SQL语句调用

你可能感兴趣的:(MySQL 学习笔记)