MySQL

安装

1
sudo apt install mysql-server

安装过程中会提示你输入 root 用户的密码


安装 MySQL Workbench:

查看版本号

1
2
3
4
5
6
mysql> select version();
+------------------------------------------+
| VERSION() |
+------------------------------------------+
| 5.5.8-mycat-1.5.1-RELEASE-20161110104159 |
+------------------------------------------+

基本使用

1
2
# 重启
sudo service mysql restart

默认配置文件:

1
/etc/mysql/mysql.conf.d/mysqld.cnf

以下话摘自 mysql --help | more:

1
2
Default options are read from the following files in the given order:
/etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf

遇到的问题:

1
2
mysql> use mysql;
ERROR 1044 (42000): Access denied for user 'root'@'%' to database 'mysql'

运行如下语句:

1
SELECT USER(),CURRENT_USER();
  • USER() reports how you 尝试认证 in MySQL
  • CURRENT_USER() reports how you 被允许认证 in MySQL

你登录的时候有什么特权:

1
SHOW GRANTS;

创建用户并授权:

1
2
CREATE USER slave_user;
GRANT REPLICATION SLAVE ON *.* TO slave_user IDENTIFIED BY '123456';
  • REPLICATION SLAVE 权限并没有什么特别之处,只是这个用户能够从 Master 上取得二进制日志的转储数据

When mysqld starts, it reads all grant table contents into memory. The in-memory tables become effective for access control at that point.

If you modify the grant tables 非直接地 using account-management statements such as GRANT, REVOKE, SET PASSWORD, or RENAME USER, the server notices these changes and loads the grant tables into memory again 立即.

If you modify the grant tables 直接地 using statements such as INSERT, UPDATE, or DELETE, your changes have no effect on privilege checking until you either 重启服务器 or tell it to reload the tables. If you change the grant tables directly but forget to reload them, your changes have no effect until you restart the server. This may leave you wondering why your changes seem to make no difference!

To tell the server to reload the grant tables, perform a flush-privileges operation. This can be done by issuing a FLUSH PRIVILEGES statement.

1) Global Privileges:

2) Database Privileges:

3) Table Privileges:

账户和密码:

A user value in a GRANT statement indicates a MySQL account to which the statement applies. To accommodate granting rights to users from arbitrary hosts, MySQL supports specifying the user value in the form ‘user_name’@’host_name’.

You can specify 通配符 in the host name. For example, 'user_name'@'%.example.com' applies to user_name for 任何 host in the example.com domain, and 'user_name'@'192.168.1.%' applies to user_name for any host in the 192.168.1 class C subnet.

The simple form ‘user_name’ is a synonym 同义词 for ‘user_name’@’%’.


删除用户:

1
DROP USER [IF EXISTS] user [, user] ...

查看已有用户:

1
2
USE mysql
SELECT User, Host FROM `user`;

地址绑定:

1
--bind-address=addr

The server treats different types of addresses as follows:

  • If the address is 0.0.0.0, the server accepts TCP/IP connections on 所有 server host IPv4 interfaces.
  • If the address is ::, the server accepts TCP/IP connections on all 所有 host IPv4 and IPv6 interfaces. Use this address to permit both IPv4 and IPv6 connections on all server interfaces.
  • If the address is an IPv4-mapped address, the server accepts TCP/IP connections for that address, in either IPv4 or IPv6 format. For example, if the server is bound to ::ffff:127.0.0.1, clients can connect using –host=127.0.0.1 or –host=::ffff:127.0.0.1.
  • If the address is a “regular” IPv4 or IPv6 address (such as 127.0.0.1 or ::1), the server accepts TCP/IP connections only for that IPv4 or IPv6 address.

数据库远程连接不上,多半是 bind-addressroot 问题搞得鬼:

这个时候,需要执行语句给 ROOT 赋上权限即可:

1
2
mysql> update mysql.user set host = '%' where user = 'root';
mysql> flush privileges;

主从复制

1) 从库从主库克隆已有数据:

主库 (锁表 -> 查询从库复制开始位置 -> 备份 -> 解锁):

1
2
3
4
FLUSH TABLES WITH READ LOCK;
SHOW MASTER STATUS;
mysqldump -uroot -p --all-databases > all.sql
UNLOCK TABLES;

