MySQL表操作

表创建

表的介绍

image.png
  • 表是关系数据库的核心
  • 表 = 关系
  • 表是记录的集合
  • 二维表格模型易于人的理解
  • MySQL默认存储引擎都是基于行(记录)存储
  • 每行记录都是基于列进行组织的

表是数据的集合

select * from table_name limit 1;

集合是无序的,上面的SQL语句的意思是 从表(集合)中随机选出一条数据,结果是不确定的, 不能简单的认为是取出第一条数据。

select * from table_name order by col_name limit 1;

只有通过order by排序之后取出的数据,才是确定的。

表的创建:官方文档

表创建语法

CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
    (create_definition,...)
    [table_options]
    [partition_options]

CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
    [(create_definition,...)]
    [table_options]
    [partition_options]
    [IGNORE | REPLACE]
    [AS] query_expression

CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
    { LIKE old_tbl_name | (LIKE old_tbl_name) }

create_definition:
    col_name column_definition
  | {INDEX|KEY} [index_name] [index_type] (key_part,...)
      [index_option] ...
  | {FULLTEXT|SPATIAL} [INDEX|KEY] [index_name] (key_part,...)
      [index_option] ...
  | [CONSTRAINT [symbol]] PRIMARY KEY
      [index_type] (key_part,...)
      [index_option] ...
  | [CONSTRAINT [symbol]] UNIQUE [INDEX|KEY]
      [index_name] [index_type] (key_part,...)
      [index_option] ...
  | [CONSTRAINT [symbol]] FOREIGN KEY
      [index_name] (col_name,...)
      reference_definition
  | CHECK (expr)

column_definition:
    data_type [NOT NULL | NULL] [DEFAULT default_value]
      [AUTO_INCREMENT] [UNIQUE [KEY]] [[PRIMARY] KEY]
      [COMMENT 'string']
      [COLLATE collation_name]
      [COLUMN_FORMAT {FIXED|DYNAMIC|DEFAULT}]
      [STORAGE {DISK|MEMORY}]
      [reference_definition]
  | data_type
      [COLLATE collation_name]
      [GENERATED ALWAYS] AS (expr)
      [VIRTUAL | STORED] [NOT NULL | NULL]
      [UNIQUE [KEY]] [[PRIMARY] KEY]
      [COMMENT 'string']
      [reference_definition]

data_type:
    (see Chapter 11, Data Types)

key_part:
    col_name [(length)] [ASC | DESC]

index_type:
    USING {BTREE | HASH}

index_option:
    KEY_BLOCK_SIZE [=] value
  | index_type
  | WITH PARSER parser_name
  | COMMENT 'string'

reference_definition:
    REFERENCES tbl_name (key_part,...)
      [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]
      [ON DELETE reference_option]
      [ON UPDATE reference_option]

reference_option:
    RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT

table_options:
    table_option [[,] table_option] ...

table_option:
    AUTO_INCREMENT [=] value
  | AVG_ROW_LENGTH [=] value
  | [DEFAULT] CHARACTER SET [=] charset_name
  | CHECKSUM [=] {0 | 1}
  | [DEFAULT] COLLATE [=] collation_name
  | COMMENT [=] 'string'
  | COMPRESSION [=] {'ZLIB'|'LZ4'|'NONE'}
  | CONNECTION [=] 'connect_string'
  | {DATA|INDEX} DIRECTORY [=] 'absolute path to directory'
  | DELAY_KEY_WRITE [=] {0 | 1}
  | ENCRYPTION [=] {'Y' | 'N'}
  | ENGINE [=] engine_name
  | INSERT_METHOD [=] { NO | FIRST | LAST }
  | KEY_BLOCK_SIZE [=] value
  | MAX_ROWS [=] value
  | MIN_ROWS [=] value
  | PACK_KEYS [=] {0 | 1 | DEFAULT}
  | PASSWORD [=] 'string'
  | ROW_FORMAT [=] {DEFAULT|DYNAMIC|FIXED|COMPRESSED|REDUNDANT|COMPACT}
  | STATS_AUTO_RECALC [=] {DEFAULT|0|1}
  | STATS_PERSISTENT [=] {DEFAULT|0|1}
  | STATS_SAMPLE_PAGES [=] value
  | TABLESPACE tablespace_name [STORAGE {DISK|MEMORY}]
  | UNION [=] (tbl_name[,tbl_name]...)

partition_options:
    PARTITION BY
        { [LINEAR] HASH(expr)
        | [LINEAR] KEY [ALGORITHM={1|2}] (column_list)
        | RANGE{(expr) | COLUMNS(column_list)}
        | LIST{(expr) | COLUMNS(column_list)} }
    [PARTITIONS num]
    [SUBPARTITION BY
        { [LINEAR] HASH(expr)
        | [LINEAR] KEY [ALGORITHM={1|2}] (column_list) }
      [SUBPARTITIONS num]
    ]
    [(partition_definition [, partition_definition] ...)]

partition_definition:
    PARTITION partition_name
        [VALUES
            {LESS THAN {(expr | value_list) | MAXVALUE}
            |
            IN (value_list)}]
        [[STORAGE] ENGINE [=] engine_name]
        [COMMENT [=] 'string' ]
        [DATA DIRECTORY [=] 'data_dir']
        [INDEX DIRECTORY [=] 'index_dir']
        [MAX_ROWS [=] max_number_of_rows]
        [MIN_ROWS [=] min_number_of_rows]
        [TABLESPACE [=] tablespace_name]
        [(subpartition_definition [, subpartition_definition] ...)]

subpartition_definition:
    SUBPARTITION logical_name
        [[STORAGE] ENGINE [=] engine_name]
        [COMMENT [=] 'string' ]
        [DATA DIRECTORY [=] 'data_dir']
        [INDEX DIRECTORY [=] 'index_dir']
        [MAX_ROWS [=] max_number_of_rows]
        [MIN_ROWS [=] min_number_of_rows]
        [TABLESPACE [=] tablespace_name]

query_expression:
    SELECT ...   (Some valid select or union statement)

创建临时表

mysql> select version();
+------------+
| version()  |
+------------+
| 5.7.26-log |
+------------+
1 row in set (0.00 sec)

mysql> 
mysql> create temporary table a ( a int );
ERROR 1787 (HY000): Statement violates GTID consistency: CREATE TEMPORARY TABLE and DROP TEMPORARY TABLE can only be executed outside transactional context.  These statements are also not allowed in a function or trigger because functions and triggers are also considered to be multi-statement transactions.
mysql> 

翻译:错误1787(HY000):语句违反GTID一致性:CREATE TEMPORARY TABLE和DROP TEMPORARY TABLE只能在事务上下文之外执行。函数或触发器中也不允许使用这些语句,因为函数和触发器也被视为多语句事务。

通过查找发现,当autocommit = 1才能创建临时表

# 查看 autocommit
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | OFF   |
+---------------+-------+
1 row in set (0.00 sec)

# 全局修改 autocommit,或者在配置中/etc/my.cnf修改
mysql> set global autocommit = 1;
Query OK, 0 rows affected (0.00 sec)

# 全局修改完成后,需要重新登录MySQL
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | OFF   |
+---------------+-------+
1 row in set (0.00 sec)

mysql> set global autocommit = 1;
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | OFF   |
+---------------+-------+
1 row in set (0.00 sec)

mysql> exit
Bye
[root@iZwz956snfyrvah6yq8sa4Z ~]# mysql -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 14
Server version: 5.7.26-log MySQL Community Server (GPL)

Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.00 sec)

# 创建临时表
Database changed
mysql> create temporary table a ( a int );
Query OK, 0 rows affected (0.00 sec)

mysql> insert into a select 123;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> show * from a;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '* from a' at line 1
mysql> select * from a;
+------+
| a    |
+------+
|  123 |
+------+
1 row in set (0.00 sec)

另开一个进程

[root@iZwz956snfyrvah6yq8sa4Z ~]# mysql -p
Enter password: 
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
[root@iZwz956snfyrvah6yq8sa4Z ~]# mysql -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 17
Server version: 5.7.26-log MySQL Community Server (GPL)

Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 
mysql> 
mysql> use test;
Database changed
mysql> 
mysql> show tables;
Empty set (0.00 sec)

mysql> 

这时发现,test数据库中是没有a这个表的。因为临时表只有在当前回话内才有效,当MySQL退出时,会话结束后,临时表就释放了。

临时有什么作用呢:可以用来存储一些临时数据或结果。

mysql> create temporary table a ( a int ) ;
Query OK, 0 rows affected (0.00 sec)

mysql> show create table a\G;
*************************** 1. row ***************************
       Table: a
Create Table: CREATE TEMPORARY TABLE `a` (
  `a` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)

ERROR: 
No query specified

mysql> show variables like 'default%tmp%';
+----------------------------+--------+
| Variable_name              | Value  |
+----------------------------+--------+
| default_tmp_storage_engine | InnoDB |
+----------------------------+--------+
1 row in set (0.00 sec)

mysql> 

临时表的存储引擎ENGINE是由配置default_tmp_storage_engine值决定的

临时表的存储位置:/usr/local/mysql/data/ibtmp1

mysql> show variables like 'innodb_temp%';
+----------------------------+-----------------------+
| Variable_name              | Value                 |
+----------------------------+-----------------------+
| innodb_temp_data_file_path | ibtmp1:12M:autoextend |
+----------------------------+-----------------------+
1 row in set (0.00 sec)

mysql> 

当创建的临时表与已存在的表同名时,查询的结果只能读取到临时的数据

