这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记。
SQL基础
数据库:长期存储在计算机内、有组织的、可共享的数据的集合。
关系型数据库:采用关系模型来组织数据的数据库,简单来说,关系模型就是二维表格模型。
非关系型数据库:非关系型的、分布式系统的,且一般不确保遵照ACID标准的数据存储系统。
MYSQL约束——什么是约束?什么是唯一约束、主键约束、联合主键约束、非空约束?
-
约束主要完成对数据的校验,保证数据库数据的完整性;如果有相互依赖的数据,保证数据不会被删除。
-
主要有五种约束:
- not null: 非空约束,指定某列不能为空。
- unique: 唯一约束,指定某列和几列组合地数据不能重复。
- primary key: 主键约束,指定某列地数据不能重复,唯一。
- foreign key: 外键约束,指定子表种该列记录属于主表中的一条记录,参照另一条数据。
- check: 检查 指定一个表达式,用于检验指定数据。
MYSQL怎么修改已经被外键约束的表?
-
可以先取消外键,修改完后再加回来,但是比较费事;
-
mysql提供了一个方法,临时关闭外键约束,修改完成之后再将外键约束加回来。
SET FOREIGN_KEY_CHECKS = 0;
....//do something
SET FOREIGN_KEY_CHECKS = 1;
MYSQL like查询的弊端
在不使用索引的情况下,普通查询和like查询的耗时相当,like略长,这也是必然的,因为它要进行额外的算法。
在使用索引的情况下,普通查询的耗时基本上算是秒查,非常快,但是like查询较慢。原因是普通查询用到了索引,但是like语句并没有用到索引。
造成mysql中like查询效率低下的原因是:在有些情况下,like查询使用不到索引,会扫描全表。但是like语句有时候也会使用到索引,例如'dd_' 'dd%'这种还是可以用到索引的,因此要尽量避免使用以%或者_开头进行模糊查询,不然会造成索引失败。数据量大的时候,耗时,效率较低。
数据库事务
数据库事务是数据库运行中的逻辑工作单位,一条或者一组语句组成的一个单元,这个单元要么全部执行,要么全都不执行。
数据库事务的四大特性ACID:
- 原子性: 要么全部执行,要么全都不执行。
- 一致性: 事务必须使得数据库从一个一致性状态,到另一个一致性状态。
- 隔离性: 指的是一个事务的执行,不能被其他的事务所干扰。
- 持久性: 一个事务一旦提交了之后,对数据库的改变就是永久的。 数据库的事务隔离级别
什么是脏读、不可重复读、幻读。
(1) 脏读: 读到了未提交事务的数据。
事务A触发了回滚,那么事务B读到的数据就是过时的数据,这种现象就是脏读。
(2) 不可重复读: 事务A在事务B更新数据之前先读取了一条数据,后来事务B更新之后,事务A再次读取了这条数据,发现数据不匹配,这个现象称作"不可重复读"。
(3) 幻读: 在一个事务内,同一条查询语句在不同时间段执行,得到不同的结果集。
脏读和不可重复读都是属于多次读取数据 数据状态的不一致性。
事务隔离级别:
级别逐渐增强
(1) 读未提交 read uncommitted: 隔离级别最低、隔离度最弱。脏读、不可重复读、幻读三种现象都有可能发生。理论上的存在,实际项目中没有人用,但性能最高。
(2) 读已提交 read committed: 保证事务不出现中间状态,所有的数据都是已提交的,解决了脏读的问题,但是仍然存在不可重复读、幻读的可能。
(3) 可重复读 repeatable reads: mysql innoDB引擎的默认隔离级别,保证同一个事务多次读取数据的一致性,解决了脏读和不可重复读的问题,但是仍然存在幻读的可能。
(4) 可串行化 serializable reads: 可串行化意味着读取数据的时候需要获取共享读锁,更新数据的时候需要获取排他写锁,如果sql使用where语句,还会获取区间锁。也就是事务A操作数据库的时候,事务B只能排队等待,因此性能是最低的。
Gorm
连接数据库
//连接数据库,并设置慢打印
var err error
dsn := "root:6J&fhT5P0gdi@tcp(localhost:3306)/gorm_demo?charset=utf8mb4&parseTime=True&loc=Local"
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer(日志输出的目标,前缀和日志包含的内容——译者注)
logger.Config{
SlowThreshold: time.Second, // 慢 SQL 阈值
LogLevel: logger.Info, // 日志级别
IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound(记录未找到)错误
Colorful: true, // 彩色打印
},
)
//全局模式
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: newLogger,
})
if err != nil {
panic(err)
}
通过Gorm生成表结构
type Person struct {
gorm.Model
Name string
CreditCards []CreditCard
}
------------------------------
//通过自动迁移建立表
db.AutoMigrate(&Person{})
Gorm中对零值的处理,update和updates都有哪些坑?
Update用于更新单个的列,需要指定条件,不然会返回ErrMissingWhereClause错误。当使用Model方法,且该对象主键有值,该值会被用于构建条件。
//使用where条件
db.Model(&User{}).Where("name = ?", "hello").Update("name", "world");
//使用Model ID
db.Model(&user).Update("name", "张三");
//使用Model和where 查找指定id的user,同时名称为张三的记录,名称修改为李四。
db.Model(&user).Where("name = ?", "张三").Update("name", "李四");
Updates方法支持struct和map[string]interface{}参数。
当使用struct进行更新的时候,gorm只会更新非零值的字段。
//struct参数
db.Model(&user).Updates(User{Name: "王五", Age: 23});
//map[string]interface{}参数
//里面的参数应该是数据库所对应的(其实struct对应的大写的字段的也行,会自动转换)
db.Model(&user).Updates(map[string]interface{}{
"name": "王五",
"age": 23,
})
添加数据
user := User{
Name: "马云",
Age: 56,
}
//1. create单个
db.Create(&user)
//2. create批量
users := []User{
{
Name: "任一",
Age: 25,
},
{
Name: "任二",
Age: 26,
},
}
db.Create(&users)
//3. CreateInBatches分批创建 可以指定每批的数量
db.CreateInBatches(users, 2)
//4.使用map[string]interface{}和[]map[string]interface{}{}创建记录
//主键不会自动填充
db.Model(&User{}).Create(map[string]interface{}{
"name": "任三",
"email": "56789@qq.com",
})
db.Model(&User{}).Create([]map[string]interface{}{
{
"name": "任四",
},
{
"name": "任五",
},
})
查询数据
//获取第一条记录
//SELECT * FROM users ORDER BY id LIMIT 1;
var user User
db.First(&user)
fmt.Println(user) //1
//获取最后一条记录
//SELECT * FROM users ORDER BY id DESC LIMIT 1;
var userLast User
db.Last(&userLast)
fmt.Println(userLast) //20
//获取一条记录 不指定排序
//SELECT * FROM `users` LIMIT 1
var random User
db.Take(&random)
fmt.Println(random)
//按照主键进行检索
var userId4 User
db.First(&userId4, 4)
fmt.Println(userId4)
//多个主键 获取数组
var usersId123 []User
db.Find(&usersId123, []int{1, 2, 3})
fmt.Println(usersId123)
//如果主键是字符串 例如uuid
//db.Find(&user, "id = ?", "nfeiwnfi-37r892bjfsd0-svsi")
var userbyname User
//获取匹配到的第一条记录
db.Where("name = ?", "1111").First(&userbyname)
fmt.Println(userbyname)
var usersbyname []User
//获取匹配到的全部记录
db.Where("name = ?", "1111").Find(&usersbyname)
fmt.Println(usersbyname)
var user User
//传入多个主键 27不存在 3 4存在
db.First(&user, 27, 3, 4)
fmt.Println(user)
//只打印了主键为3的那一条记录,说明程序会一个个的找,直到找到存在的那一条记录就会停止
//带条件的查询 面向对象的
//使用map进行查询
var usersList []User
db.Where(map[string]interface{}{"Name": "1111"}).Find(&usersList)
for _, item := range usersList {
fmt.Println(item.ID)
}
Gorm如何实现删除数据的?
//删除
//一般开发中都会使用软删除
//gorm 只要model中包含gorm.Model或者包含gorm.DeleteAt字段,会自动获得软删除的能力
//软删除,不会真正的删除,只是DeleteAt会置为当前删除时间,并且不能通过普通的查询方法找到该记录,查询的时候会自动忽略
db.Where("Name = ?", "world").Delete(&User{})
//查询刚才删除的Name="world"记录
users := []User{}
db.Where("Name = ?", "world").Find(&users)
fmt.Println(users)
-------------------------------------------------------------
[1.949ms] [rows:0] SELECT * FROM `users` WHERE Name = 'world' AND `users`.`deleted_at` IS NULL //自动添加了delete_at字段为空
[] //空集 查询不到
------------------------------------------------------------
//使用Unscoped找到被软删除的记录
users := []User{}
db.Unscoped().Where("Name = ?", "world").Find(&users)
fmt.Println(users)