从主库机器上拷贝 all.sql 到从库:

1
rsync all.sql zk@10.108.113.85:~/Desktop/

从库恢复数据:

1
mysql> source /home/zk/Desktop/all.sql

2) 执行同步:

主库查看一下从库复制的起始位置:

从库执行如下命令 (下面语句必须在 SLAVE 机器上的拥有 SUPER 权限的用户来执行):

1
2
3
4
5
STOP SLAVE IO_THREAD FOR CHANNEL '';
RESET SLAVE;
CHANGE MASTER TO MASTER_HOST = '10.108.123.112', MASTER_PORT = 3306, MASTER_USER = 'slave_user', MASTER_PASSWORD = '123456', MASTER_LOG_FILE = 'master-bin.000123', MASTER_LOG_POS = 2559;
START SLAVE;
SHOW SLAVE STATUS;

遇见的问题:

1
2
mysql> change master to master_host = '10.108.123.112', master_password = '123456', master_user = 'slave_user', master_log_file = 'master-bin.000123', master_log_pos = 1995;
ERROR 3021 (HY000): This operation cannot be performed with a running slave io thread; run STOP SLAVE IO_THREAD FOR CHANNEL '' first.

可以参考: How To Set Up Master Slave Replication in MySQL


显示 MASTERSLAVE 的状态可以使用如下命令,但是查询显示的结果可读性不好:

1
2
SHOW MASTER STATUS;
SHOW SLAVE STATUS;

如果想要更好的可读性,可以在后面加上 \G 这两个字符,其实查询语句也可以通过加上这两个字符来获得更好的阅读效果:

1
2
mysql> SHOW MASTER STATUS\G
mysql> SHOW SLAVE STATUS\G


查看复制线程有没有问题:

1
mysql> SHOW PROCESS LIST\G

二进制日志

二进制仅包含可能改变数据库的语句,它包含若干个文件:

由于二进制日志是公共资源,所有线程都向它写入语句,所以避免两个线程同时更新二进制日志很重要。为此,在事件写二进制日志之前,二进制日志需要获得一个互斥锁 LOCK_log,然后在事件写完成够释放。

my.cnf 配置文件中可以找到 MySQL 数据存放的位置:

或者也可以像下面这样执行命令找到:

1
2
3
4
5
6
7
mysql> SHOW VARIABLES LIKE 'datadir';
+---------------+-----------------------+
| Variable_name | Value |
+---------------+-----------------------+
| datadir | /usr/local/mysql/var/ |
+---------------+-----------------------+
1 row in set (0.00 sec)

我们是没有权限进入这个数据文件夹的,即使使用 sudo 也不行:

但是我们可以使用 mysqlbinlog 这个工具来装载 binlog 文件:

1
mysqlbinlog /var/lib/mysql/master-bin.000125
  • --short-form: 忽略关于二进制日志中的事件的注释信息
  • --read-from-remote-server: 远程读取
  • --host=master.example.com: 远程主机
  • --user=repl_user: 账户
  • --password: 密码

MySQL Workbeanch

双击修改某一个表的某一个字段之后,一定要记住点击右下角的 Apply:

MySQL 指定配置文件

1
mysql --defaults-file=/home/user/.my.cnf database

一台机器上运行多个 MySQL Instances

1
2
3
4
5
6
mysqld --port=3307\
--socket=3307.socket\
--pid-file=3307.pid\
--log-error=3307-error.log\
--general_log_file=3307-log.log\
--datadir=./3307-data-dir

名词解释

  • mysqld: MySQL 服务器
  • mysql: MySQL 客户端命令行程序

正则匹配

1
select name from table1 where name regexp binary '^CU[0-9]'

The documentation for regexp is here. binary is required to 确保大小写匹配


百分号 % 字符来表示任意字符

创建表和数据库示例