mysql> create table a ( a int, b int );
Query OK, 0 rows affected (0.02 sec)

mysql> insert into a select 1,2;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> select * from a;
+------+------+
| a    | b    |
+------+------+
|    1 |    2 |
+------+------+
1 row in set (0.00 sec)

mysql> create temporary table a ( a int );
Query OK, 0 rows affected (0.00 sec)

mysql> insert into a select 123;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> select * from a;
+------+
| a    |
+------+
|  123 |
+------+
1 row in set (0.00 sec)

mysql> 

所以创建临时表时,加上判断条件

mysql> create temporary table if not exists a ( a int );
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> show warnings;
+-------+------+--------------------------+
| Level | Code | Message                  |
+-------+------+--------------------------+
| Note  | 1050 | Table 'a' already exists |
+-------+------+--------------------------+
1 row in set (0.00 sec)

mysql> 

查看以创建的表结构

mysql> show create table a\G
*************************** 1. row ***************************
       Table: a
Create Table: CREATE TEMPORARY TABLE `a` (
  `a` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)

mysql> desc a;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| a     | int(11) | YES  |     | NULL    |       |
+-------+---------+------+-----+---------+-------+
1 row in set (0.00 sec)

mysql> show table status like 'a'\G
*************************** 1. row ***************************
           Name: a
         Engine: InnoDB
        Version: 10
     Row_format: Dynamic
           Rows: 1
 Avg_row_length: 8192
    Data_length: 8192
Max_data_length: 0
   Index_length: 0
      Data_free: 0
 Auto_increment: NULL
    Create_time: 2019-05-30 22:58:01
    Update_time: 2019-05-30 22:58:15
     Check_time: NULL
      Collation: utf8mb4_general_ci
       Checksum: NULL
 Create_options: 
        Comment: 
1 row in set (0.00 sec)

mysql> 

外键约束

[CONSTRAINT [symbol]] FOREIGN KEY
    [index_name] (col_name, ...)
    REFERENCES tbl_name (col_name,...)
    [ON DELETE reference_option]
    [ON UPDATE reference_option]

reference_option:
    RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT

外键约束的介绍

CREATE TABLE product (         -- 商品表
    category INT NOT NULL,     -- 商品种类
    id INT NOT NULL,           -- 商品id
    price DECIMAL, 
    PRIMARY KEY(category, id)  -- 主键是 (category, id)
)   ENGINE=INNODB;

CREATE TABLE customer (   -- 客户表
    id INT NOT NULL,      -- 客户id
    PRIMARY KEY (id)      -- 主键是 id
)   ENGINE=INNODB;

CREATE TABLE product_order (         -- 订单表
    no INT NOT NULL AUTO_INCREMENT,  -- number,自增长
    product_category INT NOT NULL,   -- 商品种类
    product_id INT NOT NULL,         -- 商品id 
    customer_id INT NOT NULL,        -- 客户id

    PRIMARY KEY(no),                        -- 主键是  no 
    INDEX (product_category, product_id),   -- 对 (product_category, product_id) 做索引
    INDEX (customer_id),                    -- 对 customer_id 做索引

    FOREIGN KEY (product_category, product_id)  -- 两个外键约束
    REFERENCES product(category, id)   -- 字段 product_category 引用自 product表的category
                                       -- 字段 product_id 引用自 product表的id
      ON UPDATE CASCADE ON DELETE RESTRICT,     -- 级联跟新  和  严格模式删除

    FOREIGN KEY (customer_id)
      REFERENCES customer(id)
)   ENGINE=INNODB;

外键约束的操作

mysql> create table parent (
    ->     id int not null,
    ->     primary key (id)
    -> ) engine=innodb;
Query OK, 0 rows affected (0.14 sec)

mysql> create table child (
    ->     id int, 
    ->     parent_id INT,
    ->     index par_ind (parent_id),
    ->     foreign key (parent_id) 
    ->         references parent(id)
    ->         on delete cascade on update cascade  -- 比官网例子增加 update cascade 
    -> ) engine=innodb;
Query ok, 0 rows affected (0.15 sec)

mysql> insert into child values(1,1);  -- 我们插入一条数据,id=1,parent_id=1
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`burn_test`.`child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`) ON DELETE CASCADE)  
-- 直接报错了,因为此时parent表中没有任何记录

mysql> insert into parent values(1);   -- 现在parent中插入记录
Query OK, 1 row affected (0.03 sec)

mysql> insert into child values(1,1);  -- 然后在child中插入记录,且parent_id是在parent中存在的
Query OK, 1 row affected (0.02 sec)

mysql> insert into child values(1,2);  -- 插入parent_id=2的记录,报错。因为此时parent_id=2的记录不存在
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`burn_test`.`child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`) ON DELETE CASCADE)

mysql> select * from  child;     
+------+-----------+
| id   | parent_id |
+------+-----------+
|    1 |         1 |  -- parent_id = 1
+------+-----------+
1 row in set (0.00 sec)

mysql> select * from  parent;
+----+
| id |
+----+
|  1 |  -- 根据表结构的定义(Foreign_key),这个值就是 child表中的id
+----+
1 row in set (0.00 sec)

mysql> update parent set id=100 where id=1;       
Query OK, 1 row affected (0.04 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from parent;
+-----+
| id  |
+-----+
| 100 |  -- 已经设置成了100
+-----+
1 row in set (0.00 sec)

mysql> select * from child;
+------+-----------+
| id   | parent_id |
+------+-----------+
|    1 |       100 |  -- 自动变化,这是on update cascade的作用,联级更新,parent更新,child也跟着更新
+------+-----------+
1 row in set (0.00 sec)

mysql> delete from parent where id=100;  -- 删除这条记录
Query OK, 1 row affected (0.03 sec)

mysql> select * from parent;  -- id=100的记录已经被删除了
Empty set (0.00 sec)

mysql> select * from child;  -- id=1,parent_id=100的记录跟着被删除了。on delete cascade的作用
Empty set (0.00 sec)

mysql> alter table child drop foreign key child_ibfk_1;  -- 删除 之前的外键
Query OK, 0 rows affected (0.07 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> alter table child add foreign key(parent_id) 
    -> references parent(id) on update cascade on delete restrict;  -- 使用严格模式
Query OK, 0 rows affected (0.27 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> insert into parent values(50);
Query OK, 1 row affected (0.03 sec)

mysql> insert into child values(3,50); 
Query OK, 1 row affected (0.03 sec)

mysql> insert into child values(3,51);  -- 和之前一样会提示错误
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`burn_test`.`child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`) ON UPDATE CASCADE)

mysql> delete from parent where id=50;  -- 删除失败了,因为是restrict模式
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`burn_test`.`child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`) ON UPDATE CASCADE)

注:no action == restrict

外键约束,可以让数据进行一致性更新,但是会有一定的性能损耗,线上业务使用不多。通常上述级联更新和删除都是由应用层业务逻辑进行判断并实现。


表修改


语法

ALTER TABLE tbl_name
    [alter_specification [, alter_specification] ...]
    [partition_options]

alter_specification:
    table_options
  | ADD [COLUMN] col_name column_definition
        [FIRST | AFTER col_name]
  | ADD [COLUMN] (col_name column_definition,...)
  | ADD {INDEX|KEY} [index_name]
        [index_type] (key_part,...) [index_option] ...
  | ADD {FULLTEXT|SPATIAL} [INDEX|KEY] [index_name]
        (key_part,...) [index_option] ...
  | ADD [CONSTRAINT [symbol]] PRIMARY KEY
        [index_type] (key_part,...)
        [index_option] ...
  | ADD [CONSTRAINT [symbol]] UNIQUE [INDEX|KEY]
        [index_name] [index_type] (key_part,...)
        [index_option] ...
  | ADD [CONSTRAINT [symbol]] FOREIGN KEY
        [index_name] (col_name,...)
        reference_definition
  | ADD CHECK (expr)
  | ALGORITHM [=] {DEFAULT|INPLACE|COPY}
  | ALTER [COLUMN] col_name {SET DEFAULT literal | DROP DEFAULT}
  | CHANGE [COLUMN] old_col_name new_col_name column_definition
        [FIRST|AFTER col_name]
  | [DEFAULT] CHARACTER SET [=] charset_name [COLLATE [=] collation_name]
  | CONVERT TO CHARACTER SET charset_name [COLLATE collation_name]
  | {DISABLE|ENABLE} KEYS
  | {DISCARD|IMPORT} TABLESPACE
  | DROP [COLUMN] col_name
  | DROP {INDEX|KEY} index_name
  | DROP PRIMARY KEY
  | DROP FOREIGN KEY fk_symbol
  | FORCE
  | LOCK [=] {DEFAULT|NONE|SHARED|EXCLUSIVE}
  | MODIFY [COLUMN] col_name column_definition
        [FIRST | AFTER col_name]
  | ORDER BY col_name [, col_name] ...
  | RENAME {INDEX|KEY} old_index_name TO new_index_name
  | RENAME [TO|AS] new_tbl_name
  | {WITHOUT|WITH} VALIDATION
  | ADD PARTITION (partition_definition)
  | DROP PARTITION partition_names
  | DISCARD PARTITION {partition_names | ALL} TABLESPACE
  | IMPORT PARTITION {partition_names | ALL} TABLESPACE
  | TRUNCATE PARTITION {partition_names | ALL}
  | COALESCE PARTITION number
  | REORGANIZE PARTITION partition_names INTO (partition_definitions)
  | EXCHANGE PARTITION partition_name WITH TABLE tbl_name [{WITH|WITHOUT} VALIDATION]
  | ANALYZE PARTITION {partition_names | ALL}
  | CHECK PARTITION {partition_names | ALL}
  | OPTIMIZE PARTITION {partition_names | ALL}
  | REBUILD PARTITION {partition_names | ALL}
  | REPAIR PARTITION {partition_names | ALL}
  | REMOVE PARTITIONING
  | UPGRADE PARTITIONING

