数据库初体验

初次学习数据库,记录笔记。

安装

官网经常访问不了,网上找的安装包已上传至OSS,自取。

安装教程及环境搭建的话 菜鸟教程写得很详细。

概念

MongoDB是目前最流行的noSQL数据库之一,它是专为Node开发的。

MongoDB的一条记录叫做文档,它是类似JSON风格的数据结构。

文档储存在集合之中,类似与关系型数据库的表。一个集合内的记录格式并不需要相同。

每个集合之中的每个文档必须有_id作为主键。

配置账户权限

  1. 创建超级管理员账户

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     db.createUser({
    user:'admin',
    pwd:'123456',
    roles: [{role:'root',db:'admin'}]
    })

    // 修改密码
    db.updateUser("admin", {pwd: "45678"})
    // 密码认证(mongo admin)
    db.auth("admin", "123456")
    // node使用时的url
    const url = 'mongod://admin:123456@localhost:27017/'

    数据库角色

    image-20210518201147604

  2. 在C:\Program Files\MongoDB\Server\4.0\bin文件夹下找到mongod.cfg,并修改

    image-20210518195542357

  3. 重启mongod服务

    在任务管理中找到mongodb服务或者cmd –> services.msc重启。

  4. 用超级管理员账户连接数据库

    1
    2
    mongo admin -u 用户名 -p 密码
    mongo 127.0.0.1:27017 -u admin -p 123456
  5. 给aaa数据库创建一个用户,只能访问aaa,不能访问其他数据库

1
2
3
4
5
6
7
8
 db.createUser({
user:'aaa-admin',
pwd:'123456',
roles: [{role:'dbOwner',db:'aaa'}]
})

// 登陆
mongo aaa -u aaa-admin -p 123456

数据库备份

导出

1
2
3
4
5
mongodump -h IP --port 端口 -u 用户名 -p 密码 -d 数据库 -o 文件存在路径 
// 导出所有
mongodump -h 127.0.0.1 -o /home/user/mongodb/
// 导出指定
mongodump -h 127.0.0.1 -d tank -o /home/user/mongodb/

导入

1
2
3
mongorestore -h IP --port 端口 -u 用户名 -p 密码 -d 数据库 --drop 文件存在路径
// 还原指定
mongorestore -d tank /home/user/mongodb/tank

使用

连接数据库

1
2
net start MongoDB // 开启服务
mongo // 连接

查询所有数据库

1
show dbs

创建数据库

1
use test // 有则切换数据库,无则创建并切换数据库(需要在创建一个集合后)

删除数据库

1
db.dropDatabase() // 删除数据库(在目标数据库执行命令)

查询数据库集合

1
2
show tables
show getCollectionNames()

创建集合

1
2
3
4
5
6
7
db.createCollection(name, options) // options是可选参数
db.createCollection('teachers', {
capped: true, // true则创建固定大小的集合,当当到最大值时覆盖最早的文档
autoIndexId: true, // true则自动在_id字段创建索引。默认false
size: 6142800, // 最大字节数。capped为true,则需指定
max: 1000 // 包含文档的最大数量
})

autoIndexId 版本3.2之后不支持。

插入文档时先检查size,再检查max。

删除集合

1
db.teachers.drop // 删除集合

插入文档

1
2
3
4
5
db.students.save({name: '小明', age: 18})
// 如果id存在则更新数据,不在则插入。该方法已废弃

db.students.insert({name : '小明'})
// 如果插入的数据主键已存在,则抛 org.springframework.dao.DuplicateKeyException 异常,提示主键重复,不能保存当前数据

版本3.2之后新增db.collection.insertOne()和db.collection.insertMany()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 向集合插入一个文档
db.collection.insertOne(
<document>,
{
writeConcern: <document>
}
)


// 向集合插入多个文档
db.collection.insertMany(
[ <document1>, <document2>, ... ],
{
writeConcern: <document>,
ordered: <boolean>
}
)

document: 要写入的文档。

writeConcern: 写入策略,默认为1,即要求确认写操作, 0是不要求。

ordered:指定是否按顺序写入,默认为true。

也可以定义成变量,如:

1
2
document=({name: '小王'})
db.collection.insert(document)

如此,便写入了。

更新文档