1
2
3
4
5
6
7
8
9
10
11
# 设置为 utf8 解决各种字符错误问题
CREATE DATABASE my_db CHARACTER SET utf8 COLLATE utf8_general_ci;
# 创建表和外键示例
CREATE TABLE IF NOT EXISTS `my_db`.`my_table` (
`id` VARCHAR(255) NOT NULL COMMENT 'my id',
`user_name` VARCHAR(255) NOT NULL COMMENT 'user_name',
`user_password` VARCHAR(255) NOT NULL COMMENT 'user_password',
`publish_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'article publish time',
PRIMARY KEY (`id`),
FOREIGN KEY (`user_name`) REFERENCES `ref_table`(`user_name`) ON DELETE CASCADE
) ENGINE = InnoDB;

数据库设置允许远程访问

1
2
3
4
5
6
7
# sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf
# 将 `mysqld.conf` 文件里面的下面这个 `bind-address` 给注释掉即可
# MySQL 默认只允许本机访问

# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
bind-address = 127.0.0.1

启动、停止、查看状态

1
2
3
4
sudo /etc/init.d/mysql start
sudo /etc/init.d/mysql stop
sudo /etc/init.d/mysql status
sudo /etc/init.d/mysql help

无法连接 MySQL 数据库

1
ERROR 2002 (HY000): Can't connect to local MySQL server through socket         '/var/run/mysqld/mysqld.sock' (2)

查看 MySQL 错误日志:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2017-11-21T12:08:01.649118Z 31311 [Note] Aborted connection 31311 to db: 'sogo' user: 'sogo' host: 'localhost' (Got an error reading communication packets)
2017-11-21T12:08:01.760904Z 31312 [Note] Aborted connection 31312 to db: 'sogo' user: 'sogo' host: 'localhost' (Got an error reading communication packets)
2017-11-21T12:08:14.021729Z 0 [Note] Giving 7 client threads a chance to die gracefully
2017-11-21T12:08:14.021782Z 0 [Note] Shutting down slave threads
2017-11-21T12:08:16.021864Z 0 [Note] Forcefully disconnecting 4 remaining clients
2017-11-21T12:08:16.021908Z 0 [Warning] /usr/sbin/mysqld: Forcing close of thread 31298 user: 'iredapd'

2017-11-21T12:08:16.021952Z 0 [Warning] /usr/sbin/mysqld: Forcing close of thread 18101 user: 'root'

2017-11-21T12:08:16.021977Z 0 [Warning] /usr/sbin/mysqld: Forcing close of thread 18109 user: 'root'

2017-11-21T12:08:16.021997Z 0 [Warning] /usr/sbin/mysqld: Forcing close of thread 19628 user: 'root'

2017-11-21T12:08:16.022024Z 0 [Note] Event Scheduler: Purging the queue. 0 events
2017-11-21T12:08:16.022253Z 0 [Note] Binlog end
2017-11-21T12:08:16.202954Z 0 [Note] Shutting down plugin 'ngram'
2017-11-21T12:08:16.202980Z 0 [Note] Shutting down plugin 'partition'
2017-11-21T12:08:16.202984Z 0 [Note] Shutting down plugin 'BLACKHOLE'
2017-11-21T12:08:16.202988Z 0 [Note] Shutting down plugin 'ARCHIVE'
2017-11-21T12:08:16.202991Z 0 [Note] Shutting down plugin 'PERFORMANCE_SCHEMA'
...

暂时不知道为什么会死去…


Try this:

1
mysql -h 127.0.0.1 -P 3306 -u root -p <database>

Also (to see if it’s running):

1
telnet 127.0.0.1 3306

修改列的定义

1
ALTER TABLE `my_db` CHANGE COLUMN `column_a` `column_a` VARCHAR(225) NULL DEFAULT NULL COMMENT 'enlarge column capacity' ;

修复时间戳插入错误的问题

MySQL Workbench 客户端中查看的值是 0000-00-00 00:00:00,说明 create_timeTIMESTAMP 时间戳类型中已经插入了错误的值,需要采用如下方式来恢复一下:

1
update `my_db` set `create_time` = NULL where `create_time` = 0;

不能采用:

1
update `my_db` set `create_time` = NULL where `create_time` = '0000-00-00 00:00:00';

来更新,不管用。

You are using safe update mode …

MySQL Workbench: You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column To disable safe mode, toggle the option ….

1
SET SQL_SAFE_UPDATES = 0;

MySQL 导入 SQL 文件

1
2
mysql> using databasename;
mysql> source /path/to/backup.sql

MySQL 导出数据库的所有表结构 (不包含数据)

1
mysqldump -d -h localhost -u root -pmypassword databasename > dumpfile.sql

MySQL Dump

  • dump 整个表:
1
mysqldump -h [remoteip] --port = 3306 -u [username] -p [password] --databases [db_name] --tables [tablename] > tablename.sql;

更多示例:

1
2
3
mysqldump --all-databases > dump.sql
mysqldump -uroot --databases db1, db2 > dump.sql
mysqldump -uroot my_db t1
  • query dump 部分结果,使用 MySQL Workbench:

在保存的格式中选择 SQL INSERT statements:

  • SELECT … INTO OUTFILE 备份:
1
2
3
4
mysql> select * INTO OUTFILE 'aaa.sql';
ERROR 1096 (HY000): No tables used
mysql> select * INTO OUTFILE 'aaa.sql' FROM iapp_app_info;
ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement

最后发现需要这样执行才算可以:

1
mysql -uroot -proot iapp_db -e 'select * FROM iapp_app_info' > aaa.txt

默认导出的数据文件是以 TAB 进行列分割的,如果想要使用其他分隔符,可以像下面这样:

1
2
3
4
5
6
7
8
9
10
```

---

关于 `mysqldump` 的一些参数信息:

- **`--add-drop-database`**: 多一行这个:

```sql
/*!40000 DROP DATABASE IF EXISTS `iapp_db`*/;

SQL 优化工具

1
2
sudo apt install cmake
sudo apt install libaio-dev

查看当前连接信息

1
mysql> status

服务器性能剖析

  • 如何确认服务器达到了最佳状态
  • 找出某条语句为什么执行不够快
  • 诊断用户描述成 “停顿”、“堆积”、“卡死” 的某些间歇性疑难故障

性能剖析 是测量和分析时间花费在哪里的主要方法。一般包含两个步骤: 测量任务所花费的时间;然后对结果进行统计和排序,将重要的任务排到前面。

第一,一些只占总响应时间比重很小的查询是不值得优化的。根据阿姆达尔定律,对一个占总响应时间不超过 5% 的查询进行优化,无论如何努力,收益也不会超过 5%。第二,如果花费了 1000 美元去优化一个任务,但业务的收入没有任何增加,那么可以说反而导致业务被逆优化了 1000 美元。如果优化的成本大于收益,就应当停止优化


强烈建议大家从现在起就利用慢查询日志捕获服务器上的所有查询,并且进行分析。可以在一些典型的时间窗口如业务高峰期的一个小时内记录查询。如果业务趋势比较均衡,那么一分钟甚至更短的时间内捕获需要优化的低效查询也是可行的。

不要直接打开整个慢查询日志进行分析,这样做只会浪费时间和金钱。


我们建议诊断问题时先使用前两种方法: SHOW STATUSSHOW PROCESSLIST。这两种方法的开销很低,而且可以通过简单的 Shell 脚本或者反复执行的查询来交互式地收集数据。

索引

(1) B-Tree 索引

索引列是按照顺序组织 (因此你可以使用 ORDER BYGROUP BY) 存储的,所以很适合查找范围数据。索引中存储了实际的列值,所以某些查询只使用索引就能够完成全部查询。

(2) Hash Index 哈希索引

对每一行的所有的索引列计算一个哈希码

(3) R-Tree 空间数据索引

多维度索引数据,一般用作地理数据存储

(4) 全文索引

更类似于搜索引擎干的事情

索引选择

(1) 前缀索引

索引很长的字符列,这会让索引变得大且慢,通常可以索引开始的部分字符,这样可以大大节约索引空间,从而提高索引效率。

计算合适的前缀长度的另外一个办法就是计算完整列的选择性,并使前缀的选择性接近于完整列的索引:

如何创建前缀索引:

MySQL 无法使用前缀索引做 ORDER BYGROUP BY

(2) 多列索引

(3) 选择合适的索引列顺序

数据库设计

通常情况下最好指定列为 NOT NULL,除非真的需要存储 NULL

DATETIMETIMESTAMP 列都可以存储相同类型的数据: 时间和日期,精确到秒,然而 TIMESTAMP 只使用 DATETIME 一半的存储空间,并且会根据时区变化,具有特殊的自动更新能力。

如果存储整数,可以使用这几种整数类型: TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT,分别使用 8, 16, 24, 32, 64 位存储空间。MySQL 可以为整数类型指定宽度,例如 INT(11),对大多数应用这是没有意义的: 它不会限制值的合法范围,只是规定了 MySQL 的一些交互工具用来显示字符的个数。对于存储和计算来说,INT(1)INT(20) 是相同的。


CHAR 适合存储很短的字符串,或者所有值都接近同一个长度。例如,CHAR 非常适合存储密码的 MD5 值,因为这是一个定长的值。对于经常变更的数据,CHAR 也比 VARCHAR 更好,因为定长的 CHAR 类型不容易产生碎片。对于非常短的列,CHARVARCHAR 在存储空间上也更有效率。例如用 CHAR(1) 来存储只有 YN 的值。


为标识符 (identifier column) 选择合适的数据类型非常重要。如果可能,应该避免使用字符串类型作为标识符,因为它们很消耗空间,并且通常比数字类型慢。对于完全“随机”的字符串也需要多加注意,例如 MD5()SHA1()UUID() 产生的字符串。这些函数生成的新值会任意分布在很大的空间内,这回导致 INSERT 以及一些 SELECT 语句变得很慢:

  • 因为插入值会随机地写到索引的不同位置,所以使得 INSERT 语句更慢。
  • SELECT 语句会变得更慢,因为逻辑上相邻的行会分布在磁盘和内存的不同地方。
  • 随机值导致缓存对所有类型的查询语句效果都很差,因为会使得缓存赖以工作的访问局部性原理失效。如果整个数据集都一样的“热”,那么缓存任何一部分g特定数据到内存都没有好处;如果工作集比内存大,缓存将会有更多刷新和不命中。

如果存储 UUID 值,则应该移除“-”符号;或者更好的做法是,用 UNHEX() 函数转换 UUID 值为 16 字节的数字,并且存储在一个 BINARY(16) 的列中。检索时可以通过 HEX() 函数来格式化为十六进制格式。

UUID() 生成的值与加密散列函数例如 SHA1() 生成的值有不同的特征: UUID 值虽然分布也不均匀,但还是有一定顺序的。尽管如此,但还是不如递增的整数好用。


1
2
create table users(id_bin binary(16), name varchar(200));
insert into users values(unhex(replace(uuid(),'-','')), 'Andromeda');

人们经常使用 VARCHAR(15) 列来存储 IP 地址,然而,它们实际上是 32 位无符号整数,不是字符串。用小数点将地址分成四段的表示方法只是为了让人们阅读容易。所以应该用无符号整数存储 IP 地址。MySQL 提供了 INET_ATON()INET_NTOA() 函数在这两种表示方法之间转换。

性能监控

SHOWSET 是仅有的两个可以用于监控 MySQL 服务器的工具

备份

1) 冷备份:

对于 InnoDB 的存储引擎的冷备份非常简单,只需要备份:

  • frm 文件
  • 共享表空间文件
  • 独立表空间文件 (*.ibd)
  • 重做日志文件
  • my.cnf

有两个不同的事务同时操作数据库中同一表的同一行,不会引起冲突的是

两个都是 DELETE

MySQL Driver Class Name

1
com.mysql.jdbc.Driver




AUTO_INCREMENT 一直自增,即使把数据库表清空,再次插入数据,依然是从上次清空前的 id 开始的。


插入数据库乱码问题:

1
con = DriverManager.getConnection("jdbc:mysql:///dbname", "user", "pass");

改为:

1
con = DriverManager.getConnection("jdbc:mysql:///dbname?useUnicode=true&characterEncoding=utf-8", "user", "pass");

1
2
3
4
5
6
7
8
9
10
SELECT VID, thumb
FROM video
WHERE VID IN (
SELECT VID
FROM video
WHERE title LIKE "%'.$Channel['name'].'%"
ORDER BY viewtime DESC
LIMIT 5)
ORDER BY RAND()
LIMIT 1

会抛出异常:

1
2
3
 Message:   Error during SQL execution: SELECT VID, thumb FROM video WHERE VID IN ( SELECT VID FROM video WHERE title LIKE "%funny%" ORDER BY viewtime DESC LIMIT 5) ORDER BY RAND() LIMIT 1<br />
MySQL Error: This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'<br />
MySQL Errno: 1235

可以替换为 (Instead of using IN, you can use JOIN):

1
2
3
4
5
6
7
8
9
10
11
SELECT v.VID, v.thumb
FROM video AS v
INNER JOIN
(SELECT VID
FROM video
WHERE title LIKE "%'.$Channel['name'].'%"
ORDER BY viewtime DESC
LIMIT 5) as v2
ON v.VID = v2.VID
ORDER BY RAND()
LIMIT 1

MySQL unresolved host

Two things to check (assuming your machine is called my-machine, you can change this as appropriate):

That the /etc/hostname file contains just the name of the machine.
That /etc/hosts has an entry for localhost. It should have something like:

1
2
127.0.0.1    localhost.localdomain localhost
127.0.1.1 my-machine

SQL 聚合

(1) RIGHT JOIN:

1
2
3
SELECT column_name(s)
FROM table1
RIGHT JOIN table2 ON table1.column_name = table2.column_name;
1
mysql> select webpage_channel.channel_id, count(*) from webpage right join webpage_channel on webpage.channel_id = webpage_channel.channel_id group by webpage_channel.channel_id;

(2) LIKE:

  • %: The percent sign represents zero, one, or multiple characters
  • _: The underscore represents a single character

(3) GROUP BY:

The GROUP BY statement is often used with 聚合函数 (COUNT, MAX, MIN, SUM, AVG) to group the result-set by one or more columns.

(4) HAVING:

The HAVING clause was added to SQL because the WHERE keyword could not be used with 聚合函数.

1
2
3
4
SELECT COUNT(CustomerID), Country
FROM Customers
GROUP BY Country
HAVING COUNT(CustomerID) > 5;

openSession 阻塞

DB 的用户名和密码配置错误了

INNER JOIN 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- 查询字段
select A.channel_id, total_count, IFNULL(error_apk_count, 0) as error_apk_count from

-- 表 A
(select channel_id, count(*) as total_count from webpage_apk where 1= 1 group by channel_id) as A

-- 表 A 数据全部显示
left outer join

-- 表 B
(select channel_id, count(*) as error_apk_count from webpage_apk where apk_is_error = 1 group by channel_id) as B

-- 两个表哪个字段进行连接
on A.channel_id = B.channel_id

-- 对最后结果进行排序
order by total_count desc

-- 限制输出
limit #{limitNum, jdbcType=INTEGER};

MySQL 时区问题

系统时间是准确的,但是在 MySQL 内查询的时间比系统时间慢 8 个小时:

MySQL 使用的也是系统时间:


默认插入的时间戳也是不正确的,慢了 8 个小时。


最终定位,MySQL 是装在 Docker 里面的,可能 Docker 的时区设置有问题。

Varchar 必须指明长度

varchar

MyCat 分布式数据库分片路由问题

单表数据库存储这张表:

1
2
3
4
5
6
7
8
9
10
11
mysql> select * from A;
+------+------+
| name | age |
+------+------+
| Tom | 23 |
| Tom | 24 |
| ZK | 23 |
| ZK | 24 |
| ZK | 25 |
+------+------+
5 rows in set (0.00 sec)

进行如下查询,返回一行,两列,没问题!

1
2
3
4
5
6
7
8
9
10
mysql> select A_1.ageCount, A_2.nameCount from
-> (select count(*) AS ageCount from A where age >= 24) AS A_1,
-> (select count(*) AS nameCount from A where name = 'Tom') AS A_2;

+----------+-----------+
| ageCount | nameCount |
+----------+-----------+
| 3 | 2 |
+----------+-----------+
1 row in set (0.00 sec)

但是这样的查询可能在分布式数据库 MyCat 中出现问题。

添加外键和删除外键

删除外键:

1
ALTER TABLE table_name DROP FOREIGN KEY constraint_name;

添加外键:

1
ALTER TABLE users ADD CONSTRAINT fk_grade_id FOREIGN KEY (grade_id) REFERENCES grades(id);

命令行执行

1
2
3
4
5
6
mysql -u root \
-h localhost \
-P 3306 \
-p \
-D test_database \
-e "$SQL"

MySQL 存储小数

推荐使用 decimal 类型,禁止使用 floatdouble 类型,因为存在精读损失的问题。 — 《码出高效》

参考

推荐文章