key_part:
    col_name [(length)] [ASC | DESC]

index_type:
    USING {BTREE | HASH}

index_option:
    KEY_BLOCK_SIZE [=] value
  | index_type
  | WITH PARSER parser_name
  | COMMENT 'string'

table_options:
    table_option [[,] table_option] ...

table_option:
    AUTO_INCREMENT [=] value
  | AVG_ROW_LENGTH [=] value
  | [DEFAULT] CHARACTER SET [=] charset_name
  | CHECKSUM [=] {0 | 1}
  | [DEFAULT] COLLATE [=] collation_name
  | COMMENT [=] 'string'
  | COMPRESSION [=] {'ZLIB'|'LZ4'|'NONE'}
  | CONNECTION [=] 'connect_string'
  | {DATA|INDEX} DIRECTORY [=] 'absolute path to directory'
  | DELAY_KEY_WRITE [=] {0 | 1}
  | ENCRYPTION [=] {'Y' | 'N'}
  | ENGINE [=] engine_name
  | INSERT_METHOD [=] { NO | FIRST | LAST }
  | KEY_BLOCK_SIZE [=] value
  | MAX_ROWS [=] value
  | MIN_ROWS [=] value
  | PACK_KEYS [=] {0 | 1 | DEFAULT}
  | PASSWORD [=] 'string'
  | ROW_FORMAT [=] {DEFAULT|DYNAMIC|FIXED|COMPRESSED|REDUNDANT|COMPACT}
  | STATS_AUTO_RECALC [=] {DEFAULT|0|1}
  | STATS_PERSISTENT [=] {DEFAULT|0|1}
  | STATS_SAMPLE_PAGES [=] value
  | TABLESPACE tablespace_name [STORAGE {DISK|MEMORY}]
  | UNION [=] (tbl_name[,tbl_name]...)

partition_options:
    (see CREATE TABLE options)

操作

mysql> create table a ( a int );
Query OK, 0 rows affected (0.09 sec)

mysql> show create table a\G
*************************** 1. row ***************************
       Table: a
Create Table: CREATE TABLE `a` (
  `a` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)

