MySql操作之表篇

发布于 4635 字 10 分钟 0 MySql MySql

一些基本的操作

[Mysql官方文档] (https://dev.mysql.com/doc/refman/5.7/en/select.html “Mysql官方文档”)

创建和删除

//创建表
mysql> create table <table_name>(field[,field2,...]) comment='';
//eg:
mysql> create table user(
> id int(4) not null primary key auto_increment,
> name varchar(20) not null,
> sex int(1) not null default '0',
> weight double(10,2)) comment='用户表';
//删除表
mysql> drop table <table_name>;
//修改表名
mysql> rename table <old_table_name> to <new_table_name>;

查看表

SHOW DATABASES                                //列出 MySQL Server 数据库。
SHOW TABLES [FROM db_name]                    //列出数据库数据表。
SHOW TABLE STATUS [FROM db_name]              //列出数据表及表状态信息。
SHOW COLUMNS FROM tbl_name [FROM db_name]     //列出资料表字段
SHOW FIELDS FROM tbl_name [FROM db_name]
DESCRIBE tbl_name [col_name]                  //可以简写为: DESC tbl_name [col_name]
SHOW FULL COLUMNS FROM tbl_name [FROM db_name]//列出字段及详情
SHOW FULL FIELDS FROM tbl_name [FROM db_name] //列出字段完整属性
SHOW INDEX FROM tbl_name [FROM db_name]       //列出表索引。
SHOW STATUS                                  //列出 DB Server 状态。
SHOW VARIABLES                               //列出 MySQL 系统环境变量。
SHOW PROCESSLIST                             //列出执行命令。
SHOW GRANTS FOR user                         //列出某用户权限

表字段

常用字段属性:

属性 含义
auto_increment 为新插入的行赋一个唯一的整数标识符,自动递增,作为主键列,每个表只允许有一个自动递增列
binary 区分大小写的方式排序
default 属性确保在没有任何值可用的情况下,赋予某个常量值,这个值必须是常量,因为MySQL不允许插入函数或表达式值。此外,此属性无法用于BLOB或TEXT列。如果已经为此列指定了NULL属性,没有指定默认值时默认值将为NULL,否则默认值将依赖于字段的数据类型
index 如果所有其他因素都相同,要加速数据库查询,使用索引通常是最重要的一个步骤。索引一个列会为该列创建一个有序的键数组,每个键指向其相应的表行。以后针对输入条件可以搜索这个有序的键数组,与搜索整个未索引的表相比,这将在性能方面得到极大的提升。在实践中尽量使用小的索引。
not null 如果将一个列定义为not null,将不允许向该列插入null值。建议在重要情况下始终使用not null属性,因为它提供了一个基本验证,确保已经向查询传递了所有必要的值。
null 为列指定null属性时,该列可以保持为空,而不论行中其它列是否已经被填充。记住,null精确的说法是“无”,而不是空字符串或0。
primary key 用于确保指定行的唯一性。指定为主键的列中,值不能重复,也不能为空。为指定为主键的列赋予auto_increment属性是很常见的,因为此列不必与行数据有任何关系,而只是作为一个唯一标识符。作为主键又分两种:(1) 单字段主键 如果输入到数据库中的每行都已经有不可修改的唯一标识符,一般会使用单字段主键。注意,此主键一旦设置就不能再修改。(2) 多字段主键如果记录中任何一个字段都不可能保证唯一性,就可以使用多字段主键。这时,多个字段联合起来确保唯一性。如果出现这种情况,指定一个auto_increment整数作为主键是更好的办法。
unique 被赋予unique属性的列将确保所有值都有不同的值,只是null值可以重复。一般会指定一个列为unique,以确保该列的所有值都不同。
zerofill 可用于任何数值类型,用0填充所有剩余字段空间。例如,无符号int的默认宽度是10;因此,当“零填充”的int值为4时,将表示它为0000000004。
character set 指定一个字符集

添加字段

mysql> alter table <table_name> add <field_name> <field_type> comment "备注";
//eg:在表user添加了一个字段update_time,类型为timestamp,设置创建时间和默认时间
mysql> alter table user add update_time timestamp default CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP comment='更新时间';
// log表中 添加一个state字段并且放在current_count后
mysql> ALTER TABLE log ADD `state` TINYINT (1) UNSIGNED NOT NULL DEFAULT 0 COMMENT "状态,0 待审核,1已审核,2拒绝" AFTER current_count;

修改字段

mysql> alter table <table_name> change <old_table> <new_table> <field_type>;
//eg:
mysql> alter table user change weight height double(8,2);

删除字段

删除字段:
mysql> alter table <table_name> drop <field_name>;

添加索引

//一般索引
mysql> alter table <table_name> add index <index_name> (field_name[,field_name2,...]);
//eg:
mysql> alter table user add index u_name (name,sex);
//加主关键字的索引
mysql> alter table <table_name> add primary key <field_name>;
//eg:
mysql> alter table user add primary key(id);
//加唯一限制条件的索引
mysql> alter table <table_name> add unique <index_name> (field_name);
//eg:
mysql> alter table user add unique u_name(cardnumber);

删除索引

mysql> alter table <table_name> drop index <index_name>;
//eg:
mysql> alter table user drop index u_name;

insert语法

INSERT [ LOW_PRIORITY | DELAYED | HIGH_PRIORITY ] [ IGNORE ] [ INTO ] tbl_name [ PARTITION (
	partition_name ,...) ] [ (
		col_name ,...) ] {
		VALUES
			|
		VALUE
			} ({ expr | DEFAULT },...),(...) ,...[ ON DUPLICATE KEY UPDATE col_name = expr [, col_name = expr ]...]

//eg1 单条插入:
mysql> INSERT INTO user(name, age) VALUE ("张三",18);
//eg2 多条插入:
mysql> INSERT INTO user(name, age) VALUES ("张三",18),("李四",19),("王五",20);

or

//只能插入一条
INSERT [ LOW_PRIORITY | DELAYED | HIGH_PRIORITY ] [ IGNORE ] [ INTO ] tbl_name [ PARTITION (
	partition_name ,...) ]
	SET col_name ={ expr | DEFAULT },...[ ON DUPLICATE KEY UPDATE col_name = expr [, col_name = expr ]...]

//eg:
mysql> INSERT INTO user set name="张三", age=18;

or

//用于表复制
INSERT [ LOW_PRIORITY | HIGH_PRIORITY ] [ IGNORE ] [ INTO ] tbl_name [ PARTITION (
	partition_name ,...) ] [ (
		col_name ,...) ] SELECT
			...[ ON DUPLICATE KEY UPDATE col_name = expr [, col_name = expr ]...]
			
//eg1 两个表数据互通:
mysql> INSERT INTO a(a_field1,a_field2) SELECT b_field1,b_field2 FROM b;
//eg2 多张表进行复制,需要先使用关联查询查出数据后再插入,嵌套部分需要设置别名!!:
mysql> INSERT INTO a(a_field1,a_field2) SELECT * FROM(SELECT f1,f2 FROM b JOIN c) AS tb;

update语法

单条更新:

UPDATE [ LOW_PRIORITY ] [ IGNORE ] table_reference
SET col_name1 ={ expr1 | DEFAULT } [, col_name2 ={ expr2 | DEFAULT }]...[
WHERE
	where_condition ] [
ORDER BY
	...] [
LIMIT row_count ]

# eg1 单表更新:
UPDATE user SET name="李四",age=19 WHERE id=1;

# eg2 多表更新:
UPDATE USER u
INNER JOIN user_info ui ON u.user_id = ui_user_id
SET ui_age = 20
WHERE
	u.sex = 1;

多条更新:

由于mysql并没有提供多条更新语句,所以进行批量更新网上有三种方法:

  1. 在程序中批量update,一条记录update一次,性能很差

  2. 使用replace into 或者insert into …on duplicate key update,性能较好

    //eg1replace into (操作本质是对重复的记录先delete后insert,如果更新的字段不全会将缺失的字段置为缺省值)
    mysql> replace into user (name,age) values ("张三",15),("李四",20),("王五",17);
    //eg2insert into (操作本质是只update重复记录,不会改变其它字段。)
    mysql> insert into user (id, name, age) values (1,"张三",15),(2,"李四",20),(3,"王五",17) on duplicate key update name=values(name), age=values(age);
    

    两者异同 相同之处:

    (1) 没有key的时候,replace与insert .. on deplicate udpate相同。 (2) 有key的时候,都保留主键值,并且auto_increment自动+1

    不同之处:

    有key的时候,replace是delete老记录,而录入新的记录,所以原有的所有记录会被清除,这个时候,如果replace语句的字段不全的话,有些原有的字段的值会被自动填充为默认值。 而insert .. duplicate update则只执行update标记之后的sql,从表象上来看相当于一个简单的update语句。所以两者的区别只有一个,insert .. on duplicate udpate保留了所有字段的旧值,再覆盖然后一起insert进去,而replace没有保留旧值,直接删除再insert新值。

    从底层执行效率上来讲,replace要比insert .. on duplicate update效率要高,但是在写replace的时候,字段要写全,防止老的字段数据被删除。

  3. 创建临时表,先更新临时表,然后从临时表中update,性能最好

    //注意:这种方法需要用户有temporary 表的create 权限。
    mysql> create temporary table tmp(id int(4) primary key,name varchar(50), age int(4));
    mysql> insert into tmp values (1,"张三",15),(2,"李四",20),(3,"王五",17);
    mysql> update user, tmp set user.name=tmp.name, user.age=tmp.age where user.id=tmp.id;
    

补充:

// 批量替换字段内容的一部分
mysql> UPDATE article SET content = REPLACE(content, 'abc', '123');
//效果: "AabcB" =》"A123B"

delete语法

普通删除:

DELETE [ LOW_PRIORITY ] [ QUICK ] [ IGNORE ]
FROM
	tbl_name [ PARTITION (
		partition_name ,...) ] [
		WHERE
			where_condition ] [
		ORDER BY
			...] [
		LIMIT row_count ]

//eg1 删除单条:
mysql> DELETE FROM user where user_id = 1;
//eg2 删除多条:
mysql> DELETE FROM user where user_id in (1,2,3,4);

多表删除:

DELETE [ LOW_PRIORITY ] [ QUICK ] [ IGNORE ] tbl_name [.*] [, tbl_name [.*]]...
FROM
	table_references [
WHERE
	where_condition ]

//eg:
DELETE t1,t2
FROM t1
INNER JOIN t2
INNER JOIN t3
WHERE
	t1.id = t2.id
AND t2.id = t3.id;

or:

DELETE [ LOW_PRIORITY ] [ QUICK ] [ IGNORE ]
FROM
	tbl_name [.*] [, tbl_name [.*]]... USING table_references [
WHERE
	where_condition ]
	
# eg
DELETE
FROM
	t1,
	t2 USING t1
INNER JOIN t2
INNER JOIN t3
WHERE
	t1.id = t2.id
AND t2.id = t3.id;

截断表:

mysql> truncate table <table_name>;
/*
截断表可以用于删除表中的所有数据。截断表命令还会回收所有索引的分配页。截断表的执行速度与不带where子句的delete(删除)命令相同,甚至比它还要快。delete(删除)一次删除一行数据,并且将每一行被删除的数据都作为一个事务记录日志;而truncate (截断)表则回收整个数据页,只记录很少的日志项。delete(删除)和truncate(截断)都会回收被数据占用的空间,以及相关的索引。只有表的拥有者可以截断表。
另外,truncate表之后,如果有自动主键的话,会恢复成默认值。
*/

select语法

基本语法:

SELECT
	[ ALL | DISTINCT | DISTINCTROW ] [ HIGH_PRIORITY ] [ STRAIGHT_JOIN ] [ SQL_SMALL_RESULT ] [ SQL_BIG_RESULT ] [ SQL_BUFFER_RESULT ] [ SQL_CACHE | SQL_NO_CACHE ] [ SQL_CALC_FOUND_ROWS ] select_expr [, select_expr...] [
FROM
	table_references [ PARTITION partition_list ] [
WHERE
	where_condition ] [
GROUP BY
	{ col_name | expr | position } [ ASC | DESC ],...[ WITH ROLLUP ]] [
HAVING
	where_condition ] [
ORDER BY
	{ col_name | expr | position } [ ASC | DESC ],...] [
LIMIT {[ OFFSET ,] row_count | row_count OFFSET OFFSET }] [ PROCEDURE procedure_name (argument_list) ] [ INTO OUTFILE 'file_name' [ CHARACTER
SET charset_name ] export_options | INTO DUMPFILE 'file_name' | INTO var_name [, var_name ]] [ FOR UPDATE | LOCK IN SHARE MODE ]]

#eg:
#查询总条数
mysql> SELECT COUNT(*) FROM test_table WHERE tf_b = 1;
#倒序分页查询
mysql> SELECT tf_a, tf_b FROM test_table WHERE tf_b = 1 ORDER BY tf_a DESC LIMIT 100,10;

花式操作

# 创建学生表1
CREATE TABLE `student1` (
  `STUDENT_ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '学生自增长ID',
  `STUDENT_NAME` varchar(30) DEFAULT NULL COMMENT '学生名称',
  `STUDENT_COURSE` varchar(30) DEFAULT NULL COMMENT '学科',
  `STUDENT_SCORE` int(11) DEFAULT NULL COMMENT '成绩',
  PRIMARY KEY (`STUDENT_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='学生表';

# 插入数据
INSERT INTO student1 (
	STUDENT_NAME,
	STUDENT_COURSE,
	STUDENT_SCORE
)
VALUES
	("张三", "语文", 80),
	("张三", "数学", 90),
	("张三", "英语", 85),
	("李四", "语文", 30),
	("李四", "数学", 90),
	("李四", "英语", 100);
STUDENT_ID STUDENT_NAME STUDENT_COURSE STUDENT_SCORE
1 张三 语文 80
2 张三 数学 90
3 张三 英语 85
4 李四 语文 30
5 李四 数学 90
6 李四 英语 100
# 创建学生表2
CREATE TABLE `student2` (
  `STUDENT_ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '学生自增长ID',
  `STUDENT_NAME` varchar(30) DEFAULT NULL COMMENT '学生名称',
  `STUDENT_TOTAL_SCORE` int(1) DEFAULT NULL COMMENT '总成绩',
  PRIMARY KEY (`STUDENT_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='学生总成绩';

查询并插入

语法格式

INSERT INTO 表名1(字段列表1) SELECT 字段列表2 FROM 表名2 WHERE 条件表达式;
# eg 用一条语句查询出student1表中学生总成绩并且降序插入student2表中
INSERT INTO student2 (
	STUDENT_NAME,
	STUDENT_TOTAL_SCORE
) SELECT
	STUDENT_NAME,
	SUM(STUDENT_SCORE) AS STUDENT_TOTAL_SCORE
FROM
	student1
GROUP BY
	STUDENT_NAME

运行结果:

STUDENT_ID STUDENT_NAME STUDENT_TOTAL_SCORE
1 张三 255
2 李四 220

查询并更新

update时,更新的表不能在set和where中用于子查询,但是可以对多个表进行更新(INNER JOIN),所以根据这个特性我们来做查询并更新 语法格式:

UPDATE table1 a INNER JOIN table2 b on a.id = b.id set ... where ...
# eg: 用一条语句将所有人的语文成绩改成95
UPDATE student1 a
INNER JOIN (
	SELECT
		STUDENT_ID,
		STUDENT_NAME,
		STUDENT_COURSE,
		STUDENT_SCORE
	FROM
		student1
	WHERE
		STUDENT_COURSE LIKE '%英语%'
) b ON a.STUDENT_ID = b.STUDENT_ID
SET a.STUDENT_SCORE = 95

查询并删除

通常查询并删除针对的是表中的重复记录,所以我们首先要查找到重复的记录:

查找记录
方法一(推荐)
SELECT * FROM
	table a
WHERE
	[(a.field1,a.field2,...)] IN (
		SELECT
			[field1,field2,...]
		FROM
			table
		GROUP BY
			[field1 ,field2,...]
		HAVING
			Count(*) > 1
	) 
方法二:
SELECT * FROM
	table a
WHERE
((  SELECT
    	COUNT(*)
    FROM
    	table
    WHERE
    	field1 = a.field1
        AND field2 = a.field2
        AND ...
)> 1)

eg: ps. 如果需要只显示一条最新记录,则添加a.STUDENT_ID和MAX(STUDENT_ID)。

# 根据多个字段查询student1
SELECT
	STUDENT_ID,
	STUDENT_NAME,
	STUDENT_COURSE,
	STUDENT_SCORE
FROM
	student1 a
WHERE
	(a.STUDENT_NAME, a.STUDENT_COURSE) IN (
	# (a.STUDENT_NAME, a.STUDENT_COURSE,a.STUDENT_ID) IN (
		SELECT
		    # MAX(STUDENT_ID)
			STUDENT_NAME,
			STUDENT_COURSE
		FROM
			student1
		GROUP BY
			STUDENT_NAME,
			STUDENT_COURSE
		HAVING
			count(*) > 1
	)
ORDER BY
	STUDENT_COURSE DESC
删除记录

如上所提,可以得到最新的一条数据,那只保留id最大的一条数据: 两种方法目前手头没有大的数据无法做测试,等以后再比较性能差异。 方法一:

DELETE FROM student1
WHERE
    # 将最后插入的数据保留 其他数据删除
	STUDENT_ID  NOT IN (
 		# 查询最后插入的数据
		SELECT
			MAX(STUDENT_ID)
		FROM
			(
				#  查询出重复数据
				SELECT
					STUDENT_ID,
					STUDENT_NAME,
					STUDENT_COURSE,
					STUDENT_SCORE
				FROM
					student1 a
				WHERE
					(
						a.STUDENT_NAME,
						a.STUDENT_COURSE
					) IN (
						SELECT
							STUDENT_NAME,
							STUDENT_COURSE
						FROM
							student1
						GROUP BY
							STUDENT_NAME,
							STUDENT_COURSE
						HAVING
							count(*) > 1
					)
			) AS tmp
		GROUP BY
			STUDENT_NAME,
			STUDENT_COURSE
		HAVING
			count(*) > 1
	)

方法二:

DELETE
FROM
	student1
WHERE
    #查出重复数据
	(
		STUDENT_NAME,
		STUDENT_COURSE
	) IN (
		SELECT
			STUDENT_NAME,
			STUDENT_COURSE
		FROM
			(
				SELECT
					STUDENT_NAME,
					STUDENT_COURSE
				FROM
					student1
				GROUP BY
					STUDENT_NAME,
					STUDENT_COURSE
				HAVING
					COUNT(*) > 1
			) s1
	)
# 排除最后插入的数据保留
AND STUDENT_ID NOT IN (
	SELECT
		STUDENT_ID
	FROM
		(
			SELECT
				MAX(STUDENT_ID)
			FROM
				student1
			GROUP BY
				STUDENT_NAME,
				STUDENT_COURSE
			HAVING
				COUNT(*) > 1
		) s2
);