MySQL快速入门

MySQL是一款流行的关系型数据库, 结构化查询语言SQL(Structured Query Language)是关系型数据库通用的操作语言, 但是不同数据库系统的行为还是有着细微的不同, 本文将以MySQL为例进行介绍.

一个MySql服务实例下可以维护多个database, 其中部分数据库用于维护用户和权限信息. Mysql提供了一些数据库管理语句:

  • SHOW DATABASES; 显示当前实例所拥有的数据库

  • DROP DATABASES db_name; 删除指定数据库

  • CREATE DATABASE [IF NOT EXISTS] db_name; 创建数据库

  • use db_name; 进入数据库

MySQL将数据存储在数据表中, 数据表由数据库来管理. 此外, 有一些命令可以查看数据表的元信息:

  • SHOW TABLES 显示当前数据库所有数据表

  • DESC <table_name>DESCRIBE <table_name> 显示表结构

DDL

数据定义语言DDL(Data Definition Language)用于操作数据表等数据库对象,对数据表、索引等进行定义。

数据表table是行列的逻辑结构, 每行是一条记录, 每列是记录的一个字段. 同一张表的所有记录拥有相同的结构, 创建表时最重要的信息就是表的结构信息.

CREATE TABLE `user` (
	`id` INT PRIMARY KEY AUTO_INCREMENT,
	`username` VARCHAR(20) DEFAULT "",
	`password` CHAR(20) DEFAULT "",
	`gender` CHAR(1),
	`age` INT
);

SQL语句不区分大小写, 通常我们将SQL关键字大写, 自定义标识符小写. 用反点号包括自定义标识符可以避免它们被认为是SQL关键字.

SQL标识符以字母开头, 可以使用字母, 数字或三个特殊符号#, _, $.标识符用于表示表, 列和其它对象的名称.

所有DDL语句中的DEFAULT项都是可以省略的.

MySQL中常用的数据类型有:

  • INT: 32位有符号整数
  • DOUBLE 双精度浮点数
  • DECIMAL 精确小数
  • CHAR(limit), 定长字符串
  • VARCHAR(limit) 变长字符串
  • TEXT 长文本数据
  • DATE 日期
  • DATETIME 日期和时间
  • TIMESTAMP UNIX时间戳

使用drop语句来删除数据表:

DROP TABLE `user`;

表在创建完成后仍可以修改其结构, 不过要注意对其中数据的影响:

  • 删除列
ALTER TABLE `user` DROP `age`;
  • 增加列:
ALTER TABLE `user` ADD `age` INT DEFAULT 0;
  • 修改列的类型或名称
ALTER TABLE `user` CHANGE `gender` `sex` CHAR(1) DEFAULT "M";
ALTER TABLE `user` MODIFY `gender` INT DEFAULT 0;

DML

数据操作语言DML(Data Manipulation Language), 用于操作表中的数据。

插入一条记录:

INSERT INTO `user` (`username`, `password`) VALUES ("abcd", "1234");
INSERT INTO `user` VALUES ("abcd", "1234");

要向所有没有默认值且不允许为空的列插入数据.

更新已存在的记录:

UPTATE `user` SET `username`="abc" WHERE `id`=1; 

update可以更新所有符合WHERE子句的记录, 当没有WHERE子句时更新该表所有记录.

当键重复时更新记录, 否则插入记录:

INSERT INTO `user` VALUES ("a", "2") ON DUPLICATE KEY UPDATE `username`="a", "password"=2; 

删除记录:

DELETE FROM `user` WHERE `id`=1;

删除所有符合WHERE子句的记录, 当没有WHERE子句时删除该表所有记录.

SELECT

查询语句SELECT是最灵活最复杂也是最重要的SQL语句.

我们创建用户user和文章post两张表做为示例:

CREATE TABLE `user` (
	`id` INT PRIMARY KEY AUTO_INCREMENT,
	`username` VARCHAR(20) DEFAULT "",
	`password` CHAR(20) DEFAULT "",
	`gender` CHAR(1),
	`age` INT
);
CREATE TABLE `post` (
	id INT PRIMARY KEY AUTO_INCREMENT,
	uid INT,
	title TEXT,
	CONTENT TEXT
);

查询user表中所有记录的所有字段.

SELECT * FROM `user`;

SELECT可以可以查询某个表中符合条件记录的某些字段.如所有男性用户的username:

SELECT `username`
FROM `user`
WHERE `gender`='M';

我们可以使用LIMIT限制返回结果的数量:

SELECT * FROM `user` LIMIT 10 OFFSET 10;

上述查询跳过前10个(OFFSET 10)结果, 并返回最多10条记录(LIMIT 10)。

在多表查询时可能需要指定列的所在的表:

SELECT `user`.`username`
FROM `user`
WHERE `user`.`gender`='M';

为了简化表达式可以为列和表指定别名:

SELECT u.`username` AS name
FROM `user` u
WHERE u.`gender`='M';

表中可能出现用户名相同的情况, 使用DISTINCT函数去除重复的用户名:

SELECT DISTINCT(`username`)
FROM `user`;

聚集

MySQL可以进行取平均值, 求和等聚集操作.