# 增加一列 b
mysql> alter table a add column b char(11);
Query OK, 0 rows affected (0.20 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> show create table a\G
*************************** 1. row ***************************
       Table: a
Create Table: CREATE TABLE `a` (
  `a` int(11) DEFAULT NULL,
  `b` char(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)

mysql> select * from a;
Empty set (0.00 sec)

mysql> insert into a select 1, 'asd';
Query OK, 1 row affected (0.01 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> select * from a;
+------+------+
| a    | b    |
+------+------+
|    1 | asd  |
+------+------+
1 row in set (0.00 sec)

# 删除 a 列
mysql> alter table a drop column a;
Query OK, 0 rows affected (0.06 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> select * from a;
+------+
| b    |
+------+
| asd  |
+------+
1 row in set (0.00 sec)

mysql> 

注意,当表记录很大的时候,alter table会很耗时,影响性能


安装Employees


Employees官方文档

Employees介绍

Employees数据库是一个用于学习和测试的数据库,大约160MB,4百万条记录

下载

git clone https://github.com/datacharmer/test_db.git

查看

cd test_db

ls -al

[root@iZwz956snfyrvah6yq8sa4Z test_db]# ll
总用量 168344
-rw-r--r-- 1 root root      964 6月   9 13:40 Changelog
-rw-r--r-- 1 root root     7948 6月   9 13:40 employees_partitioned_5.1.sql
-rw-r--r-- 1 root root     6276 6月   9 13:40 employees_partitioned.sql
-rw-r--r-- 1 root root     4193 6月   9 13:40 employees.sql
drwxr-xr-x 2 root root     4096 6月   9 13:40 images
-rw-r--r-- 1 root root      250 6月   9 13:40 load_departments.dump
-rw-r--r-- 1 root root 14159880 6月   9 13:40 load_dept_emp.dump
-rw-r--r-- 1 root root     1090 6月   9 13:40 load_dept_manager.dump
-rw-r--r-- 1 root root 17722832 6月   9 13:40 load_employees.dump
-rw-r--r-- 1 root root 39806034 6月   9 13:40 load_salaries1.dump
-rw-r--r-- 1 root root 39805981 6月   9 13:40 load_salaries2.dump
-rw-r--r-- 1 root root 39080916 6月   9 13:40 load_salaries3.dump
-rw-r--r-- 1 root root 21708736 6月   9 13:40 load_titles.dump
-rw-r--r-- 1 root root     4568 6月   9 13:40 objects.sql
-rw-r--r-- 1 root root     4325 6月   9 13:40 README.md
drwxr-xr-x 2 root root     4096 6月   9 13:40 sakila
-rw-r--r-- 1 root root      272 6月   9 13:40 show_elapsed.sql
-rwxr-xr-x 1 root root     1800 6月   9 13:40 sql_test.sh
-rw-r--r-- 1 root root     4878 6月   9 13:40 test_employees_md5.sql
-rw-r--r-- 1 root root     4882 6月   9 13:40 test_employees_sha.sql

安装

[root@iZwz956snfyrvah6yq8sa4Z test_db]# mysql -uroot -p -t < employees.sql 
Enter password: 
+-----------------------------+
| INFO                        |
+-----------------------------+
| CREATING DATABASE STRUCTURE |
+-----------------------------+
+------------------------+
| INFO                   |
+------------------------+
| storage engine: InnoDB |
+------------------------+
+---------------------+
| INFO                |
+---------------------+
| LOADING departments |
+---------------------+
+-------------------+
| INFO              |
+-------------------+
| LOADING employees |
+-------------------+
+------------------+
| INFO             |
+------------------+
| LOADING dept_emp |
+------------------+
+----------------------+
| INFO                 |
+----------------------+
| LOADING dept_manager |
+----------------------+
+----------------+
| INFO           |
+----------------+
| LOADING titles |
+----------------+
+------------------+
| INFO             |
+------------------+
| LOADING salaries |
+------------------+
+---------------------+
| data_load_time_diff |
+---------------------+
| 00:01:14            |
+---------------------+
[root@iZwz956snfyrvah6yq8sa4Z test_db]# 

验证

[root@iZwz956snfyrvah6yq8sa4Z test_db]# mysql -uroot -p -t < test_employees_sha.sql 
Enter password: 
+----------------------+
| INFO                 |
+----------------------+
| TESTING INSTALLATION |
+----------------------+
+--------------+------------------+------------------------------------------+
| table_name   | expected_records | expected_crc                             |
+--------------+------------------+------------------------------------------+
| employees    |           300024 | 4d4aa689914d8fd41db7e45c2168e7dcb9697359 |
| departments  |                9 | 4b315afa0e35ca6649df897b958345bcb3d2b764 |
| dept_manager |               24 | 9687a7d6f93ca8847388a42a6d8d93982a841c6c |
| dept_emp     |           331603 | d95ab9fe07df0865f592574b3b33b9c741d9fd1b |
| titles       |           443308 | d12d5f746b88f07e69b9e36675b6067abb01b60e |
| salaries     |          2844047 | b5a1785c27d75e33a4173aaa22ccf41ebd7d4a9f |
+--------------+------------------+------------------------------------------+
+--------------+------------------+------------------------------------------+
| table_name   | found_records    | found_crc                                |
+--------------+------------------+------------------------------------------+
| employees    |           300024 | 4d4aa689914d8fd41db7e45c2168e7dcb9697359 |
| departments  |                9 | 4b315afa0e35ca6649df897b958345bcb3d2b764 |
| dept_manager |               24 | 9687a7d6f93ca8847388a42a6d8d93982a841c6c |
| dept_emp     |           331603 | d95ab9fe07df0865f592574b3b33b9c741d9fd1b |
| titles       |           443308 | d12d5f746b88f07e69b9e36675b6067abb01b60e |
| salaries     |          2844047 | b5a1785c27d75e33a4173aaa22ccf41ebd7d4a9f |
+--------------+------------------+------------------------------------------+
+--------------+---------------+-----------+
| table_name   | records_match | crc_match |
+--------------+---------------+-----------+
| employees    | OK            | ok        |
| departments  | OK            | ok        |
| dept_manager | OK            | ok        |
| dept_emp     | OK            | ok        |
| titles       | OK            | ok        |
| salaries     | OK            | ok        |
+--------------+---------------+-----------+
+------------------+
| computation_time |
+------------------+
| 00:00:16         |
+------------------+
+---------+--------+
| summary | result |
+---------+--------+
| CRC     | OK     |
| count   | OK     |
+---------+--------+
[root@iZwz956snfyrvah6yq8sa4Z test_db]# mysql -uroot -p -t < test_employees_md5.sql 
Enter password: 
+----------------------+
| INFO                 |
+----------------------+
| TESTING INSTALLATION |
+----------------------+
+--------------+------------------+----------------------------------+
| table_name   | expected_records | expected_crc                     |
+--------------+------------------+----------------------------------+
| employees    |           300024 | 4ec56ab5ba37218d187cf6ab09ce1aa1 |
| departments  |                9 | d1af5e170d2d1591d776d5638d71fc5f |
| dept_manager |               24 | 8720e2f0853ac9096b689c14664f847e |
| dept_emp     |           331603 | ccf6fe516f990bdaa49713fc478701b7 |
| titles       |           443308 | bfa016c472df68e70a03facafa1bc0a8 |
| salaries     |          2844047 | fd220654e95aea1b169624ffe3fca934 |
+--------------+------------------+----------------------------------+
+--------------+------------------+----------------------------------+
| table_name   | found_records    | found_crc                        |
+--------------+------------------+----------------------------------+
| employees    |           300024 | 4ec56ab5ba37218d187cf6ab09ce1aa1 |
| departments  |                9 | d1af5e170d2d1591d776d5638d71fc5f |
| dept_manager |               24 | 8720e2f0853ac9096b689c14664f847e |
| dept_emp     |           331603 | ccf6fe516f990bdaa49713fc478701b7 |
| titles       |           443308 | bfa016c472df68e70a03facafa1bc0a8 |
| salaries     |          2844047 | fd220654e95aea1b169624ffe3fca934 |
+--------------+------------------+----------------------------------+
+--------------+---------------+-----------+
| table_name   | records_match | crc_match |
+--------------+---------------+-----------+
| employees    | OK            | ok        |
| departments  | OK            | ok        |
| dept_manager | OK            | ok        |
| dept_emp     | OK            | ok        |
| titles       | OK            | ok        |
| salaries     | OK            | ok        |
+--------------+---------------+-----------+
+------------------+
| computation_time |
+------------------+
| 00:00:14         |
+------------------+
+---------+--------+
| summary | result |
+---------+--------+
| CRC     | OK     |
| count   | OK     |
+---------+--------+
[root@iZwz956snfyrvah6yq8sa4Z test_db]# 

至此,Employees测试数据库安装成功


表查询


语法

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]]

操作

mysql> select host,user from user;
+-----------+---------------+
| host      | user          |
+-----------+---------------+
| 127.0.0.1 | chase         |
| 127.0.0.1 | jim           |
| localhost | mysql.session |
| localhost | mysql.sys     |
| localhost | root          |
+-----------+---------------+
5 rows in set (0.00 sec)

mysql> 

LIMIT 和 ORDER BY

mysql> select * from employees limit 1;  -- 从employees中 随机 取出一条数据,结果是不确定的
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date  |
+--------+------------+------------+-----------+--------+------------+
|  10001 | 1953-09-02 | Georgi     | Facello   | M      | 1986-06-26 |
+--------+------------+------------+-----------+--------+------------+
1 row in set (0.00 sec)

--
-- order by col_name 根据某列的值进行排序
-- asc : 升序(default)
-- desc: 降序
--

mysql> select * from employees order by emp_no asc  limit 1;   -- 使用order by col_name asc进行升序排序
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date  |
+--------+------------+------------+-----------+--------+------------+
|  10001 | 1953-09-02 | Georgi     | Facello   | M      | 1986-06-26 |
+--------+------------+------------+-----------+--------+------------+
1 row in set (0.00 sec)

mysql> select * from employees order by  emp_no limit 1;       -- 默认就是升序的
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date  |
+--------+------------+------------+-----------+--------+------------+
|  10001 | 1953-09-02 | Georgi     | Facello   | M      | 1986-06-26 |  -- 结果和上面一致
+--------+------------+------------+-----------+--------+------------+
1 row in set (0.00 sec)

mysql> select * from employees order by emp_no desc limit 1;    -- desc表示降序
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date  |
+--------+------------+------------+-----------+--------+------------+
| 499999 | 1958-05-01 | Sachin     | Tsukuda   | M      | 1997-11-30 |  -- 降序显示
+--------+------------+------------+-----------+--------+------------+
-- 通过order by排序后 limit 1 才是确定的
1 row in set (0.00 sec)

mysql> show create table employees\G
*************************** 1. row ***************************
       Table: employees
Create Table: CREATE TABLE `employees` (
  `emp_no` int(11) NOT NULL,
  `birth_date` date NOT NULL,
  `first_name` varchar(14) NOT NULL,
  `last_name` varchar(16) NOT NULL,
  `gender` enum('M','F') NOT NULL,
  `hire_date` date NOT NULL,
  PRIMARY KEY (`emp_no`)    -- emp_no 是主键,order by 主键 不会创建临时表的,主键(索引)本身有序
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)

mysql> select * from employees order by emp_no asc limit 5,5;  -- limit start, offset
                                                               -- 从第5条 开始取,取5条出来
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date  |
+--------+------------+------------+-----------+--------+------------+
|  10006 | 1953-04-20 | Anneke     | Preusig   | F      | 1989-06-02 |
|  10007 | 1957-05-23 | Tzvetan    | Zielinski | F      | 1989-02-10 |
|  10008 | 1958-02-19 | Saniya     | Kalloufi  | M      | 1994-09-15 |
|  10009 | 1952-04-19 | Sumant     | Peac      | F      | 1985-02-18 |
|  10010 | 1963-06-01 | Duangkaew  | Piveteau  | F      | 1989-08-24 |
+--------+------------+------------+-----------+--------+------------+
5 rows in set (0.00 sec)

-- 以上这个语法有一种分页的效果,但是会随着start的增加,性能会下降,因为会扫描表(从 1 到 start)

-- 相对比较推荐的方法
mysql> select * from employees where emp_no > 20000 order by emp_no limit 5;
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date  |
+--------+------------+------------+-----------+--------+------------+
|  20001 | 1962-05-16 | Atreye     | Eppinger  | M      | 1990-04-18 |
|  20002 | 1955-12-25 | Jaber      | Brender   | M      | 1988-01-26 |
|  20003 | 1953-04-11 | Munehiko   | Coors     | F      | 1991-02-07 |
|  20004 | 1952-03-07 | Radoslaw   | Pfau      | M      | 1995-11-24 |
|  20005 | 1956-02-20 | Licheng    | Przulj    | M      | 1992-07-17 |
+--------+------------+------------+-----------+--------+------------+
5 rows in set (0.00 sec)
-- (当然推荐把热数据放cache里,比如Redis)
ORDER BY 是把已经查询好的结果集进行排序

WHERE

WHERE是将查询出来的结果,通过WHERE后面的条件(condition),对结果进行过滤

mysql> select * from employees  where emp_no > 30000  emp_no limit 4; -- 不加order by的limit是不确定的SQL
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date  |
+--------+------------+------------+-----------+--------+------------+
|  30001 | 1953-03-27 | Izaskun    | Morton    | M      | 1988-05-21 |
|  30002 | 1960-08-23 | Branimir   | Snedden   | M      | 1998-09-24 |
|  30003 | 1952-11-25 | Takahito   | Vilarrasa | M      | 1990-08-22 |
|  30004 | 1957-11-26 | Lucian     | Penttonen | F      | 1992-10-08 |
+--------+------------+------------+-----------+--------+------------+
4 rows in set (0.00 sec)

mysql> select * from employees  where emp_no > 40000  order by emp_no limit 4; 
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date  |
+--------+------------+------------+-----------+--------+------------+
|  40001 | 1956-03-28 | Akemi      | Maliniak  | F      | 1987-08-06 |
|  40002 | 1960-03-15 | Nakhoon    | Badr      | M      | 1990-02-13 |
|  40003 | 1960-01-26 | Jacopo     | Marshall  | F      | 1991-09-30 |
|  40004 | 1955-09-09 | Anneke     | Stiles    | F      | 1986-03-05 |
+--------+------------+------------+-----------+--------+------------+
4 rows in set (0.02 sec)

mysql> select * from employees  
    -> where emp_no > 40000
    -> and hire_date > '1991-01-01'   -- 可以用 and 进行 逻辑与 
    -> order by emp_no limit 4;
+--------+------------+------------+------------+--------+------------+
| emp_no | birth_date | first_name | last_name  | gender | hire_date  |
+--------+------------+------------+------------+--------+------------+
|  40003 | 1960-01-26 | Jacopo     | Marshall   | F      | 1991-09-30 |
|  40005 | 1961-02-27 | Zsolt      | Fairtlough | F      | 1991-07-08 |
|  40012 | 1955-02-07 | Chinhyun   | Ozeri      | F      | 1995-08-12 |
|  40015 | 1964-10-08 | Ioana      | Lemarechal | M      | 1997-08-07 |
+--------+------------+------------+------------+--------+------------+
4 rows in set (0.00 sec)

mysql> select * from employees
    -> where (emp_no > 40000 and birth_date > '1961-01-01') -- 使用()明确条件的逻辑规则
    ->    or (emp_no > 40000 and hire_date > '1991-01-01')  -- 可以使用 or 做 逻辑或
    -> order by emp_no limit 5;
+--------+------------+------------+------------+--------+------------+
| emp_no | birth_date | first_name | last_name  | gender | hire_date  |
+--------+------------+------------+------------+--------+------------+
|  40003 | 1960-01-26 | Jacopo     | Marshall   | F      | 1991-09-30 |
|  40005 | 1961-02-27 | Zsolt      | Fairtlough | F      | 1991-07-08 |
|  40006 | 1962-11-07 | Basim      | Panienski  | F      | 1986-12-27 |
|  40012 | 1955-02-07 | Chinhyun   | Ozeri      | F      | 1995-08-12 |
|  40015 | 1964-10-08 | Ioana      | Lemarechal | M      | 1997-08-07 |
+--------+------------+------------+------------+--------+------------+
5 rows in set (0.00 sec)

JOIN

image.png
--
-- ANSI SQL 89
-- 关联employees表和titles表
-- 要求是 employees的emp_no 等于 titles的emp_no
--
mysql> select * from employees,titles where employees.emp_no = titles.emp_no limit 5;
+--------+------------+------------+-----------+--------+------------+--------+-----------------+------------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date  | emp_no | title           | from_date  | to_date    |
+--------+------------+------------+-----------+--------+------------+--------+-----------------+------------+------------+
|  10001 | 1953-09-02 | Georgi     | Facello   | M      | 1986-06-26 |  10001 | Senior Engineer | 1986-06-26 | 9999-01-01 |
|  10002 | 1964-06-02 | Bezalel    | Simmel    | F      | 1985-11-21 |  10002 | Staff           | 1996-08-03 | 9999-01-01 |
|  10003 | 1959-12-03 | Parto      | Bamford   | M      | 1986-08-28 |  10003 | Senior Engineer | 1995-12-03 | 9999-01-01 |
|  10004 | 1954-05-01 | Chirstian  | Koblick   | M      | 1986-12-01 |  10004 | Engineer        | 1986-12-01 | 1995-12-01 |
|  10004 | 1954-05-01 | Chirstian  | Koblick   | M      | 1986-12-01 |  10004 | Senior Engineer | 1995-12-01 | 9999-01-01 |
+--------+------------+------------+-----------+--------+------------+--------+-----------------+------------+------------+
5 rows in set (0.00 sec)

--
-- 在上面的基础上只显示emp_no,名字,性别和职位名称
--
mysql> select emp_no, concat(last_name,' ', first_name), gender, title 
    -> from employees,titles
    -> where employees.emp_no = titles.emp_no limit 5;
ERROR 1052 (23000): Column 'emp_no' in field list is ambiguous  -- 报错了,原因是emp_no两个表都有

mysql> select employees.emp_no, -- 指定了employees
    -> concat(last_name,' ', first_name), gender, title
    -> from employees,titles
    -> where employees.emp_no = titles.emp_no limit 5;
+--------+-----------------------------------+--------+-----------------+
| emp_no | concat(last_name,' ', first_name) | gender | title           |
+--------+-----------------------------------+--------+-----------------+
|  10001 | Facello Georgi                    | M      | Senior Engineer |
|  10002 | Simmel Bezalel                    | F      | Staff           |
|  10003 | Bamford Parto                     | M      | Senior Engineer |
|  10004 | Koblick Chirstian                 | M      | Engineer        |
|  10004 | Koblick Chirstian                 | M      | Senior Engineer |
+--------+-----------------------------------+--------+-----------------+    

mysql> select employees.emp_no,
    -> concat(last_name,' ', first_name) as emp_name, gender, title  -- 对名字的列取一个别名叫emp_name
    -> from employees,titles
    -> where employees.emp_no = titles.emp_no limit 5;
+--------+-------------------+--------+-----------------+
| emp_no | emp_name          | gender | title           |  -- 这里就显示了emp_name
+--------+-------------------+--------+-----------------+
|  10001 | Facello Georgi    | M      | Senior Engineer |
|  10002 | Simmel Bezalel    | F      | Staff           |
|  10003 | Bamford Parto     | M      | Senior Engineer |
|  10004 | Koblick Chirstian | M      | Engineer        |
|  10004 | Koblick Chirstian | M      | Senior Engineer |
+--------+-------------------+--------+-----------------+
5 rows in set (0.00 sec)

mysql> select e.emp_no,  -- 使用表的别名
    -> concat(last_name,' ', first_name) as emp_name, gender, title
    -> from employees as e,titles as t      -- 对表做别名
    -> where e.emp_no = t.emp_no limit 5;   -- 使用报表的别名
+--------+-------------------+--------+-----------------+
| emp_no | emp_name          | gender | title           |
+--------+-------------------+--------+-----------------+
|  10001 | Facello Georgi    | M      | Senior Engineer |
|  10002 | Simmel Bezalel    | F      | Staff           |
|  10003 | Bamford Parto     | M      | Senior Engineer |
|  10004 | Koblick Chirstian | M      | Engineer        |
|  10004 | Koblick Chirstian | M      | Senior Engineer |
+--------+-------------------+--------+-----------------+
5 rows in set (0.00 sec)

--
-- ANSI SQL 92
-- inner join ... on ...语法
--
mysql> select e.emp_no,
    -> concat(last_name,' ', first_name) as emp_name, gender, title
    -> from employees as e inner join titles as t  -- inner join 可以省略inner关键字
    -> on e.emp_no = t.emp_no limit 5;             -- 配合join使用on
+--------+-------------------+--------+-----------------+
| emp_no | emp_name          | gender | title           |
+--------+-------------------+--------+-----------------+
|  10001 | Facello Georgi    | M      | Senior Engineer |
|  10002 | Simmel Bezalel    | F      | Staff           |
|  10003 | Bamford Parto     | M      | Senior Engineer |
|  10004 | Koblick Chirstian | M      | Engineer        |
|  10004 | Koblick Chirstian | M      | Senior Engineer |
+--------+-------------------+--------+-----------------+
5 rows in set (0.00 sec)

--
-- 上面两种语句在效率上其实是一样的,只是语法上的区别
--
--- 第一种
mysql> explain select e.emp_no,
    -> concat(last_name,' ', first_name) as emp_name, gender, title
    -> from employees as e,titles as t
    -> where e.emp_no = t.emp_no limit 5;
+----+-------------+-------+------------+------+---------------+---------+---------+--------------------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key     | key_len | ref                | rows   | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+---------+---------+--------------------+--------+----------+-------------+
|  1 | SIMPLE      | e     | NULL       | ALL  | PRIMARY       | NULL    | NULL    | NULL               | 298124 |   100.00 | NULL        |
|  1 | SIMPLE      | t     | NULL       | ref  | PRIMARY       | PRIMARY | 4       | employees.e.emp_no |      1 |   100.00 | Using index |
+----+-------------+-------+------------+------+---------------+---------+---------+--------------------+--------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)

--- 第二种
mysql> explain select e.emp_no,
    -> concat(last_name,' ', first_name) as emp_name, gender, title
    -> from employees as e inner join titles as t
    -> on e.emp_no = t.emp_no limit 5;
+----+-------------+-------+------------+------+---------------+---------+---------+--------------------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key     | key_len | ref                | rows   | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+---------+---------+--------------------+--------+----------+-------------+
|  1 | SIMPLE      | e     | NULL       | ALL  | PRIMARY       | NULL    | NULL    | NULL               | 298124 |   100.00 | NULL        |
|  1 | SIMPLE      | t     | NULL       | ref  | PRIMARY       | PRIMARY | 4       | employees.e.emp_no |      1 |   100.00 | Using index |
+----+-------------+-------+------------+------+---------------+---------+---------+--------------------+--------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)

