SQL语言的兴起与语法标准
20世纪70年代,IBM开发出SQL,用于DB2
1981年,IBM推出SQL/DS数据库
业内标准微软和Sybase的T-SQL,Oracle的PL/SQL
SQL作为关系型数据库所使用的标准语言,最初是基于IBM的实现在1986年被批准的。 1987年,“国际标准化组织(ISO)” 把ANSI(美国国家标准化组织)SQL作为国际标准。
SQL:ANSI SQL
SQL-1986, SQL-1989, SQL-1992, SQL-1999, SQL-2003,SQL-2008, SQL-2011
SQL语言规范
- 在数据库系统中,SQL语句不区分大小写(建议用大写)
- SQL语句可单行或多行书写,以“;” 结尾
- 关键词不能跨多行或简写
- 用空格和缩进来提高语句的可读性
- 子句通常位于独立行,便于编辑,提高可读性
- 注释:
- SQL标准:
/*注释内容*/ : 多行注释
-- 注释内容 : 单行注释,注意有空格 - MySQL注释:
#
- SQL标准:
数据库对象
- 数据库的组件(对象):
数据库、表、索引、视图、用户、存储过程、函数、触发器、事件调度器等 - 命名规则:
必须以字母开头
可包括数字和三个特殊字符(# _ $)
不要使用MySQL的保留字
同一database(Schema)下的对象不能同名
SQL语句分类:
- DDL: Data Defination Language 数据定义语言 :命令后面跟database,table
CREATE,DROP,ALTER - DML: Data Manipulation Language 数据操纵语言 : 命令后面跟record
INSERT,DELETE,UPDATE - DCL:Data Control Language 数据控制语言
GRANT,REVOKE,COMMIT,ROLLBACK - DQL:Data Query Language 数据查询语言
SELECT
SQL语句构成
- SQL语句构成:
Keyword组成clause
多条clause组成语句 - 示例:
SELECT * :SELECT子句
FROM products : FROM子句
WHERE price>400 :WHERE子句 - 说明:一组SQL语句,由三个子句构成,SELECT,FROM和WHERE是关键字
注意点1:
- SQL语句的对象是大小写敏感的,必须针对写的一字不错(比如数据库名字,表的名字),但是其他的语句大小写不敏感,怎么写都可以(注意表中的字段名字大小写不敏感,可以随意写,不过一般还是写的和字段一样比较好)
- SQL语言的多行注释可以写在一个命令的中间,不过一般也不这样写,都是些在命令的前面或者后面。MySQL可用#注释一行,这是自己独有的注释方式。
数据库操作
- 创建数据库:
CREATE DATABASE|SCHEMA [IF NOT EXISTS] 'DB_NAME';
CHARACTER SET 'character set name’COLLATE 'collate name' - 修改数据库:
ALTER DATABASE DB_NAME character set utf8; - 删除数据库
DROP DATABASE|SCHEMA [IF EXISTS] 'DB_NAME'; - 查看支持所有字符集:
SHOW CHARACTER SET; - 查看支持所有排序规则:
SHOW COLLATION; - 获取命令使用帮助:
mysql> HELP KEYWORD; - 查看数据库列表:
mysql> SHOW DATABASES;
表
表:二维关系
- 设计表:遵循规范
- 定义:字段,索引
字段:字段名,字段数据类型,修饰符
约束,索引:应该创建在经常用作查询条件的字段上
创建表
创建表:CREATE TABLE
(1) 直接创建
(2) 通过查询现存表创建;新表会被直接插入查询而来的数据
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name [(create_definition,...)] [table_options] [partition_options] select_statement
(3) 通过复制现存的表的表结构创建,但不复制数据
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name { LIKE old_tbl_name | (LIKE old_tbl_name) }
- 注意:
Storage Engine是指表类型,也即在表创建时指明其使用的存储引擎,同一库中不同表可以使用不同的存储引擎 - 同一个库中表建议要使用同一种存储引擎类型
表操作1
- 查看所有的引擎:SHOW ENGINES
- 查看表:SHOW TABLES [FROM db_name]
- 查看表结构:DESC [db_name.]tb_name
SHOW COLUMNS FROM [db_name.]tb_name - 删除表:DROP TABLE [IF EXISTS] tb_name
- 查看表创建命令:SHOW CREATE TABLE tbl_name
- 查看表状态:SHOW TABLE STATUS LIKE 'tbl_name’
- 查看库中所有表状态:SHOW TABLE STATUS FROM db_name
- 注意:加上\G在命令的后面,可以每一行竖着显示
数据类型
数据类型:
- 数据长什么样
- 数据需要多少空间来存放
系统内置数据类型和用户定义数据类型
MySql支持多种列类型: - 数值类型
- 日期/时间类型
- 字符串(字符)类型
选择正确的数据类型对于获得高性能至关重要,三大原则: - 更小的通常更好,尽量使用可正确存储数据的最小数据类型
- 简单就好,简单数据类型的操作通常需要更少的CPU周期
- 尽量避免NULL,包含为NULL的列,对MySQL更难优化
数据类型分类
更详细可见官方文档:
https://dev.mysql.com/doc/refman/8.0/en/data-types.html
- 整型
- tinyint(m) 1个字节 范围(-128~127)
- smallint(m) 2个字节 范围(-32768~32767)
- mediumint(m) 3个字节 范围(-8388608~8388607)
- int(m) 4个字节 范围(-2147483648~2147483647)
- bigint(m) 8个字节 范围(+-9.22*10的18次方)
注1:加了unsigned,则最大值翻倍,如:tinyint unsigned的取值范围为(0~255)
注2:int(m)里的m是表示SELECT查询结果集中的显示宽度,并不影响实际的取值范围,规定了MySQL的一些交互工具(例如MySQL命令行客户端)用来显示字符的个数。对于存储和计算来说,Int(1)和Int(20)是相同的 - BOOL,BOOLEAN:布尔型,是TINYINT(1)的同义词。zero值被视为假,非zero值视为真
- 浮点型(float和double),近似值
float(m,d) 单精度浮点型 8位精度(4字节) m总个数,d小数位
double(m,d) 双精度浮点型16位精度(8字节) m总个数,d小数位
例:设一个字段定义为float(6,3),如果插入一个数123.45678,实际数据库里存的是123.457,但总个数还以实际为准,即6位 - 定点数
在数据库中存放的是精确值,存为十进制
decimal(m,d) 参数 m < 65 是总个数,d < 30且 d < m 是小数位
MySQL5.0和更高版本将数字打包保存到一个二进制字符串中(每4个字节存9个数字)。例如,decimal(18,9)小数点两边将各存储9个数字,一共使用9个字节:小数点前的数字用4个字节,小数点后的数字用4个字节,小数点本身占1个字节
浮点类型在存储同样范围的值时,通常比decimal使用更少的空间。 float使用4个字节存储。double占用8个字节因为需要额外的空间和计算开销,所以应该尽量只在对小数进行精确计算时才使用decimal——例如存储财务数据。但在数据量比较大的时候,可以考虑使用bigint代替decimal - 字符串(char,varchar,_text)
char(n) 固定长度,最多255个字符 :查询速度块
varchar(n) 可变长度,最多65535个字符 :查询速度慢
tinytext 可变长度,最多255个字符
text 可变长度,最多65535个字符
mediumtext 可变长度,最多2的24次方-1个字符
longtext 可变长度,最多2的32次方-1个字符
BINARY(M) 固定长度,可存二进制或字符,长度为0-M字节
VARBINARY(M) 可变长度,可存二进制或字符,允许长度为0-M字节
内建类型:ENUM枚举, SET集合 - 二进制数据:BLOB
BLOB和text存储方式不同,TEXT以文本方式存储,英文存储区分大小写,而Blob是以二进制方式存储,不分大小写
BLOB存储的数据只能整体读出
TEXT可以指定字符集,BLOB不用指定字符集 - 日期时间类型
date 日期 '2008-12-2'
time 时间 '12:25:36'
datetime 日期时间 '2008-12-2 22:06:44'
timestamp 自动存储记录修改时间
YEAR(2), YEAR(4):年份
注意:timestamp字段里的时间数据会随其他字段修改的时候自动刷新,这个数据类型的字段可以存放这条记录最后被修改的时间
修饰符
所有类型:
• NULL 数据列可包含NULL值
• NOT NULL 数据列不允许包含NULL值
• DEFAULT 默认值
• PRIMARY KEY 主键
• UNIQUE KEY 唯一键
• CHARACTER SET name 指定一个字符集
数值型
• AUTO_INCREMENT 自动递增,适用于整数类型
• UNSIGNED 无符号
创建(定义)表示例
- CREATE TABLE students (id int UNSIGNED NOT NULL PRIMARYKEY,name VARCHAR(20)NOT NULL,age tinyint UNSIGNED);
- DESC students;
- CREATE TABLE students2 (id int UNSIGNED NOT NULL ,name VARCHAR(20) NOT NULL,age tinyint UNSIGNED,gender ENUM('m','f') DEFAULT 'm' ,PRIMARY KEY(id,name));
- 注意,只要是char类型的,不论是字段中还是表中的相对应的char类型中的数据,都需要加上单引号引起来,不然会报错
- 上面3中定义的primary key是复合主键,查看help create table中可以看到。
表操作2
DROP TABLE [IF EXISTS] 'tbl_name';
ALTER TABLE 'tbl_name'
字段:
添加字段:add
ADD col1 data_type [FIRST|AFTER col_name]
删除字段:drop
修改字段:
alter(默认值), change(字段名), modify(字段属性)
索引:
添加索引:add index
删除索引:drop index
表选项
修改:
查看表上的索引:SHOW INDEXES FROM [db_name.]tbl_name;
查看帮助:Help ALTER TABLE
修改表示例:
ALTER TABLE students RENAME s1;
ALTER TABLE s1 ADD phone varchar(11) AFTER name;
ALTER TABLE s1 MODIFY phone int;
ALTER TABLE s1 CHANGE COLUMN phone mobile char(11);
ALTER TABLE s1 DROP COLUMN mobile;
ALTER TABLE s1 character set utf8;
ALTER TABLE s1 change name name varchar(20) character set utf8;
Help ALTER TABLE 查看帮助
ALTER TABLE students ADD gender ENUM('m','f')
ALETR TABLE students CHANGE id sid int UNSIGNED NOT NULL PRIMARY KEY;
ALTER TABLE students drop primary key ;
ALTER TABLE students ADD UNIQUE KEY(name);
ALTER TABLE students ADD INDEX(age);
ALTER TABLE students drop primary key ;
DESC students;
SHOW INDEXES FROM students;
ALTER TABLE students DROP age;
注意点2(DDL语句):
- 可以进入数据库以后用help create [database | table ...] 来查看帮助如何创建数据库和表等等
- 创建数据库database的时候可以选择要创建的字符集以及它的排序规则是什么(创建的时候就可以定义写上),创建之后则用alter命令修改,
- 可以用show character set命令来查看当前数据库支持的字符集以及其对应的默认的排序规则都有哪些。(utf8,utf8mb4,latin1等等)
- show collation查看排序规则
- 用show create database DATABASE_NAME 来查看创建的数据库的默认字符集是什么
- help show,help alter命令来查看帮助
- 除了2中提到的查看数据库字符集的方式,还可以直接进入database对应的文件夹内(注意要进入到数据库对应的文件夹下的子文件夹database目录内),其中的*.opt文件中就记录着当前对应database的字符集和排序方式。
- 不过这样的方式每次创建一个新的数据库都无法指定自己想要的字符集和排序方式,可以直接把它写入到配置文件中,指定整个mysql服务的字符集和排序方式(进入数据库之后可以用status查看),具体的配置选项参考下面的变量或者官网的选项说明。
- 注意下划线和横线在配置选项中没有区别(写在my.cnf中的)
下面是源码编译时用Cmake选项修改过的mysql数据库服务为utf8mb4字符集的这些参数:
14:29[root@centos7 ~]# mysqladmin variable -p123456 | grep character
| character_set_client | utf8mb4
| character_set_connection | utf8mb4
| character_set_database | utf8mb4
| character_set_filesystem | binary
| character_set_results | utf8mb4
| character_set_server | utf8mb4
| character_set_system | utf8
| character_sets_dir | /app/mysql/share/charsets/
14:29[root@centos7 ~]# mysqladmin variable -p123456 | grep collation
| collation_connection | utf8mb4_general_ci
| collation_database | utf8mb4_general_ci
| collation_server | utf8mb4_general_ci
下面是二进制安装未修改的默认的mysql字符集:
14:28[root@centos7 ~]# mysqladmin -S/data/mysql/3307/socket/mysql.sock variable | grep character
| character_set_client | latin1
| character_set_connection | latin1
| character_set_database | latin1
| character_set_filesystem | binary
| character_set_results | latin1
| character_set_server | latin1
| character_set_system | utf8
| character_sets_dir | /usr/local/mariadb-10.2.23-linux-x86_64/share/charsets/
14:36[root@centos7 ~]# mysqladmin -S/data/mysql/3307/socket/mysql.sock variable | grep collation
| collation_connection | latin1_swedish_ci
| collation_database | latin1_swedish_ci
| collation_server | latin1_swedish_ci
可在/etc/mysql/my.cnf中的[mysqld]下添加一行(只需要改这一项即可修改全部的,不过要重启):
[mysqld]:
character-set-server = utf8mb4
1. 注意在,多实例的情况下,如果启动脚本中,mysqld330X的safe的参数选项选择配置文件时添加了extra的话,这个位置的my.cnf是能够控制所有实例的,如果没有加extra,这只能进入到每一个示例中去修改了。(类似于mysql连接时的命令的extra)
2. 建议的方式是/data/mysql/my.cnf中只写相同的项目,而/data/mysql/330X/etc/my.cnf写不同的项目
3. 不过需要注意优先级是按照覆盖的方式来写的,要注意/etc/my.cnf 这些其它的配置文件中不要写上不想要的配置,不然如果/data/mysql/330X/etc/my.cnf中如果没有写这些在前面的配置文件中写入过的选项的话,就会以前面的参数为准了。可能会造成自己一些不想修改的选项被修改了。
4. 经测试果然按照所想的,在这里优先级最高的(也就是最后使用的配置文件会把前面的配置给覆盖掉)文件就是这个safe脚本参数extra后面指定的文件(注意之前不加extra的时候只用了这个配置文件,现在是额外用了这个配置文件,不过它是最后一个使用的,覆盖掉前面的设置,因此优先级最高)。因此,就算前面的配置文件写了家目录,端口号等等,只要在最后的这个配置文件中也写入并修改为自己想要的设置的话,前面配置中写入的都会被覆盖不使用。因此也不会影响使用~
5. 在这里就可以利用这种方式只写一次修改全部实例的字符集了,写在前面的总的配置文件中,extra中不写这一项即可。
6. 注意用mysql连接的时候也可以加上参数mysql -S/data/mysql/3307/socket/mysql.sock --default-character-set=utf8mb4 ,让连接的客户端的字符集和服务器端的字符集完全一致
=======================================特别注意点:
注意此时修改了mysql服务器的字符集之后,之前已经创建的数据库database的字符集以及已经存在的表中的某些数据类型的字段(比如char类型的可规定字符集的)已经创建了字符集的,这些字符集是不会改变的。
=====
这个修改只能让新的创建的数据库database和表table的字符集按照这个设置的默认字符集进行创建,而已经创建的那些字符集是不会改变的。
=====
经过测试,就算这个设置已经生效,但是如果一个旧的数据库database的字符集是老的字符集比如latin1,则在这个数据库中创建的新的表的字符集仍然为这个数据库的老的字符集latin1,而并非是utf8mb4.
=====
因此需要把旧的数据库的字符集也修改为服务器设置的相同字符集,以后创建新的表的时候字符集才会正确,查看数据库database的字符集,可以进入这个数据库use之后用status命令来查看.
- drop database DATABASE_NAME 其实就是删除对应的database的目录,(同理drop table也就是删除其对应的表的定义和数据文件)
- 不过注意不要用rm的方式删除,要用命令的方式删除才可
- help create table可看到创建表的三种方式以及各种类型等,这里就不解释了
- 注意,只要是char类型的,不论是字段中还是表中的相对应的char类型中的数据,都需要加上单引号引起来,不然会报错;同时如果创建表的命令太长的话可以直接回车换行再继续书写即可。
- 创建之后可以用desc TABLE_NAME 查看表的定义
- 用show create table TABLE_NAME 查看表的字符集等相关信息(这个字符集创建的时候也可以写上,具体查看6中的帮助所写哪些data类型需要字符集)
- 创建完表之后,在相对应的磁盘上的数据库目录下的database的目录中就会增加一个表的名字相对应的.frm文件,这个文件就是表的定义的文件。
- 注意老版本的数据库的数据都存放在数据库目录下的(不是database目录下)的ibdata1文件中,不管是哪个database下的哪个表都往这里存放,非常不好。而新版本已经分别放到对应的database目录中的文件中了。
- 新版本的表对应存放的数据就在对应的database目录中的另外一个文件,就是.idb后缀文件中。
- show table status like 后面的表的名字,必须加上单引号引起来。可以用匹配的方式(利用%和_符号)。命令后面可以加上\G用于把每行竖行显示。
- ++创建表的第二种方式++是比第一种方式多了一个参考select的命令的结果(select 命令选择的字段的定义会被复制过来,原先字段内数据同样会被一并复制过来,注意和第三种克隆方式的区别:克隆方式只克隆表的定义,不克隆数据;它把字段的定义作为参考,加入新创建的表的字段定义中,注意其中有些信息会丢失,比如原先字段的主键信息等);
- 附加:在数据库外面想要select数据库内的表或者字段,可以用DATABASE.TABLE中间的加点的方式来引用。
- ++创建表的第三种方式++直接就是克隆原先的表的定义过来,并且与第二种方式相比,会保留字段的主键信息等,相当于和被克隆的表的定义一模一样;
- 但是需要注意,克隆的方式也会把被克隆的表的字符集的定义和顺序一并克隆过来,不论当前的database的字符集是什么,它只看原来的表的字符集,这一点尤其要注意。可以用show create table TABLE_NAME来查看。
- 另外有一点就是,表的默认字符集和字段(char类型的字段)的字符集可以是不相同的,在create table的帮助中也可以看到char类型的字段定义的时候是可以定义字符集的,因此用克隆的方式就算用下面的方式把表的默认字符集改变了,但是克隆过来的字段字符集没有变化,仍旧是原先数据库默认字符集(latin1),此时输入的字符(中文等)因为是在字段的record中,所以还是会乱码报错。
- 还有就是上面提到的第二种方式会把select选中的字段中数据一并复制过来,但是会丢失一些字段的额外属性;第三种方式克隆只会克隆表的定义,数据不复制,但是能保留字段的所有属性
- 可以创建完表字后用alter table TABLE_NAME character set utf8mb4命令修改表的字符集(注意和字段的字符集的区别)
- 表的修改很少去做,尽量在表创建的时候想好要创建什么字段。而且表之间的关联最多不能超过3表。
- 注意创建表的时候表的字段中的选项有AUTO_INCREMENT参数,同样表的选项中table_options也有这个参数,注意区分。
DML语句
DML: INSERT, DELETE, UPDATE
INSERT:
一次插入一行或多行数据
INSERT语法1
INSERT [L OW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE] [INTO] tbl_name [(col_name,...)] {VALUES | VALUE} ({expr | DEFAULT},...),(...),... [ ON DUPLICATE KEY UPDATE 如果重复更新之 col_name=expr [, col_name=expr] ... ]
简化写法:
INSERT tbl_name [(col1,...)] VALUES (val1,...), (val21,...)
INSERT语法2
INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE] [INTO] tbl_name SET col_name={expr | DEFAULT}, ... [ ON DUPLICATE KEY UPDATE col_name=expr [, col_name=expr] ... ]
INSERT语法3
INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE] [INTO] tbl_name [(col_name,...)] SELECT ... [ ON DUPLICATE KEY UPDATE col_name=expr [, col_name=expr] ... ]
UPDATE:
UPDATE [LOW_PRIORITY] [IGNORE] table_reference SET col_name1={expr1|DEFAULT} [, col_name2={expr2|DEFAULT}] ... [WHERE where_condition] [ORDER BY ...] [LIMIT row_count]
- 注意:一定要有限制条件,否则将修改所有行的指定字段
限制条件:
WHERE
LIMIT - Mysql 选项:-U|--safe-updates| --i-am-a-dummy
DELETE:
DELETE [LOW_PRIORITY] [QUICK] [IGNORE] FROM tbl_name [WHERE where_condition] [ORDER BY ...] [LIMIT row_count]
可先排序再指定删除的行数
- 注意:一定要有限制条件,否则将清空表中的所有数据
限制条件:
WHERE
LIMIT - TRUNCATE TABLE tbl_name; 清空表
注意点3(DML语句):
- insert命令后面的value的值一定要和前面的col写入的顺序一一对应,其中一次性可以添加多个record,只需要每一个value后面的值用小括号括起来,之间用逗号分隔开即可。
- 注意如果col的值没有写的话,代表后面要添加的value的值要包含所有的字段,同时要按照对应的表中字段的默认排序来写(可用desc TABLE_NAME查看表中的字段和默认排序从上到下。)
- 当前面的cols选定了要添加的记录record中的字段时,剩下的没有被选中的字段必须默认不能为空值或者有其他的属性(比如说数值型的自动增长,或者有默认的值),不然添加字段的时候如果不可以为空值的字段即没有特殊的其他属性又不在添加记录的时候给它赋值,这个字段就会出现问题。
- 表中的字段尽量不要让它为空值,要么默认不为空输入的时候写上,要么默认给它一个值,之后再修改。总之尽量不要为空值。(注意定义字段的时候如果不写上NOT NULL 或者 DEFAULT的话,这个字段默认可以为空值)
- 添加字段的时候注意字符串和创建字段的时候的枚举类型一样,只要是字符类型的数据都要单引号引起来,特殊的:如果选定了cols但是insert命令中有些记录record的某些字段并不赋值,可以用null来把它设置为空值。注意这里null不需要加单引号。不过前提是这些字段可以为空值。
- 数据库的data目录下有一个隐藏的history日志文件,它保留了数据库内进行的操作的历史命令。当退出数据库后就会进行更新。
- insert student select * form db2.student2 ;(第三种语法,第二种语法只能添加一行的字段)
- 特别注意:表的记录操作update和delete命令,修改删除record的时候一定要加上限制条件,不然会把所有的row都给修改掉。
- 可以在连接数据库的时候加上-U选项,避免这种不加限制条件的语法进行批量修改的情况发生(会把这种语句当做错误语句)
- 或者用别名的方式把mysql永远加上-U选项
- 或者说在配置文件中的[mysql]栏中(可以写在/data/mysql/my.cnf中或者说/etc/my.cnf.d/目录下的客户端配置文件中等等。)添加一项:safe-updates [=1 | =ture | on |...]
- delete from TABLE_NAME [where_condition],如果不加条件就会把整个表给删除掉。
- 注意条件的判断语句参看下面的select中的命令,或者说查看官方解释文档http://dev.mysql.com/doc/refman/5.5/en/expressions.html
- 当删除掉一行,再次添加新的行的时候,如果不指明行中含有的自动增长的数值型字段的值,则它会按照当前表中此字段最后的值来进行增长并添加。而被删除掉而这一行就会跳过并空着。不过可以添加的时候指明这个空着的值并添加一个记录进去。
- delete form TABLE_NAME 命令删除表的速度不如 truncate table TABLE_NAME,因为前者仍旧会记录到日志中,后者虽然快但是有一定的危险性,无法回复,所有慎用;不过它俩都是只删除表中数据,并未删除表的定义。
- 注意update命令修改表中record的命令只能用set 字段=VALUE的方式,后面加上判断条件where等。类似insert的第二中方式。
DQL语句
单表查询
SELECT
[ALL | DISTINCT | DISTINCTROW ]
[SQL_CACHE | SQL_NO_CACHE]
select_expr [, select_expr ...]
[FROM table_references
[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}]
[FOR UPDATE | LOCK IN SHARE MODE]
SELECT
- 字段显示可以使用别名:
col1 AS alias1, col2 AS alias2, ... - WHERE子句:指明过滤条件以实现“选择”的功能:
过滤条件:布尔型表达式
算术操作符:+, -, *, /, %
比较操作符:=,<=>(相等或都为空), <>, !=(非标准SQL), >, >=, <, <=
BETWEEN min_num AND max_num
IN (element1, element2, ...)
IS NULL
IS NOT NULL- 注意:下面的4 5 6 7用在 where 的++字段++ 后面,当做判断符号
- DISTINCT 去除重复列
SELECT DISTINCT gender FROM students; - LIKE:
% 任意长度的任意字符
_ 任意单个字符 - RLIKE:正则表达式,索引失效,不建议使用
- REGEXP:匹配字符串可用正则表达式书写模式,同上
- 逻辑操作符:
NOT
AND
OR
XOR - GROUP BY:根据指定的条件把查询结果进行“分组”以用于做“聚合”运算
avg(), max(), min(), count(), sum()
HAVING: 对分组聚合运算后的结果指定过滤条件 - ORDER BY: 根据指定的字段对查询结果进行排序
升序:ASC
降序:DESC - LIMIT [[offset,]row_count]:对查询的结果进行输出行数数量限制
- 对查询结果中的数据请求施加“锁”
FOR UPDATE: 写锁,独占或排它锁,只有一个读和写
LOCK IN SHARE MODE: 读锁,共享锁,同时多个读
select示例:
DESC students;
INSERT [INTO] students VALUES(1,'tom','m'),(2,'alice','f');
INSERT [INTO] students(id,name) VALUES(3,'jack'),(4,'allen');
SELECT * FROM students WHERE id < 3;
SELECT * FROM students WHERE gender='m';
SELECT * FROM students WHERE gender IS NULL;
SELECT * FROM students WHERE gender IS NOT NULL;
SELECT * FROM students ORDER BY name DESC LIMIT 2;
SELECT * FROM students ORDER BY name DESC LIMIT 1,2;
SELECT * FROM students WHERE id >=2 and id <=4
SELECT * FROM students WHERE BETWEEN 2 AND 4
SELECT * FROM students WHERE name LIKE ‘t%’
SELECT * FROM students WHERE name RLIKE '.*[lo].*';
SELECT id stuid,name as stuname FROM students
注意点4(DQL单表和基础):
- select命令选择字段的时候,可以直接在选择的字段后面加上别名,中间的AS符号可以省略(可以中文,如果用的utf8字符集的话,这里不用as可以直接加别名的原因就是字段都是以逗号,分隔开的);同样的,from关键字后面的表名也可以加别名。
- 不过需要注意,字段加别名可在select命令结束并显示结果的时候在字段栏中显示别名;同时字段和表加了别名之后,在后面的命令中,就可以用别名的方式再次使用这个表或者字段了
- 注意表名加了别名之后,在引用这个表中的字段(select后面,或者条件判断中on,where having等)都要用这个表的别名方式来引用了。
- select整个语句也可以作为一个整体加别名,把这个select的结果(可看作是一个表,类似view)用这个别名来引用,嵌套等
- select语句的from关键字后面也可以限定范围(where,limit等等)。
- 注意where等判定的时候有多种方式,最常见的就是后面跟的是"(数值型)字段 判断符号(=>等) VALUE "
- 当然字符型字段也可以判断:"(字符型)字段 判断符号(!=,<=>等等) 'string' " ,此时大于小于就是按照字符集排序方式的前后顺序来判断字符串的大小的(可用order by 查看顺序)。同时一定要注意string外面要加上单引号引起来。
- where后面的字段后面的判断符号还可以用like来进行模糊匹配(或者精确匹配)
- 经过测试,在这数据库里的字符型的数据不管在哪都要加上引号,数值型的没有要求。这个要求和命令无关,只要是在数据库内操作输入等都要按照这个规则而已。
- where 字段 is [not] null
- SQL注入:注意where后面的语句中可以写上一个永远正确的语句比如 1=1(2=2等等) 然后用它与其他的条件进行 OR的关系,就可以达到绕过这个OR所跟的条件判断的目的,从而来实现一些操作,比如绕过密码输入可以写语句: where password='' or 1=1 ;
- select 还支持数运算,where后面的语句也可以用类似for的in方式(就相当于=符号,不过不用写那么多的OR了),比如where 字段 in (value1,value2,...)
- 正则表达式rlike也可用,但正则则表达式方式无法使用索引,基本上不用。
- 排序分组限定(GROUP BY,ORDER BY,limit)也是在form关键字后面,它和where是同一类别的(同级)。
- 注意GROUP BY后面跟的也是字段,它会把整个表中这个字段中相同的record值的行给合并,然后按照前面select选择的项目(一般就是聚合函数,如果选择字段的话没有意义了)进行显示,把group by后面的字段放到显示结果的第一列colume上。
- 用group by 的时候,select 后面的字段一定要包含有与group by 后面的字段1相同的字段(不然看不出来结果,虽然不写也可以),同时select 后面的字段一定要有2聚合函数(这个结果也可以作为别名,前面已经说过),不然也没有意义了。
- group by 先分组之后,如果还要再次进行条件限定,此时用的关键字是having,(类似于where,只不过where是放在group by 前面的关键字,having 是放在后面的关键字),判断的符号等基本和where一致。它俩都可以进行条件限定,视情况选择使用(也可以同时使用,先where,然后group by 最后 having)
附加知识点:count()函数,对字段进行统计的时候count(字段名),其中记录如果可以为空,则里面的null值会不作为统计结果,这一点需要注意。对于主键字段利用count(主键字段)和count(*)的结果则是一致的,都是统计这个表(如果有限定条件的话就是限定条件的)所拥有的所有记录,因此简单点可以用count(*)来统计记录总数。 - group by 后面可以跟多个字段进行多次分组,中间直接用逗号隔开即可。此时就是按照所写字段的从左往右的顺序一个一个分组。
- order by 排序也可以与前面说的where,group by ,having 同时使用;同时它也像group by一样可以对多列按照从左往右的顺序进行排序(因为排序并不会像group by一样把字段中重复的值的行给换成一个,注意多列排序的时候最好不要与group by 同时使用)。
- limit命令用在select最后,可以取出需要的行,limit OFFSET,ROW ; 其中offset 和 row都是数值,中间逗号隔开。
多表查询
- 交叉连接:笛卡尔乘积
- 内连接:
等值连接:让表之间的字段以“等值”建立连接关系;
不等值连接
自然连接:去掉重复列的等值连接
自连接 - 外连接:
左外连接:
FROM tb1 LEFT JOIN tb2 ON tb1.col=tb2.col
右外连接
FROM tb1 RIGHT JOIN tb2 ON tb1.col=tb2.col - 子查询:在查询语句嵌套着查询语句,性能较差
基于某语句的查询结果再次进行的查询- 用在WHERE子句中的子查询
- 用于比较表达式中的子查询;子查询仅能返回单个值
SELECT Name,Age FROM students WHERE Age>(SELECT avg(Age)
FROM students); - 用于IN中的子查询:子查询应该单键查询并返回一个或多个值从构成列表
SELECT Name,Age FROM students WHERE Age IN (SELECT Age
FROM teachers); - 用于EXISTS
- 用于FROM子句中的子查询
使用格式:
SELECT tb_alias.col1,... FROM (SELECT clause) AS tb_alias WHERE Clause;
示例:
SELECT s.aage,s.ClassID FROM (SELECT avg(Age) AS aage,ClassID
FROM students WHERE ClassID IS NOT NULL GROUP BY ClassID) AS s WHERE s.aage>30;
- 联合查询:UNION
SELECT Name,Age FROM students UNION SELECT Name,Age FROM
teachers;
SELECT查询语句执行顺序,基本上也就是select命令的书写顺序:
注意点5(DQL多表):
- 第一种多表合并最简单的方式就是纵向合并,不过要保证这两个表的字段数(或者说用select选择后的字段数)数量要一致;
- 其中的字段的datatype并没有要求多个表都要一致,只需要字段数一样就行
- 这两个表最终的显示效果的标头是按照第一个表的select选择的字段(的别名)来显示的。
- 纵向合并的关键字为union,前后分别是select语句。,注意如果from关键字后多个表的表名如果有重名的(不同的数据库中),要指明是哪个数据库。用点.符号即可。
- 注意,union前后的select语句如果完全一样(相当于自己union自己,两个相同的),它并不会显示两遍结果出来,而仅仅是显示一遍:因为union会自动合并内容完全一致的相同的row,具有自动去重功能;因此用这个功能可以直接自己连接自己来达到去重的效果。
- 横向合并1,最简单的就是一张表的所有记录和另外一张表的所有记录一一对应分别组合,两张表的字段数相加合并,同时组合数为两张表的记录数的乘积
- 组合的命令为cross join, 和 union类似 ,不过它的命令前后不需要都加上select语句,它的前后只需要表的名字即可,例如:select * from TABLE1 corss join TABLE2 ;
- 横向合并2:内连接(TABLE 1 inner join TABLE2 on Condition)
- 内连接就是用于两张表相关联的部分进行链接,这里和cross join 不同的就是它的命令以及后面加上了on的条件限定(这里on就类似之前的where,having)
- 比如 select * rom TABLE1 inner join TABLE2 on TABLE1.COLUME2 = TABLE2.COLUME1 [and | or等等更多条件] ;
- 如果只选定某些特定字段(如果不同名字段则不用标明表名,如果同名字段则要写上表名,可用表名的别名)则可以在上面命令的基础上 在TABL1和TABLE2后面加上别名,然后前面的*号就可以用别名后面加`.字段`的方式来引用不同的表的字段显示了。
- 当然这个内连接也可以用 “select 字段 from TABLE1 [ALIAS1] ,TABLE2 [ALIAS2] where TABLE1.COLUME2 = TABLE2.COLUME1 ; ”的旧命令方式来代替。
- 横向合并3:左外连接和右外连接(TABLE1 left outer join TABLE2 on Condition)
- 它俩的命令和内连接的区别就在于吧inner换成left outer 或right outer 其他都一样
- 左外连接逻辑就是左边的表格select选中的内容全部显示出来,而右边的表中select中只有符合后面on后面的判断条件的record才会显示出来,而那些没有匹配到on条件的显示出的左边的表的record,在右边的表的这些字段中全部用null填充显示出来。
- 右外连接和左外链接刚好相反,右边的表全部显示,左边的表匹配的显示,剩余的补null(可以把表换个位置都用左外链接一样的结果)
- 横向合并4:左外链接取补和右外连接取补(左右外链接condition条件增加)
- 左外链接取补就是在左外链接的基础性上,把on后面的condition中的符合条件的这一部分给去除掉即可,可以用某个字段为空的方式来取补(右外连接取补类似)
- 比如左外链接 select * rom TABLE1 left outer join TABLE2 on TABLE1.COLUME2 = TABLE2.COLUME1
- 然后对它进行条件取补 select * rom TABLE1 left outer join TABLE2 on TABLE1.COLUME2 = TABLE2.COLUME1 and TABLE2.COLUME1 is null
- 这样就实现了左外链接取补
- 横向合并5:完全外连接(左外连接 union 右外连接 )
- 它就是上面所写的 ,只需要关键字 left 换成 right 然后 两个select语句union即可,其他的条件等等全部相同。
- 注意它利用了union自动去重的功能
- 子查询:多种方式,用where方式的时候结果必须为单值(看子查询部分的例子)
- 横向合并6:非交集部分(利用完全外连接和子查询的方式)
- 先把完全外连接的select的表达式写出来,把它看做为一张表,(用小括号括起来)同时给它起一个别名
- 然后再次select 语句,并利用where判断条件把两个表中内连接的部分去掉(可以利用!=符号方式)或者选中带有空值的记录(利用 or及is null ,用where创造两个条件将两边的空值项筛选选出来)
- 上面写的两种方式是两种思想,一种是利用完全外连接去掉内连接的之后想法,一种是利用左右外连接取补之后的并集想法来书写where后面的限定条件
- 按照第二中上面写的OR的想法:select * from (横向合并5中的完全外连接)as a where a.TABLE2_COLUME1 is null or a.TABLE1_COLUME2 is null
- 这个过程中注意不要有重名,各个部分该用别名的就用别名。逻辑上没有那么复杂。
- 多对多的表(比如说学生表和科目表,每个学生可以报多个科目,且每个科目都有成绩(这个成绩在第三张表中),同时一个科目有多个学生可以报)
- 多对多的表都有第三张表来表示对应关系,在上面的例子中第三张表就是成绩表,里面记录着"记录的ID(主键)""学生的ID" "科目的ID" "科目的成绩",以这种方式来表示学生表和科目表的对应关系
- 在这里想要把成绩表中的学生和科目显示出来而不是ID号的话,就要用3张表的内连接方式了。
- 可以先连接两张表的内连接 “select 字段 TABLE1 inner join TABLE2 on cONDITION1 ”
- 然后直接在后面再连接第三张表,而前面的select利用表的别名来进行选择字段即可“select 字段 TABLE1 inner join TABLE2 on cONDITION1 inner join TABLE3 on Condition2”
- 或者也可以用另外的方式,就是先连接两张表,然后小括号把它括起来作为一个表(view)来看待,之后再按照对表的操作方式对它进行操作即可,只是注意要多加别名,利用别名的方式来引用这个select命令结果的表
- 还有一种表的自内连接,比如说一个表三个字段为"员工ID""员工姓名""员工上司ID",想要把员工的上司的名字给显示出来,利用自内连接,这也是一种也是很常见的方法。
- 把这张表直接当做两张表来使用,一张看做员工表,一张看做上司表,利用内连接,别名进行链接即可
- select alias1.员工ID [员工号],alias1.员工姓名 [员工名], alias2.员工姓名 [上司名] from TABLE as alias1 inner join TABLE alias2 on alias1.员工上司ID = alias2.员工ID ;
- 上面命令中alias2第二张表看作是上司表,员工名字就是上司名,中括号内的为显示出来的别名,为了更加清楚。