SELECT AVG(`age`) FROM `user`;

上述语句查询user表中所有记录age字段的平均值. 可以使用GROUP BY子句指定分组聚集方式.

SELECT AVG(`age`) FROM `user`
GROUP BY `gender`;

上述语句将gender字段相同的记录视作一组, 求出age字段的平均值. 我们可以附加where子句查询男性用户的平均年龄:

SELECT gender, AVG(`age`) FROM `user`
WHERE gender=`M`
GROUP BY gender;

WHERE子句中无法使用聚集函数, 可以使用HAVING子句完成该功能, 比如查询发表文章超过3篇的作者:

SELECT uid FROM `post`
HAVING count(id) > 3
GROUP BY uid;

常用的聚集函数有:

  • SUM 求和

  • AVG 求平均值

  • MIN 求最小值

  • MAX 求最大值

  • COUNT 求记录数

此外还有一些工具函数也顺便介绍:

  • LENGETH(txt), LEN(txt): 求文本长度

  • REPLACE(txt, from, to) 替换文本

  • UCASE(txt), LCASE(txt): 转为大写 / 小写

  • mid(txt, start, len): 提取子串

排序

ORDER BY 语句可以根据结果中某一列进行排序:

SELECT * FROM user ORDER BY age DESC LIMIT 10;

把user表中的记录根据age字段进行降序排列, 并返回前10条记录。

SELECT * FROM user ORDER BY age ASC LIMIT 10;

把user表中的记录根据age字段进行升序排列, 并返回前10条记录。

ORDER BY 语句可以进行多列排序:

SELECT * FROM user ORDER BY age DESC, username ASC;

把user表中的记录根据age字段进行升序排列,age相同的根据username字典序升序排列。

ORDER BY 语句可以根据聚集函数进行排序:

SELECT post_id, count(*) as comment_count 
FROM comment
GROUP BY post_id
ORDER BY count(*) DESC;

根据文章的评论数进行降序排列。

联合查询

现在查询所有女性用户发表的文章:

SELECT p.`id` AS post_id, `title`, `content`, u.`username` AS author
FROM `user` u, `post` p
WHERE
	p.`uid`=u.`id`
	AND u.`gender`='F'; 

字符串可以使用单引号'或双引号"表示.

上述查询将搜索userpost表中记录所有组合, 满足条件p.uid=u.iduser记录和post记录将组合为同一条记录. 然后查询组合记录中符合条件u.gender='F'的记录.

使用等价的join操作来代替上述查询:

SELECT p.`id` AS post_id, title, content, u.`username` AS author 
FROM `post` p
JOIN `user` u 
ON p.`uid`=u.`id`
WHERE u.`gender`='F'; 

join操作有3种:

  • INNER JOIN, JOIN: 查询出的记录必须满足ON条件,若左表或右表中没有对应的记录,查询结果中均不会包含相应记录。

  • LEFT JOIN: 查询左表post中所有记录, 即使右表中没有对应的记录. 当没有右表记录时, 查询结果中右表的字段为空值.

  • REIGHT JOIN 查询右表user中所有记录, 即使左表中没有对应记录. 当没有左表记录时, 查询结果中左表的字段为空值.

  • OUTER JOIN: LEFT JOINRIGHT JOIN 结果的并集,即左右表只有一个表中有记录即可。

注意若user表中有m条记录, post表中有n条记录则连接查询将会扫描m*n条记录. 这在表中数据量较大时会非常耗时.

IN 和 EXISTS 查询

使用INEXISTS子查询可以完成连接查询的任务.

SELECT *
FROM `post`
WHERE `uid` IN (
	SELECT `user`.`id` FROM `user` WHERE `gender`='F'
);

IN运算符将匹配uid和子查询的结果, 若记录的uid在结果集中则IN条件为真.

IN运算符后可以直接书写列表:

SELECT * FROM user
WHERE username in ('a', 'b');

查询username为'a'或'b'的用户。

EXISTS运算符可以起到类似的作用:

SELECT *
FROM `post`
WHERE EXISTS (
	SELECT `user`.`id` FROM `user` WHERE `gender`='F' 
	AND `user`.`id`=`post`.`uid`
); 

在EXISTS子查询中可以访问外表,如上例exists子查询中访问了post表。

上述SQL语句查询使EXISTS子查询SELECT user.id FROM user WHERE gender='F' AND user.id=post.uid 结果不为空集的post记录。即查询女性作者发表的所有文章,与本节开头的IN查询作用相同。

INEXISTS都有对应的否定形式:

SELECT *
FROM `post`
WHERE `uid` NOT IN (
	SELECT `user`.`id` FROM `user` WHERE `gender`='F'
);
SELECT *
FROM `post`
WHERE NOT EXISTS (
	SELECT `user`.`id` FROM `user` WHERE `gender`='F' 
	AND `user`.`id`=`post`.`uid`
); 

子查询

SELECT * FROM语句可以把另外一个查询结果作为数据源。

添加两张表:

CREATE TABLE `likes` (
	`uid` INT,
	`post_id` INT,
	PRIMARY KEY (`uid`, `post_id`) 
);