-- 通过explain看两条语句的执行计划,发现是一样的,所以性能上是一样的,只是语法的不同

OUTER JOIN

--
-- 左连接 left join
--
mysql> use burn_test
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> create table test_left_join_1(a int);
Query OK, 0 rows affected (0.16 sec)

mysql> create table test_left_join_2(b int); 
Query OK, 0 rows affected (0.14 sec)

mysql> insert into test_left_join_1 values (1);
Query OK, 1 row affected (0.03 sec)

mysql> insert into test_left_join_1 values (2);
Query OK, 1 row affected (0.03 sec)

mysql> insert into test_left_join_2 values (1); 
Query OK, 1 row affected (0.03 sec)

mysql> select * from test_left_join_1;
+------+
| a    |
+------+
|    1 |
|    2 |
+------+
2 rows in set (0.00 sec)

mysql> select * from test_left_join_2;
+------+
| b    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

mysql> select * from 
    -> test_left_join_1 as t1 
    -> left join   -- 使用left join
    -> test_left_join_2 as t2 
    -> on t1.a = t2.b;
+------+------+
| a    | b    |
+------+------+
|    1 |    1 |  -- 满足条件的,显示t2中该条记录的值
|    2 | NULL |  -- 不满足条件的,用NULL填充
+------+------+
2 rows in set (0.00 sec)
-- left join : 左表 left join 右表 on 条件;
--              左表全部显示,右表是匹配表,
--              如果右表的某条记录满足 [on 条件] 匹配,则该记录显示
--              如果右表的某条记录 不 满足 匹配,则该记录显示NULL

--
-- 右连接 right join (继续使用test_left_join_1和2两张表)
--
mysql> select * from
    -> test_left_join_1 as t1
    -> right join   -- 使用right join
    -> test_left_join_2 as t2
    -> on t1.a = t2.b;
+------+------+
| a    | b    |
+------+------+
|    1 |    1 |   -- 右表(t2)全部显示
+------+------+
1 row in set (0.00 sec)
-- right join : 左表 right join 右表 on 条件
--               右表全部显示,左边是匹配表
--               同样和left join,满足则显示,不满足且右表中有值,则填充NULL

mysql> insert into test_left_join_2 values (3);  -- t2 中再增加一条记录
Query OK, 1 row affected (0.03 sec)

mysql> select * from
    -> test_left_join_1 as t1
    -> right join
    -> test_left_join_2 as t2
    -> on t1.a = t2.b;
+------+------+
| a    | b    |
+------+------+
|    1 |    1 |
| NULL |    3 | -- 右表存在,左表没有,用NULL填充
+------+------+
2 rows in set (0.00 sec)