1
2
3
4
5
6
7
8
9
db.colletion.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
)

query: update的查询条件。

update:update的对象和一些更新的操作符。

upsert: 可选,如果不存在undate的记录,是否插入objNew, true为插入

multi:可选,mongodb默认是false,只更新找到的第一条记录,为true则多条记录全部更新。

writeConcern:可选,抛出异常的级别。

除此之外,还有我们上面提的save也能用于更新文档,需要使用相同的_id, 而且,更新是直接替换之前的文档。

删除文档

1
2
3
4
db.collection.remove(
<query>,
<justOne>
)

版本2.6之后,语法格式如下

1
2
3
4
5
6
7
db.collection.remove(
<query>,
{
justOne: <boolean>,
writeConcern: <document>
}
)

query: (可选)删除的文档的条件。

justOne:(可选)如果设为 true 或 1,则只删除一个文档,默认false,匹配所有

writeConcern:可选,抛出异常的级别。

补充1:如果想删除所有,可以使用db.collection.remove({})

补充2:remove()方法已过时,官方推荐deleteOne()和deleteMany()方法。

1
2
3
db.collection.deleteMany({}) // 删除所有
db.collection.deleteMany({age:25}) // 删除所有age等于25的文档
db.collection.deleteOne({age:25}) // 删除一个age等于25的文档

补充3: remove()方法不会真正释放空间,需要继续指向db.repairDatabase()或者db.runCommand({ repairDatabase: 1 })来回收磁盘空间,推荐使用补充2

查询文档

1
db.collection.find(query, projection)

query :可选,使用查询操作符指定查询条件

projection:可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)

除了find()方法之外,还有一个findOne()方法,它只返回一个文档。

相关操作符

1
2
3
4
5
6
db.collection.find({age: {$lt: 25}}) // 匹配age小于25的值
// $lte 小于等于
// $gt 大于
// $gte 大于等于
// $ne 不等于
db.collection.find({age: {$gte: 20, $lt: 50}}) // 匹配age大于等于20,小于50
AND条件

MongoDB 的 find() 方法可以传入多个键(key),每个键(key)以逗号隔开,即常规 SQL 的 AND 条件。

1
db.collection.find({key1: value1, key2: value2}).pretty()
OR条件

MongoDB OR 条件语句使用了关键字 $or,语法格式如下:

1
2
3
4
5
6
db.collection.find({
$or: [
{key1: value1},
{key2: value2}
]
}).pretty()
AND和OR联合使用
1
2
3
4
5
6
7
db.collection.find({
key1:value1,
$or:[
key2: value2,
key3: value3
]
})

补充1:projection参数的使用方法

1
db.collection.find(query, {key1:1, key2:1})
  • inclusion模式 指定返回的键,不返回其他键(key都为1)

  • exclusion模式 指定不返回的键,返回其他键(key都为0)

tips:不能同时指定0和1,否则不能推断其他键值是否返回(_id除外)

补充2: 模糊查询

支持正则:比如

1
db.collection.find({name: /小/}); // /^小/ /小$/ 

$type操作符

$type操作符是基于BSON类型来检索集合中匹配的数据类型,并返回结果。

MongoDB 中可以使用的类型: $type操作符

作用:可以通过字段类型筛选

Limit与Skip方法

1
2
3
db.collection.find().limit(2) // 限制读取的记录条数,按顺序输出,即输出第一二条

db.collection.find().skip(2) // 跳过指定数量的数据,即输出从第三条到最后一条

排序

1
2
db.collection.find().sort(1) // 1为升序,-1为降序
// 同时使用skip(), limilt(), sort()时,先执行sort,然后skip,最后limit

索引

索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
db.users.find({username: '123'}).explain('executionStats') // 查看花费时间

db.collection.createIndex(keys, options)

db.students.createIndex({age: 1},{background:true})

db.students.createIndex({age:1,name:-1}, {background}) // 复合索引

db.collection.getIndexes() // 查看集合索引

db.collection.totalIndexSize() // 查看集合索引大小

db.collection.dropIndexes() // 删除所有索引

db.collection.dropIndex() // 索引名称

常用可选options

  • background:background可指定以后台方式创建索引
  • unique: 建立的索引是否唯一
  • name:索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。
  • sparse:对文档不存在的字段数据不启用索引,默认false