CREATE TABLE `comment` (
	`id` INT PRIMARY KEY AUTO_INCREMENT,
	`uid` INT,
	`post_id` INT,
	`content` TEXT,
);

我们要搜索所有文章的点赞like数和评论comment数:

SELECT 
	lc.post_id,
	lc.like_count,
	cc.comment_count
FROM (
	SELECT `post_id`, count(*) AS like_count FROM `likes` GROUP BY `post_id`
) lc
OUTER JOIN (
	SELECT `post_id`, count(*) AS comment_count FROM `comment` GROUP BY `post_id`
) cc
ON lc.post_id = cc.post_id;

上述语句中我们将查询语句SELECT post_id, count(*) AS like_count FROM likes GROUP BY post_id的结果作为一个数据表, 并给它一个别名lc

类似地,我们将另一个查询的结果作为数据表cc, 然后将两个数据表进行JOIN。

视图

MySQL可以根据查询创建视图view对象, 可以用访问数据表的方式访问视图. 视图只保存查询操作不保存数据, 视图在被访问时从数据表中取出数据.

CREATE VIEW `post_detail` AS
SELECT p.`id` AS post_id, title, content, 
	p.`uid` AS author_id, u.`username` AS author_name 
FROM `post` p
JOIN `user` u 
ON p.`uid`=u.`id`
WHERE u.`gender`='F'; 
SELECT * FROM `post_detail`;

约束

约束是在表上强制执行的校验规则, 用于保证数据的完整性. 约束分为对单列的约束和对多个列集合的约束.

非空约束

MySQL中所有数据类型都可以使用null, 通常使用field IS NULL来判断某个字段是否为空.

非空约束限制某一列不允许出现null值, 在建表时添加非空约束:

CREATE TABLE `user` {
  `username` VARCHAR(20) NOT NULL
}

也可以随时添加或删除非空约束:

ALTER TABLE `user` modify `username` VARCHAR(20) NULL;
ALTER TABLE `user` modify `username` VARCHAR(20) NOT NULL;

唯一约束

唯一约束要求单列或列的集合不允许出现多个相同的非NULL值, 不允许username列出现重复值:

CREATE TABLE `user` {
  `username` VARCHAR(20) UNIQUE
}

允许usernameemail重复, 但不允许任意两条记录在username相同时email也相同.

CREATE TABLE `user` {
  `username` VARCHAR(20),
  `email` VARCHAR(20),
  UNIQUE(`username`, `email`)
}

为了便于后续操作, 最好使用命名约束:

CREATE TABLE `user` {
  username VARCHAR(20),
  email VARCHAR(20),
  CONSTRAINT uc_user UNIQUE(username, email)
}

使用ALTER添加唯一约束:

ALTER TABLE `user` ADD UNIQUE(`username`);
ALTER TABLE `user` ADD CONSTRAINT uc_user UNIQUE(`username`, `email`);

撤销唯一约束:

ALTER TABLE `user` DROP CONSTRAINT uc_user;

主键约束

主键是表上的一列或几列的集合, 主键列必须非空且唯一. 主键必须可以唯一确定表中的记录, 即不存在主键列相同的两条记录.

每个表最多包含一个主键约束:

CREATE TABLE `user` (
	`id` INT PRIMARY KEY AUTO_INCREMENT,
	`username` VARCHAR(20) DEFAULT "",
);
CREATE TABLE `user` {
  username VARCHAR(20),
  email VARCHAR(20),
  CONSTRAINT user_key PRIMARY KEY(username, email)
}

使用ALTER修改主键约束:

ALTER TABLE `user` ADD PRIMARY KEY(`id`);
ALTER TABLE `user` ADD CONSTRAINT user_key PRIMARY KEY(`username`, `email`);

因为最多有一个主键约束, 所以删除时不用指定约束对象:

ALTER TABLE `user` DROP PRIMARY KEY;

外键约束

外键约束将数据表B上的某列或列集合与数据表A的主键关联, 外键列的结构必须与被参照主键的结构一致, 但允许外键列的值重复. 数据表B上每条记录的外键列必须与被参照表A的某条记录的主键相同.

CREATE TABLE `user` (
	`id` INT PRIMARY KEY AUTO_INCREMENT,
	`username` VARCHAR(20) DEFAULT "",
	`password` CHAR(20) DEFAULT "",
	`gender` CHAR(1),
	`age` INT
);
CREATE TABLE `post` (
	id INT PRIMARY KEY AUTO_INCREMENT,
	uid INT,
	title TEXT,
	CONTENT TEXT,
	FOREIGN KEY(uid) REFERENCES user(`id`) 
);

建立在post表上的外键可以保证post表中每条记录的uid都指向user表中一条记录. 避免存在找不到作者的文章.

当删除被参照表上的某条记录时, 必须删除所有参照它的记录. 即删除用户前必须前必须删除他发表的所有文章, 以保证外键约束不被破坏.

在拥有外键约束的表post上添加数据时必须扫描一遍被参照的user, 这可能消耗很多时间, 在使用外键时必须要考虑到这一点.

posted @ 2016-03-16 19:48  -Finley-  阅读(711)  评论(0编辑  收藏  举报