--
-- 查找在t1表,而不在t2表的数据
--
mysql> select * from 
    -> test_left_join_1 as t1 
    -> left join
    -> test_left_join_2 as t2 
    -> on t1.a = t2.b where t2.b is null;
+------+------+
| a    | b    |
+------+------+
|    2 | NULL |  -- 数据1 在t1和t2中都有,所以不显示
+------+------+
1 row in set (0.00 sec)


-- left join : left outer join , outer关键字可以省略
-- right join: right outer join , outer 关键字可以省略

-- join无论inner还是outer,列名不需要一样,甚至列的类型也可以不一样,会进行转换。
-- 一般情况下,表设计合理,需要关联的字段类型应该是一样的


--
-- 查找哪些员工不是经理
--
mysql> select e.emp_no,
    -> concat(last_name,' ', first_name) as emp_name, gender, d.dept_no
    -> from employees as e left join dept_manager as d
    -> on e.emp_no = d.emp_no
    -> where d.emp_no is null limit 5;
+--------+-------------------+--------+---------+
| emp_no | emp_name          | gender | dept_no | -- dept_no是dept_manager的字段
+--------+-------------------+--------+---------+
|  10001 | Facello Georgi    | M      | NULL    |
|  10002 | Simmel Bezalel    | F      | NULL    |
|  10003 | Bamford Parto     | M      | NULL    |
|  10004 | Koblick Chirstian | M      | NULL    |
|  10005 | Maliniak Kyoichi  | M      | NULL    |
+--------+-------------------+--------+---------+
5 rows in set (0.00 sec)

-- 在 inner join中,过滤条件放在where或者on中都是可以的
-- 在 outer join中 条件放在where和on中是不一样的
mysql> select * from 
    -> test_left_join_1 as t1
    -> left join
    -> test_left_join_2 as t2
    -> on t1.a = t2.b
    -> where t2.b is null;
+------+------+
| a    | b    |
+------+------+
|    2 | NULL |
+------+------+
1 row in set (0.00 sec)

mysql> select * from 
    -> test_left_join_1 as t1
    -> left join
    -> test_left_join_2 as t2
    -> on t1.a = t2.b
    -> and t2.b is null;  -- 除了a=b, 还要找到b=null的,但是b里面没有null,所有a全部显示,b全为null
+------+------+
| a    | b    |
+------+------+
|    1 | NULL |
|    2 | NULL |
+------+------+
2 rows in set (0.00 sec)

-- ON 参与outer join的结果的生成,而where只是对结果的一个过滤

GROUP BY

--
-- 找出同一个部门的员工数量
--
mysql> select dept_no, count(dept_no)  -- count是得到数量,这里就是分组函数
    -> from dept_emp
    -> group by dept_no;  -- 通过 dept_no 分组
+---------+----------------+
| dept_no | count(dept_no) |
+---------+----------------+
| d001    |          20211 |
| d002    |          17346 |
| d003    |          17786 |
| d004    |          73485 |
| d005    |          85707 |
| d006    |          20117 |
| d007    |          52245 |
| d008    |          21126 |
| d009    |          23580 |
+---------+----------------+
9 rows in set (0.10 sec)

--
-- 选出部门人数 > 50000 
-- 
mysql> select dept_no, count(dept_no)
    -> from dept_emp
    -> group by dept_no
    -> having count(dept_no) > 50000;  -- 如果是对分组的聚合函数做过滤,使用having,用where报语法错误
+---------+----------------+
| dept_no | count(dept_no) |
+---------+----------------+
| d004    |          73485 |
| d005    |          85707 |
| d007    |          52245 |
+---------+----------------+
3 rows in set (0.09 sec)

--
-- 每个用户每个月产生的订单数目
--
mysql> desc orders;
+-----------------+-------------+------+-----+---------+-------+
| Field           | Type        | Null | Key | Default | Extra |
+-----------------+-------------+------+-----+---------+-------+
| o_orderkey      | int(11)     | NO   | PRI | NULL    |       |  -- 订单ID
| o_custkey       | int(11)     | YES  | MUL | NULL    |       |  -- 客户ID
| o_orderstatus   | char(1)     | YES  |     | NULL    |       |
| o_totalprice    | double      | YES  |     | NULL    |       |
| o_orderDATE     | date        | YES  | MUL | NULL    |       |  -- 订单日期
| o_orderpriority | char(15)    | YES  |     | NULL    |       |
| o_clerk         | char(15)    | YES  |     | NULL    |       |
| o_shippriority  | int(11)     | YES  |     | NULL    |       |
| o_comment       | varchar(79) | YES  |     | NULL    |       |
+-----------------+-------------+------+-----+---------+-------+
9 rows in set (0.00 sec)

mysql> select  o_orderkey, o_custkey, o_orderDATE from orders limit 3; 
+------------+-----------+-------------+
| o_orderkey | o_custkey | o_orderDATE |
+------------+-----------+-------------+
|          1 |     36901 | 1996-01-02  |
|          2 |     78002 | 1996-12-01  |
|          3 |    123314 | 1993-10-14  |
+------------+-----------+-------------+
3 rows in set (0.00 sec)


--
-- 查找客户每年每月产生的订单数
--

mysql> select o_custkey, count(o_orderkey),                                                                  -> year(o_orderDATE), month(o_orderDATE)
    -> from orders
    -> group by o_custkey, year(o_orderDATE), month(o_orderDATE)
    -> limit 10;
+-----------+-------------------+-------------------+--------------------+
| o_custkey | count(o_orderkey) | year(o_orderDATE) | month(o_orderDATE) |
+-----------+-------------------+-------------------+--------------------+
|         1 |                 1 |              1992 |                  4 |
|         1 |                 1 |              1992 |                  8 |
|         1 |                 1 |              1996 |                  6 |
|         1 |                 1 |              1996 |                  7 |
|         1 |                 1 |              1996 |                 12 |
|         1 |                 1 |              1997 |                  3 |
|         2 |                 1 |              1992 |                  4 |
|         2 |                 1 |              1994 |                  5 |
|         2 |                 1 |              1994 |                  8 |
|         2 |                 1 |              1994 |                 12 |
+-----------+-------------------+-------------------+--------------------+
10 rows in set (8.97 sec)

-- 使用 date_format 函数

mysql> select o_custkey, count(o_orderkey),
    -> date_format(o_orderDATE, '%Y-%m') as date
    -> from orders
    -> group by o_custkey, date_format(o_orderDATE, '%Y-%m')
    -> limit 10;
+-----------+-------------------+---------+
| o_custkey | count(o_orderkey) | date    |
+-----------+-------------------+---------+
|         1 |                 1 | 1992-04 |
|         1 |                 1 | 1992-08 |
|         1 |                 1 | 1996-06 |
|         1 |                 1 | 1996-07 |
|         1 |                 1 | 1996-12 |
|         1 |                 1 | 1997-03 |
|         2 |                 1 | 1992-04 |
|         2 |                 1 | 1994-05 |
|         2 |                 1 | 1994-08 |
|         2 |                 1 | 1994-12 |
+-----------+-------------------+---------+
10 rows in set (11.46 sec)


子查询


子查询就是指在一个select语句中嵌套另一个select语句。同时,子查询必须包含括号。
MySQL 5.6.x 版本之前,MySQL的子查询性能较差,但是从5.6开始,不存在性能差的问题。

  • ANY / SOME

如果外部查询的列的结果和子查询的列的结果比较得到为True的话,则返回比较值为True的(外查询)的记录

mysql> create table  t1 (a int);
Query OK, 0 rows affected (0.15 sec)

mysql> create table  t2 (a int); 
Query OK, 0 rows affected (0.14 sec)