复合索引要考虑顺序问题

1
2
3
4
db.users.createIndex({username: 1, age:1})
db.users.find({username: '123', age: 17}) //会命中
db.users.find({username: '123'}) //会命中
db.users.find({age: 17}) //不命中

聚合

MongoDB 中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果

1
db.collection.aggregate(options)

代表在students集合通过name计算每个出现过的次数。

集合表达式:

表达式描述实例
sum计算总和。db.mycol.aggregate([{$group : {_id : “$by_user”, num_tutorial : {$sum : “$likes”}}}])
$avg计算平均值db.mycol.aggregate([{$group : {_id : “$by_user”, num_tutorial : {$avg : “$likes”}}}])
$min获取集合中所有文档对应值得最小值。db.mycol.aggregate([{$group : {_id : “$by_user”, num_tutorial : {$min : “$likes”}}}])
$max获取集合中所有文档对应值得最大值。db.mycol.aggregate([{$group : {_id : “$by_user”, num_tutorial : {$max : “$likes”}}}])
$push在结果文档中插入值到一个数组中。db.mycol.aggregate([{$group : {_id : “$by_user”, url : {$push: “$url”}}}])
$addToSet在结果文档中插入值到一个数组中,但不创建副本。db.mycol.aggregate([{$group : {_id : “$by_user”, url : {$addToSet : “$url”}}}])
$first根据资源文档的排序获取第一个文档数据。db.mycol.aggregate([{$group : {_id : “$by_user”, first_url : {$first : “$url”}}}])
$last根据资源文档的排序获取最后一个文档数据db.mycol.aggregate([{$group : {_id : “$by_user”, last_url : {$last : “$url”}}}])

管道

MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。

表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。

以下是我们建立的数据库user下的集合books下的文档:

常用管道府:

  • $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。

    1
    db.books.aggregate({$project: {_id:0,name:1}}) //此时输出所有只带name字段的文档
  • $match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    db.books.aggregate([
    {
    $match: {
    $or: [
    {
    name: '格林童话'
    },
    {
    cost: {
    $gte: 120
    }
    }
    ]
    }
    },
    {
    $groud: {
    _id: null,
    count: {
    $sum: 1
    }
    }
    }
    ])
    // {"_id": null, "count": 3}
  • $limit:用来限制MongoDB聚合管道返回的文档数。

  • $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。

  • $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。

    1
    2
    3
    db.books.aggregate({
    $unwind: "$items" // 数组字段
    })
  • $group:将集合中的文档分组,可用于统计结果。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    db.books.aggregate([
    {
    $group: {
    _id: null, // 例2:'$name'
    count: {
    $sum: 1 // 例3: '$cost'
    }
    }
    }
    ])

    // {"_id": null, count: 3}
    // 例2 {"_id": "一千零一夜", count: 1}{"_id": "格林童话", count: 2} 求出现次数
    // 例3 {"_id": null, count: 340} 求总和
    // 总结: 以_id为条件,相同条件的求和
  • $sort:将输入文档排序后输出。

  • $geoNear:输出接近某一地理位置的有序文档。

补充

时间关键字如下:

  • $dayOfYear: 返回该日期是这一年的第几天(全年 366 天)。
  • $dayOfMonth: 返回该日期是这一个月的第几天(1到31)。
  • $dayOfWeek: 返回的是这个周的星期几(1:星期日,7:星期六)。
  • $year: 返回该日期的年份部分。
  • $month: 返回该日期的月份部分( 1 到 12)。
  • $week: 返回该日期是所在年的第几个星期( 0 到 53)。
  • $hour: 返回该日期的小时部分。
  • $minute: 返回该日期的分钟部分。
  • $second: 返回该日期的秒部分(以0到59之间的数字形式返回日期的第二部分,但可以是60来计算闰秒)。
  • $millisecond:返回该日期的毫秒部分( 0 到 999)。
  • $dateToString: { $dateToString: { format: , date: } }。

举例

1
2
3
4
5
6
7
8
9
10
11
12
db.books.aggregate([
{
$project: {
Day: {
"dateToString": {
format: "%Y-%m-%d",
date: "$publish_time"
}
}
}
}
])