文章目录
数据库笔记。
学号 | 姓名 | 班级 | 班主任 | 课程 | 分数 |
---|---|---|---|---|---|
001 | 萧炎 | 一班 | 药老 | 青莲地心火 | 100 |
001 | 萧炎 | 一班 | 药老 | 净莲妖火 | 100 |
002 | 林动 | 二班 | 天蚕土豆 | 通背拳 | 100 |
002 | 林动 | 二班 | 天蚕土豆 | 大荒囚天指 | 100 |
003 | 谢文东 | 三班 | 六道 | 坏蛋是怎样炼成的 | 100 |
如上表格:
1.事务
2.事务四大特性
3.四个特性的关系
1.1第一类丢失更新(回滚导致丢失)。T1回滚导致T2提交的修改丢失了。
T1 T2
修改var
修改var
回滚
1.2第二类丢失更新(覆盖导致丢失)。T1对var的修改被T2提交的修改覆盖了,这是不可重复读的一种特殊情况。
T1 T2
修改var
修改var
读取var
2.脏读,又称无效数据读出。一个事务读取另外一个事务还没有提交的数据叫脏读。
T1 T2
修改var
读取var
回滚
3.不可重复读,一个事务读取了另一个事务提交的数据。
T1 T2
读取var
修改var
读取var
4.幻读,读取到了不存在的数据。
T1 T2
范围读取数据
插入一条数据
范围读取数据
数据库事务的隔离级别有4个,依次递增:
隔离级别 | 脏读 | 不可重复读 | 幻影读 | 丢失更新 | 第二类丢失更新 |
---|---|---|---|---|---|
未提交读 | √ | √ | √ | x | x |
提交读 | × | √ | √ | x | x |
可重复读 | × | × | √ | x | x |
可串行化 | × | × | × | x | x |
PS:我的理解是未提交读采用一级封锁协议就已经解决了丢失更新问题,但是网上很多人的总结”提交读”级别还存在第二类丢失更新问题?
Mysql提供两种封锁粒度,分别是行级锁和表级锁。
当访问数据的时候,尽量加锁小的范围可以减少性能的损耗,所以加锁的时候需要在锁开销和并发程度之间做一个平衡。
如果事务T1对数据A加了共享锁,T2可以对数据A再加共享锁,但是不能再加排他锁。
如果事务T2对数据A加了排它锁,T2不可以再加共享锁或排它锁。
两者的兼容关系如下:
- | S | X |
---|---|---|
S | √ | × |
X | × | × |
1.如果事务T1对表的第5行加了排它锁,T2又想对表加排它锁/共享锁,T2需要检查两件事:
在判断第二点的时候,需要遍历表中的每一行才能判断,这样的开销太大了。如果能给表加一个标志,用来标志表内某一行是否有排它锁。这样就产生了意向锁。
2.意向锁分为以下两种:
事务T在获取某行的S锁之前,需要先获取该表的IS锁或者IX锁。
事务T在获取某行的X锁之前,需要先获取该表的IX锁。
PS:S和X是我们要手动去加的,IS和IX是数据库根据我们的操作自动去加的。
3.意向锁只是一个标志,表示表上已经加了或即将要加共享锁/排它锁,所以意向锁之间相互兼容:
- | IS | IX |
---|---|---|
IS | √ | √ |
IX | √ | √ |
4.意向锁和表级的共享锁排它锁之间的兼容关系如下:
- | IS | IX |
---|---|---|
S | √ | x |
X | x | x |
InnoDB会根据隔离级别在合适的时候进行加锁和解锁,这是隐式锁定。我们也可以通过Sql语句显示加锁。
SELECT ... LOCK IN SHARE MODE; #添加共享锁
SELECT ... FOR UPDATE; #添加排它锁
了解读写锁和意向锁的概念后,何时加锁何时解锁还是个问题。有三级封锁协议,分别对应几种隔离级别。
一级封锁协议要求修改数据时必须要加锁,能够避免丢失修改问题,对应”未提交读”隔离级别。
T1 | T2 |
---|---|
lockX(var) | |
修改var | lockX(var) |
提交 | 阻塞 |
unlockX(var) | … |
获得锁 | |
修改var | |
提交 | |
unlockX(var) |
二级封锁协议要求在一级封锁协议基础上再加一条:
这样事务T1在写数据时已经加了X锁,事务T2想要读取数据在加S锁时会阻塞,能够避免脏读问题。这对应了“提交读”的隔离级别。
T1 | T2 |
---|---|
lockX(var) | |
修改var | lockS(var) |
回滚 | 阻塞 |
unlockX(var) | … |
获得锁 | |
读取var | |
提交 | |
unlockS(var) |
三级封锁协议要求在一级封锁协议的基础上再加一条:
二级封锁协议在读取结束后就释放S锁,还会存在不可重复读问题:事务T2对数据加S锁,读取结束释放S锁。事务T1加X锁修改数据,T1结束释放X锁。事务T2又加S锁重复读取数据,这时T2在一个事务里就两次读到了不同的数据。
三级封锁协议要求在事务结束后释放S锁,就避免了读数据过程中其他事务来修改数据。解决了不可重复读的问题,对应”可重复读”隔离级别。
疑问:三级封锁协议并不能阻止幻读,修改的不能再被读取,但是新增(删除)的记录数可以统计。为什么三级封锁协议不能阻止幻读,insert和delete操作是如何加锁的?
T1 | T2 |
---|---|
lockS(var) | |
读取var | |
lockX(var) | |
阻塞 | |
读取var | |
提交 | |
unlockS(var) | |
获得锁 | |
修改var | |
提交 | |
unlockX(var) |
该协议要求一个事务分为加锁(生长阶段)和解锁(衰退阶段)两个阶段,也就是在一个事务里,任何一个加锁都在解锁前面。
事务满足两阶段封锁协议>>串行化调度,反之就不一定了。该协议可以满足”可串行化”隔离级别。
xxx
判断mysql是否启动
ps -ef | grep mysqld
启动mysql服务器
cd /usr/bin
./mysqld_safe &
关闭mysql服务器
cd /usr/bin
./mysqladmin -u root -p shutdown
Enter password: ******
启动mysql终端
mysql -u root -p
退出mysql终端
mysql>exit
#sql语句创建
CREATE DATABASE 库名;
#命令行直接创建
mysqladmin -u root -p create 库名
DROP DATABASE 库名;
#命令行删除
mysqladmin -u root -p drop 库名;
SHOW DATABASES;
USE 库名;
SHOW TABLE STATUS LIKE '表名';
ALTER TABLE 表名 ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `test_tbl`(
`test_id` INT UNSIGNED AUTO_INCREMENT,
`test_title` VARCHAR(100) NOT NULL,
`test_author` VARCHAR(40) NOT NULL,
`submission_date` DATE,
PRIMARY KEY ( `test_id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
============================================================
如果你不想字段为 **NULL** 可以设置字段的属性为 **NOT NULL**, 在操作数据库时如果输入该字段的数据为**NULL** ,就会报错。
AUTO_INCREMENT定义列为自增的属性,一般用于主键,数值会自动加1。
PRIMARY KEY关键字用于定义列为主键。 您可以使用多列来定义主键,列间以逗号分隔。
ENGINE 设置存储引擎,CHARSET 设置编码。
SHOW COLUMNS FROM test;
DROP TABLE 表名;
INSERT INTO test_tbl
(test_title,test_author,submission_date)
VALUES
("study sql","YIFEI",NOW());
#SELECT语法
SELECT column_name,column_name
FROM table_name
[WHERE Clause]
[LIMIT N][ OFFSET M]
============================================
查询语句中你可以使用一个或者多个表,表之间使用逗号(,)分割,并使用WHERE语句来设定查询条件。
SELECT 命令可以读取一条或者多条记录。
你可以使用星号(*)来代替其他字段,SELECT语句会返回表的所有字段数据
你可以使用 WHERE 语句来包含任何条件。
你可以使用 LIMIT 属性来设定返回的记录数。
你可以通过OFFSET指定SELECT语句开始查询的数据偏移量。默认情况下偏移量为0。
SELECT * FROM 表名;
SELECT column1,column2 FROM 表名;
SELECT DISTINCT column1,coulumn2 FROM 表名;
单表
SELECT test_author,test_id FROM test_tbl
WHERE BINARY test_title='study php' AND
BINARY test_author='YIFEI'
=======================================
多个条件用AND、OR连接。
=检测是否相等,<>、!=检测不等,其他还有<=、>=。
BINARY表示区分大小写。
WHERE也可以用于DELETE 或者 UPDATE中。
多表
UPDATE test_tbl
SET test_title='study cpp',test_author='tws'
WHERE test_id=2;
DELETE FROM test_tbl WHERE test_id=3;
SELECT * FROM test_2 WHERE submission_date LIKE "%08-26";
SELECT submission_date FROM test_2
UNION all
SELECT submission_date FROM test_tbl
ORDER BY submission_date;
====================================
all是显示所有数据,默认为distinct去重。
两个select查询的列数应当相同。
ORDER BY submission_date是按照该列排序。
SELECT country, name FROM Websites
WHERE country='CN'
UNION ALL
SELECT country, app_name FROM apps
WHERE country='CN'
ORDER BY country;
SELECT * FROM test_tbl ORDER BY name DESC,date ASC;
===========================================================
ASC升序排列,DESC为降序排列。
如果排序列为汉字,想要按照拼音排序:ORDER BY CONVERT(title using gbk);
SELECT name,COUNT(*) FROM employee_tbl GROUP BY name;
======================================================
按照名字分组。
count(*)统计每类的数量。
SELECT coalesce(name, '总数'), SUM(singin) as singin_count FROM employee_tbl GROUP BY name WITH ROLLUP;
+--------------------------+--------------+
| coalesce(name, '总数') | singin_count |
+--------------------------+--------------+
| 小丽 | 2 |
| 小明 | 7 |
| 小王 | 7 |
| 总数 | 16 |
+--------------------------+--------------+
4 rows in set (0.01 sec)
==================================================
coalesce(a,b,c)如果a!=null就使用a作为该列名称,如上如果name为空,就使用"总数"作列名。
ifnull(a,b)跟coalesce一样的效果。
WITH ROLLUP之后可以对分组结果再统计SUM(singin)。
SUM之外还有AVG、COUNT等。
内连接
SELECT a.runoob_id,a.runoob_title,b.runoob_author
FROM runoob_2 AS a INNER JOIN runoob_tbl AS b
ON BINARY a.runoob_author=b.runoob_author;
内连接是取两表的交集,上面语句等价于以下WHERE语句:
SELECT a.runoob_id,a.runoob_title,b.runoob_author
FROM runoob_2 AS a,runoob_tbl AS b
WHERE BINARY a.runoob_author=b.runoob_author;
左连接
SELECT a.runoob_id,a.runoob_title,b.runoob_author
FROM runoob_2 AS a LEFT JOIN runoob_tbl AS b
ON BINARY a.runoob_author=b.runoob_author;
左连接会选取左表全部,输出右表满足要求的。
右连接
SELECT a.runoob_id,a.runoob_title,b.runoob_author
FROM runoob_2 AS a RIGHT JOIN runoob_tbl AS b
ON BINARY a.runoob_author=b.runoob_author;
在WHERE子句中判断某值为空时不能WHERE name=null,应当使用如下方式:
SELECT * FROM test WHERE name IS NULL;
=============================================
**IS NULL:** 当列的值是 NULL,此运算符返回 true。
**IS NOT NULL:** 当列的值不为 NULL, 运算符返回 true。
**<=>:** 比较操作符(不同于 = 运算符),当比较的的两个值相等或者都为 NULL 时返回 true。
SELECT * FROM runoob_2 WHERE runoob_author REGEXP '^y';
模式 | 描述 |
---|---|
^ | 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 ‘\n’ 或 ‘\r’ 之后的位置。 |
$ | 匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 ‘\n’ 或 ‘\r’ 之前的位置。 |
. | 匹配除 “\n” 之外的任何单个字符。要匹配包括 ‘\n’ 在内的任何字符,请使用像 ‘[.\n]’ 的模式。 |
[…] | 字符集合。匹配所包含的任意一个字符。例如, ‘[abc]’ 可以匹配 “plain” 中的 ‘a’。 |
[^…] | 负值字符集合。匹配未包含的任意字符。例如, ‘[^abc]’ 可以匹配 “plain” 中的’p’。 |
p1|p2|p3 | 匹配 p1 或 p2 或 p3。例如,’z|food’ 能匹配 “z” 或 “food”。’(z|f)ood’ 则匹配 “zood” 或 “food”。 |
* | 匹配前面的子表达式零次或多次。例如,zo 能匹配 “z” 以及 “zoo”。 等价于{0,}。 |
+ | 匹配前面的子表达式一次或多次。例如,’zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。 |
{n} | n 是一个非负整数。匹配确定的 n 次。例如,’o{2}’ 不能匹配 “Bob” 中的 ‘o’,但是能匹配 “food” 中的两个 o。 |
{n,m} | m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。 |
设置隔离级别
查看当前会话隔离级别:
select @@tx_isolation;
查看系统的隔离级别:
select @@global.tx_isolation;
设置会话的隔离级别,隔离级别由低到高设置依次为:
set session transaction isolation level read uncommitted;
set session transaction isolation level read committed;
set session transaction isolation level repeatable read;
set session transaction isolation level serializable;
设置当前系统的隔离级别,隔离级别由低到高设置依次为:
set global transaction isolation level read uncommitted;
set global transaction isolation level read committed;
set global transaction isolation level repeatable read;
set global transaction isolation level serializable;
事务控制语句
1、用 BEGIN, ROLLBACK, COMMIT来实现
BEGIN 开始一个事务
ROLLBACK 事务回滚
COMMIT 事务确认
2、直接用 SET 来改变 MySQL 的自动提交模式:
SET AUTOCOMMIT=0 禁止自动提交
SET AUTOCOMMIT=1 开启自动提交
3、保存点
SAVEPOINT identifier,SAVEPOINT 允许在事务中创建一个保存点,一个事务中可以有多个 SAVEPOINT;
RELEASE SAVEPOINT identifier 删除一个事务的保存点,当没有指定的保存点时,执行该语句会抛出一个异常;
ROLLBACK TO identifier 把事务回滚到标记点;
SHOW COLUMNS FROM testalter_tbl;
ALTER TABLE runoob_1
ADD i INT AFTER runoob_id;
可以将AFTER runoob_id改为FIRST,插入到第一列,默认是末尾。
ALTER TABLE testalter_tbl
DROP i;
ALTER TABLE testalter_tbl
MODIFY c CHAR(10);
#修改字段位置
ALTER TABLE test
MODIFY name1 type1 AFTER name2;
ALTER TABLE testalter_tbl
CHANGE i j BIGINT;
CHANGE 关键字之后,紧跟着的是你要修改的字段名,然后指定新字段名及类型。
ALTER TABLE testalter_tbl
ALTER i SET DEFAULT 1000;
ALTER TABLE testalter_tbl
RENAME TO alter_tbl;
SHOW TABLE STATUS LIKE 'testalter_tbl'
ALTER TABLE testalter_tbl
ENGINE = MYISAM;
ALTER TABLE talbename
DROP FOREIGN KEY keyname;
SHOW INDEX FROM table_name;
#添加普通索引,索引值可出现多次。
ALTER TABLE tbl_name ADD INDEX index_name (column_list);
#添加唯一索引,创建索引的值必须是唯一的(除了NULL外,NULL可能会出现多次)。
ALTER TABLE tbl_name ADD UNIQUE index_name (column_list);
#添加全文索引
ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list);
ALTER TABLE tbl_name DROP INDEX index_name;
#添加主键,首先确保主键列不为空。
ALTER TABLE testalter_tbl MODIFY i INT NOT NULL;
ALTER TABLE tbl_name ADD PRIMARY KEY (column_list);
ALTER TABLE testalter_tbl DROP PRIMARY KEY;
CREATE INDEX indexName ON table_name (column_name);
DROP INDEX [indexName] ON table_name;
CREATE TABLE mytable(
ID INT NOT NULL,
username VARCHAR(16) NOT NULL,
INDEX [indexName] (username(length))
);
CREATE TEMPORARY TABLE SalesSummary (
product_name VARCHAR(50) NOT NULL,
total_sales DECIMAL(12,2) NOT NULL DEFAULT 0.00,
avg_unit_price DECIMAL(7,2) NOT NULL DEFAULT 0.00,
total_units_sold INT UNSIGNED NOT NULL DEFAULT 0
);
INSERT INTO SalesSummary
(product_name, total_sales, avg_unit_price, total_units_sold)
VALUES
('cucumber', 100.25, 90, 2);
SELECT * FROM SalesSummary;
DROP TABLE SalesSummary;
SELECT * FROM SalesSummary;
CREATE TEMPORARY TABLE aaa AS(
SELECT * FROM runoob_1
LIMIT 1,10000
);
法一:执行sql创建新表、再插入数据
#输出该表的创建语句
SHOW CREATE TABLE runoob_tbl \G;
#修改表名,然后执行sql语句
CREATE TABLE `clone_tbl` (
`runoob_id` int(11) NOT NULL auto_increment,
`runoob_title` varchar(100) NOT NULL default '',
`runoob_author` varchar(40) NOT NULL default '',
`submission_date` date default NULL,
PRIMARY KEY (`runoob_id`),
UNIQUE KEY `AUTHOR_INDEX` (`runoob_author`)
) ENGINE=InnoDB;
#将原表数据插入到新表
INSERT INTO clone_tbl (runoob_id,runoob_title,runoob_author,submission_date)
SELECT runoob_id,runoob_title,runoob_author,submission_date FROM runoob_tbl;
法二:复制表结构、再插入数据
CREATE TABLE targetTable LIKE sourceTable;
INSERT INTO targetTable SELECT * FROM sourceTable;
法三:复制表结构和数据
CREATE TABLE new_name SELECT * FROM old_name;
创建新表并修改字段名
CREATE TABLE newadmin(
SELECT id, username AS uname, password AS pass FROM admin
)
SELECT VERSION( ) | 服务器版本信息 |
---|---|
SELECT DATABASE( ) | 当前数据库名 (或者返回空) |
SELECT USER( ) | 当前用户名 |
SHOW STATUS | 服务器状态 |
SHOW VARIABLES | 服务器配置变量 |
CREATE TABLE tmp
SELECT last_name, first_name, sex FROM person_tbl
GROUP BY (last_name, first_name, sex);
DROP TABLE person_tbl;
ALTER TABLE tmp RENAME TO person_tbl;
导出数据
show variables like 'datadir';
SELECT * FROM table_name
INTO OUTFILE 'table.txt';
SELECT a,b,a+b INTO OUTFILE '/tmp/result.text'
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n'
FROM test_table;
mysqldump -uroot -proot 数据库名 数据表名 > tmp.txt
#导出数据库
mysqldump -uroot -proot 数据库名 > tmp.txt
#导入数据库(库名要先手动创建)
mysql -u root -p database_name < tmp.txt
mysqldump -u root -p --all-databases > database_dump.txt
mysqldump -h other-host.com -P port -u root -p database_name > dump.txt
导入数据
#导入数据库(库名要先手动创建)
mysql -u root -p database_name < tmp.txt
CREATE DATABASE abc;
USE abc;
SET NAMES utf8;
SOURCE /tmp/tmp.txt
LOAD DATA LOCAL INFILE 'dump.txt' INTO TABLE mytbl;
1179. Reformat Department Table
# Write your MySQL query statement below
select id,
max(case when month='Jan' then revenue end) as Jan_Revenue,
max(case when month='Feb' then revenue end) as Feb_Revenue,
max(case when month='Mar' then revenue end) as Mar_Revenue,
max(case when month='Apr' then revenue end) as Apr_Revenue,
max(case when month='May' then revenue end) as May_Revenue,
max(case when month='Jun' then revenue end) as Jun_Revenue,
max(case when month='Jul' then revenue end) as Jul_Revenue,
max(case when month='Aug' then revenue end) as Aug_Revenue,
max(case when month='Sep' then revenue end) as Sep_Revenue,
max(case when month='Oct' then revenue end) as Oct_Revenue,
max(case when month='Nov' then revenue end) as Nov_Revenue,
max(case when month='Dec' then revenue end) as Dec_Revenue
from department
group by id