mysql> insert into t1 values (4), (10);
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> insert into t2 values(5), (12), (15);
Query OK, 3 rows affected (0.01 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from t1;
+------+
| a    |
+------+
|    4 |
|   10 |
+------+
2 rows in set (0.00 sec)

mysql> select * from t2;
+------+
| a    |
+------+
|    5 |
|   12 |
|   15 |
+------+
3 rows in set (0.00 sec)

mysql> select * from t1 where a > any ( select * from t2 );
+------+
| a    |
+------+    -- 10 比 5 大为True,则返回该值,4比t2中所有的a值小,为False
|   10 |
+------+
1 row in set (0.00 sec)

-- 返回(5, 12, 15)
-- t1中a列的值,只要大于(5, 12, 15)中任意一值
-- 即t1.a > t2.a为True,则返回对应的t1.a

-- 这个查询可以解释为,t1表内a列的值 大于 t2表中a列的任意(any)一个值(t1.a > any(t2.a) == true),则返回t1.a的记录
ANY关键词必须与一个比较操作符一起使用: =, >, <, >=, <=, <> (这个是!=的意思)
子查询中SOME和ANY是同一个意思
  • IN

in是ANY的一种特殊情况:"in" equals "= any"

mysql> insert into t2 select 4;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> select * from t1 where a = any( select * from t2 );
+------+
| a    |
+------+
|    4 |
+------+
1 row in set (0.00 sec)

mysql>
# in的结果等同于 =any 的结果
mysql> select a from t1 where a in ( select a from t2 );
+------+
| a    |
+------+
|    4 |
+------+
1 row in set (0.00 sec)

# select a from s1 where a in (select a in t2);是用的比较多的一种语法

ALL

如果外部查询的列的结果和子查询的列的所有结果比较得到为True的话,则返回比较值为True的(外查询)的记录

# 清空 t1 , t2
mysql> truncate t1;
Query OK, 0 rows affected (0.02 sec)

mysql> truncate table t2;
Query OK, 0 rows affected (0.02 sec)

mysql> insert into t1 values (4), (10);
Query OK, 2 rows affected (0.01 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> insert into t2 values (3), (4), (5);
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select a from t1 where a > all( select a from t2 );
+------+
| a    |
+------+
|   10 |
+------+
1 row in set (0.00 sec)
# (10 > 5, 4, 3 为 True) 而 (4 >5, 4, 3 为 False)

mysql>

ALL关键词必须与一个比较操作符一起使用
NOT IN 是 <> ALL的别名

子查询的分类

  • 独立子查询
    • 不依赖外部查询而运行的子查询
mysql> select * from t1 where a in( 1, 2, 3, 4, 5 );
+------+
| a    |
+------+
|    4 |
+------+
1 row in set (0.00 sec)

mysql>

  • 相关子查询
    • 引用了外部查询列的子查询
mysql> select a from t1 where a in (select * from t2 where t1.a = t2.a);
+------+
| a    |
+------+
|    4 |
+------+
1 row in set (0.00 sec)

子查询的优化

  • 在MySQL5.6之前,优化器会把子查询重写成exists的形式

select a from t1 where a in (select a from t2); 这个是一条独立的子查询,时间复杂度 O(M+N)
经过优化器重写后
select a from t1 where exists (select 1 from t2 where t1.a = t2.a); 这是相关子查询,复杂度O(M*N + M)
所以在MySQL 5.6之前,部分的子查询需要重写成join的形式 (注意表的大小)

mysql> select t1.a from t1 join t2 on t1.a = t2.a;
+------+
| a    |
+------+
|    4 |
+------+
1 row in set (0.00 sec)

mysql>
  • 在MySQL 5.6之后,优化器不会将子查询重写成exists的形式,而是自动优化,性能有了大幅提升

包含NULL值的NOT IN

# MySQL数据库的比较操作,除了返回1(True), 0(False)之外,还会返回NULL 
# NULL和NULL的比较,返回的还是NULL
mysql> select null in ( 'a', 'b', null );
+----------------------------+
| null in ( 'a', 'b', null ) |
+----------------------------+
|                       NULL |
+----------------------------+
1 row in set (0.00 sec)

# null不在('a', 'b', null)中,返回的还是null,因为有null和null的比较
mysql> select null not in ( 'a', 'b', null );
+--------------------------------+
| null not in ( 'a', 'b', null ) |
+--------------------------------+
|                           NULL |
+--------------------------------+
1 row in set (0.00 sec)

# a 不在 ('a', 'b', null)中,返回0,即False
mysql> select 'a' not in ( 'a', 'b', null );
+-------------------------------+
| 'a' not in ( 'a', 'b', null ) |
+-------------------------------+
|                             0 |
+-------------------------------+
1 row in set (0.00 sec)

# 理论上应该是返回1,即True的。但是包含了null值。则返回null
mysql> select 'c' not in ( 'a', 'b', null );
+-------------------------------+
| 'c' not in ( 'a', 'b', null ) |
+-------------------------------+
|                          NULL |
+-------------------------------+
1 row in set (0.00 sec)

# 这个返回值可以理解 'c'不在('a', 'b')中,返回1,即为True
mysql> select 'c' not in ( 'a', 'b' );
+-------------------------+
| 'c' not in ( 'a', 'b' ) |
+-------------------------+
|                       1 |
+-------------------------+
1 row in set (0.00 sec)

mysql>

对于包含了NULL值的IN操作,总是返回True或者NULL
NOT IN返回NOT True (False)或者NOT NULL (NULL)

1.--
2.-- SQL语句一 使用 EXISTS
3.--
4.select customerid, companyname 
5.    from customers as A
6.    where country = 'Spain' 
7.        and not exists
8.            ( select * from orders as B
9.              where A.customerid = B.customerid );
10.
11.--
12.-- SQL语句二 使用 IN
13.--
14.select customerid, companyname 
15.    from customers as A
16.    where country = 'Spain' 
17.        and customerid not in (select customerid from orders);
18.
19.-----
20.-- 当结果集合中没有NULL值时,上述两条SQL语句查询的结果是一致的 
21.-----
22.
23.--
24.-- 插入一个NULL值
25.--
26.insert into orders(orderid) values (null);
27.
28.-----
29.-- SQL语句1 : 返回和之前一致
30.-- SQL语句2 : 返回为空表,因为子查询返回的结果集中存在NULL值。not in null 永远返回False或者NULL
31.--            此时 where (country = 'Spain' and (False or NULL)) 为 False OR NULL,条件永远不匹配
32.-----
33.
34.--
35.-- SQL语句2 改写后
36.--
37.select customerid, companyname 
38.    from customers as A
39.    where country = 'Spain' 
40.        and customerid not in (select customerid from orders 
41.                                where customerid is not null);  -- 增加这个过滤条件,使用is not,而不是<>
42.
43.
44.--
45.-- 和 null比较,使用is和is not, 而不是 = 和 <>
46.--
47.mysql> select null = null; 
48.+-------------+
49.| null = null |
50.+-------------+
51.|        NULL |
52.+-------------+
53.1 row in set (0.00 sec)
54.
55.mysql> select null <> null;
56.+--------------+
57.| null <> null |
58.+--------------+
59.|         NULL |
60.+--------------+
61.1 row in set (0.00 sec)
62.
63.mysql> select null is null; 
64.+--------------+
65.| null is null |
66.+--------------+
67.|            1 |  -- 返回 True
68.+--------------+
69.1 row in set (0.00 sec)
70.
71.mysql> select null is not  null;
72.+-------------------+
73.| null is not  null |
74.+-------------------+
75.|                 0 |  -- 返回 False
76.+-------------------+
77.1 row in set (0.00 sec)

EXISTS不管返回值是什么,而是看是否有行返回,所以EXISTS中子查询都是select *、select 1等,因为只关心返回是否有行(结果集)


INSERT



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

INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
    [INTO] tbl_name
    [PARTITION (partition_name [, partition_name] ...)]
    SET assignment_list
    [ON DUPLICATE KEY UPDATE assignment_list]

INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
    [INTO] tbl_name
    [PARTITION (partition_name [, partition_name] ...)]
    [(col_name [, col_name] ...)]
    SELECT ...
    [ON DUPLICATE KEY UPDATE assignment_list]

value:
    {expr | DEFAULT}

value_list:
    value [, value] ...

assignment:
    col_name = value

assignment_list:
    assignment [, assignment] ...

操作

# 插入一个值
mysql> insert into t1 values(3);
Query OK, 1 row affected (0.01 sec)

# 插入多个值
mysql> insert into t1 values(5),(6),(8);
Query OK, 3 rows affected (0.03 sec)
Records: 3  Duplicates: 0  Warnings: 0

# insert XXX select XXX 语法
mysql> insert into t1 select 23;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

# 有多个列
mysql> create table t3 ( a int, b int );
Query OK, 0 rows affected (0.16 sec)

# 没有指定列,报错
mysql> insert into t3 select 8;
ERROR 1136 (21S01): Column count doesn't match value count at row 1

# 指定 a 列
mysql> insert into t3(a) select 8;
Query OK, 1 row affected (0.01 sec)
Records: 1  Duplicates: 0  Warnings: 0

# 不指定列,但是插入值匹配列的个数和类型
mysql> insert into t3 select 8, 9;
Query OK, 1 row affected (0.01 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> select * from t3;
+------+------+
| a    | b    |
+------+------+
|    8 | NULL |
|    8 |    9 |
+------+------+
2 rows in set (0.00 sec)

# 从t2表中查询数据并插入到t3(a)中,注意指定列
mysql> insert into t3(b) select a from t2;
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from t3;
+------+------+
| a    | b    |
+------+------+
|    8 | NULL |
|    8 |    9 |
| NULL |    3 |
| NULL |    4 |
| NULL |    5 |
+------+------+
5 rows in set (0.00 sec)

# 如果想快速增长表格中的数据,可以使用如下方法,使得数据成倍增长
mysql> insert into t3(a) select * from t2;
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from t3;
+------+------+
| a    | b    |
+------+------+
|    8 | NULL |
|    8 |    9 |
| NULL |    3 |
| NULL |    4 |
| NULL |    5 |
|    3 | NULL |
|    4 | NULL |
|    5 | NULL |
+------+------+
8 rows in set (0.00 sec)

mysql> insert into t3(a) select * from t2;
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from t3;
+------+------+
| a    | b    |
+------+------+
|    8 | NULL |
|    8 |    9 |
| NULL |    3 |
| NULL |    4 |
| NULL |    5 |
|    3 | NULL |
|    4 | NULL |
|    5 | NULL |
|    3 | NULL |
|    4 | NULL |
|    5 | NULL |
+------+------+
11 rows in set (0.00 sec)

mysql>

DELETE


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

操作

# 根据过滤条件删除
mysql> select * from t3;
+------+------+
| a    | b    |
+------+------+
|    8 | NULL |
|    8 |    9 |
| NULL |    3 |
| NULL |    4 |
| NULL |    5 |
|    3 | NULL |
|    4 | NULL |
|    5 | NULL |
|    3 | NULL |
|    4 | NULL |
|    5 | NULL |
+------+------+
11 rows in set (0.00 sec)

# 删除整个t3表数据
mysql> delete from t3 where a is null;
Query OK, 3 rows affected (0.01 sec)

mysql> select * from t3;
+------+------+
| a    | b    |
+------+------+
|    8 | NULL |
|    8 |    9 |
|    3 | NULL |
|    4 | NULL |
|    5 | NULL |
|    3 | NULL |
|    4 | NULL |
|    5 | NULL |
+------+------+
8 rows in set (0.00 sec)

mysql> delete from t3;
Query OK, 8 rows affected (0.01 sec)

mysql> select * from t3;
Empty set (0.00 sec)

mysql>


UPDATE


UPDATE [LOW_PRIORITY] [IGNORE] table_reference
    SET assignment_list
    [WHERE where_condition]
    [ORDER BY ...]
    [LIMIT row_count]

value:
    {expr | DEFAULT}

assignment:
    col_name = value

assignment_list:
    assignment [, assignment] ...

操作

mysql> insert into t3 select 1,2;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> select * from t3;
+------+------+
| a    | b    |
+------+------+
|    1 |    2 |
+------+------+
1 row in set (0.00 sec)

mysql> update t3 set a = 100 where a = 1;
Query OK, 1 row affected (0.02 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from t3;
+------+------+
| a    | b    |
+------+------+
|  100 |    2 |
+------+------+
1 row in set (0.00 sec)

mysql> select * from t1;
+------+
| a    |
+------+
|    4 |
|   10 |
|    1 |
|    3 |
|    5 |
|    6 |
|    8 |
|   23 |
+------+
8 rows in set (0.00 sec)

mysql> select * from t2;
+------+
| a    |
+------+
|    3 |
|    4 |
|    5 |
+------+
3 rows in set (0.00 sec)

# 关联更新,先得到t1.a=t2.a的结果集,然后将结果集中的t1.a设置为100
mysql> update t1 join t2 on t1.a = t2.a set t1.a = 100;
Query OK, 3 rows affected (0.03 sec)
Rows matched: 3  Changed: 3  Warnings: 0

mysql> select * from t1;
+------+
| a    |
+------+
|  100 |
|   10 |
|    1 |
|  100 |
|  100 |
|    6 |
|    8 |
|   23 |
+------+
8 rows in set (0.00 sec)

mysql>


REPLACE


REPLACE [LOW_PRIORITY | DELAYED]
    [INTO] tbl_name
    [PARTITION (partition_name [, partition_name] ...)]
    [(col_name [, col_name] ...)]
    {VALUES | VALUE} (value_list) [, (value_list)] ...

REPLACE [LOW_PRIORITY | DELAYED]
    [INTO] tbl_name
    [PARTITION (partition_name [, partition_name] ...)]
    SET assignment_list

REPLACE [LOW_PRIORITY | DELAYED]
    [INTO] tbl_name
    [PARTITION (partition_name [, partition_name] ...)]
    [(col_name [, col_name] ...)]
    SELECT ...

value:
    {expr | DEFAULT}

value_list:
    value [, value] ...

assignment:
    col_name = value

assignment_list:
    assignment [, assignment] ...

replace的原理是:先delete,在insert,如果没有替换对象时,类似于插入效果

操作

mysql> create table t4 ( a int auto_increment primary key, b int );
Query OK, 0 rows affected (0.03 sec)

mysql> insert into t4 values (null, 11);
Query OK, 1 row affected (0.02 sec)

mysql> insert into t4 values (null, 12);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t4 values (null, 13);
Query OK, 1 row affected (0.01 sec)

mysql> select * from t4;
+---+------+
| a | b    |
+---+------+
| 1 |   11 |
| 2 |   12 |
| 3 |   13 |
+---+------+
3 rows in set (0.00 sec)

mysql> replace into t4 values ( 1, 100);
Query OK, 2 rows affected (0.00 sec)

mysql> select * from t4;
+---+------+
| a | b    |
+---+------+
| 1 |  100 |
| 2 |   12 |
| 3 |   13 |
+---+------+
3 rows in set (0.00 sec)

mysql> replace into t4 values (5, 200);
Query OK, 1 row affected (0.01 sec)

mysql> select * from t4;
+---+------+
| a | b    |
+---+------+
| 1 |  100 |
| 2 |   12 |
| 3 |   13 |
| 5 |  200 |
+---+------+
4 rows in set (0.01 sec)

mysql>
mysql> create table t5 ( a int primary key, b int auto_increment, c int, key(b));
Query OK, 0 rows affected (0.03 sec)

mysql> insert into t5 values (10, null, 100);
Query OK, 1 row affected (0.01 sec)

mysql> insert into t5 values (20, null, 200);
Query OK, 1 row affected (0.01 sec)

mysql> select * from t5;
+----+---+------+
| a  | b | c    |
+----+---+------+
| 10 | 1 |  100 |
| 20 | 2 |  200 |
+----+---+------+
2 rows in set (0.00 sec)

mysql> replace into t5 values (10, null, 300);
Query OK, 2 rows affected (0.00 sec)

mysql> select * from t5;
+----+---+------+
| a  | b | c    |
+----+---+------+
| 10 | 3 |  300 |
| 20 | 2 |  200 |
+----+---+------+
2 rows in set (0.00 sec)

mysql>

insert on duplicate

mysql> select * from t4;
+---+------+
| a | b    |
+---+------+
| 1 |  100 |
| 2 |   12 |
| 3 |   13 |
| 5 |  200 |
+---+------+
4 rows in set (0.00 sec)

mysql> show create table t4\G
*************************** 1. row ***************************
       Table: t4
Create Table: CREATE TABLE `t4` (
  `a` int(11) NOT NULL AUTO_INCREMENT,
  `b` int(11) DEFAULT NULL,
  PRIMARY KEY (`a`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)

# 主键1 已存在,报错
mysql> insert into t4 values (1, 1);
ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'

# 带上 on duplicate参数,这是非sql标准,不推荐
mysql> insert into t4 values(1, 1) on duplicate key update b = 1;
Query OK, 2 rows affected (0.00 sec)

mysql> select * from t4;
+---+------+
| a | b    |
+---+------+
| 1 |    1 |
| 2 |   12 |
| 3 |   13 |
| 5 |  200 |
+---+------+
4 rows in set (0.00 sec)

mysql>

INSERT IGNORE 忽略重复的错误

mysql> insert ignore t4 values(1, 1);
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> show warnings;
+---------+------+---------------------------------------+
| Level   | Code | Message                               |
+---------+------+---------------------------------------+
| Warning | 1062 | Duplicate entry '1' for key 'PRIMARY' |
+---------+------+---------------------------------------+
1 row in set (0.00 sec)

mysql>


其他操作


  • 更新有关系的值

mysql> create table t6 ( a int, b int );
Query OK, 0 rows affected (0.03 sec)

mysql> insert into t6 values (1, 1);
Query OK, 1 row affected (0.00 sec)

mysql> select * from t6;
+------+------+
| a    | b    |
+------+------+
|    1 |    1 |
+------+------+
1 row in set (0.00 sec)

mysql> update t6 set a=a+1, b=a where a=1;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from t6;
+------+------+
| a    | b    |
+------+------+
|    2 |    2 |
+------+------+
1 row in set (0.00 sec)
bgi
mysql>


UNION


  • UNION 的作用是将两个查询的结果集进行合并。
  • UNION必须由两条或两条以上的SELECT语句组成,语句之间用关键字UNION分隔。
  • UNION中的每个查询必须包含相同的列(类型相同或可以隐式转换)、表达式或聚集函数。
mysql> create table test_union_1(a int, b int);
Query OK, 0 rows affected (0.18 sec)

mysql> create table test_union_2(a int, c int);  
Query OK, 0 rows affected (0.15 sec)

mysql> insert into test_union_1 values(1, 2), (3, 4), (5, 6), (10, 20);
Query OK, 4 rows affected (0.06 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> insert into test_union_2 values(10, 20), (30, 40), (50, 60);  
Query OK, 3 rows affected (0.03 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from test_union_1;
+------+------+
| a    | b    |
+------+------+
|    1 |    2 |
|    3 |    4 |
|    5 |    6 |
|   10 |   20 |  -- test_union_1 中的10, 20
+------+------+
4 rows in set (0.00 sec)

mysql> select * from test_union_2;
+------+------+
| a    | c    |
+------+------+
|   10 |   20 | -- test_union_2 中的10, 20
|   30 |   40 |
|   50 |   60 |  
+------+------+
3 rows in set (0.00 sec)

mysql> select a, b as t from test_union_1
    -> union
    -> select * from test_union_2;
+------+------+
| a    | t    |
+------+------+
|    1 |    2 |
|    3 |    4 |
|    5 |    6 |
|   10 |   20 | -- 只出现了一次 10, 20,union会去重
|   30 |   40 |
|   50 |   60 |
+------+------+
6 rows in set (0.00 sec)

mysql> select a, b as t from test_union_1
    -> union all   -- 使用 union all 可以不去重
    -> select * from test_union_2;
+------+------+
| a    | t    |
+------+------+
|    1 |    2 |
|    3 |    4 |
|    5 |    6 |
|   10 |   20 | -- test_union_1 中的10, 20
|   10 |   20 | -- test_union_2 中的10, 20
|   30 |   40 |
|   50 |   60 |
+------+------+
7 rows in set (0.00 sec)

mysql> select a, b as t from test_union_1 where a > 2 
    -> union
    -> select * from test_union_2 where c > 50;  -- 使用where过滤也可以 
+------+------+
| a    | t    |
+------+------+
|    3 |    4 |
|    5 |    6 |
|   10 |   20 |
|   50 |   60 |
+------+------+
4 rows in set (0.00 sec)

你可能感兴趣的:(MySQL表操作)