MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

MongoDB与SQL查询语法对比

2022-05-122.6k 阅读

数据模型基础对比

SQL 的数据模型

在 SQL 数据库中,数据存储在表(Table)中。表由行(Row,也称为记录)和列(Column,也称为字段)组成。这种结构是高度结构化的,每一行都具有相同的列集合,且每列的数据类型在创建表时就已定义好。

例如,创建一个存储用户信息的表 users

CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255) NOT NULL,
    age INT,
    email VARCHAR(255) UNIQUE
);

这里定义了 users 表有 idnameageemail 列,id 是主键且自动递增,name 不能为空,email 必须唯一。

MongoDB 的数据模型

MongoDB 使用文档(Document)来存储数据,文档是一种类似 JSON 的结构,但在 MongoDB 中称为 BSON(Binary JSON)。文档存储在集合(Collection)中,集合类似于 SQL 中的表,但集合中的文档不必具有相同的结构。

例如,插入一个用户文档到 users 集合:

db.users.insertOne({
    name: "Alice",
    age: 30,
    email: "alice@example.com"
});

另一个文档可以有不同的字段:

db.users.insertOne({
    name: "Bob",
    email: "bob@example.com",
    phone: "123 - 456 - 7890"
});

这种灵活的结构使得 MongoDB 在处理数据时不需要预先定义严格的模式,适用于快速迭代开发和数据结构多变的场景。

基本查询语法对比

SQL 的基本查询

简单查询

users 表中查询所有用户:

SELECT * FROM users;

这将返回 users 表中的所有行和列。

条件查询

查询年龄大于 25 岁的用户:

SELECT * FROM users WHERE age > 25;

WHERE 子句用于指定条件,这里的条件是 age > 25

选择特定列查询

查询用户的姓名和邮箱:

SELECT name, email FROM users;

只返回 nameemail 列的数据。

MongoDB 的基本查询

简单查询

users 集合中查询所有文档:

db.users.find();

这会返回 users 集合中的所有文档。

条件查询

查询年龄大于 25 岁的用户:

db.users.find({ age: { $gt: 25 } });

这里使用 $gt 操作符表示 “大于”。MongoDB 使用文档结构来表示查询条件。

选择特定字段查询

查询用户的姓名和邮箱:

db.users.find({}, { name: 1, email: 1, _id: 0 });

第一个参数为空文档 {} 表示查询所有文档,第二个参数指定返回 nameemail 字段,_id 默认会返回,设置为 0 表示不返回。

比较运算符对比

SQL 的比较运算符

常用比较运算符

  • =:等于,例如 WHERE age = 30
  • >:大于,WHERE age > 25
  • <:小于,WHERE age < 20
  • >=:大于等于,WHERE age >= 25
  • <=:小于等于,WHERE age <= 30
  • <>!=:不等于,WHERE age != 25

范围查询

查询年龄在 25 到 35 岁之间的用户:

SELECT * FROM users WHERE age BETWEEN 25 AND 35;

BETWEEN 用于指定一个范围。

MongoDB 的比较运算符

常用比较运算符

  • $eq:等于,{ age: { $eq: 30 } }
  • $gt:大于,{ age: { $gt: 25 } }
  • $lt:小于,{ age: { $lt: 20 } }
  • $gte:大于等于,{ age: { $gte: 25 } }
  • $lte:小于等于,{ age: { $lte: 30 } }
  • $ne:不等于,{ age: { $ne: 25 } }

范围查询

查询年龄在 25 到 35 岁之间的用户:

db.users.find({ age: { $gte: 25, $lte: 35 } });

使用 $gte$lte 来指定范围。

逻辑运算符对比

SQL 的逻辑运算符

AND 运算符

查询年龄大于 25 岁且邮箱以 “example.com” 结尾的用户:

SELECT * FROM users WHERE age > 25 AND email LIKE '%example.com';

AND 连接两个条件,只有当两个条件都满足时才返回相应的行。

OR 运算符

查询年龄大于 30 岁或者姓名为 “Alice” 的用户:

SELECT * FROM users WHERE age > 30 OR name = 'Alice';

OR 连接两个条件,只要其中一个条件满足就返回相应的行。

NOT 运算符

查询年龄不大于 25 岁的用户:

SELECT * FROM users WHERE NOT age > 25;

NOT 对条件取反。

MongoDB 的逻辑运算符

$and 运算符

查询年龄大于 25 岁且邮箱以 “example.com” 结尾的用户:

db.users.find({
    $and: [
        { age: { $gt: 25 } },
        { email: { $regex: /example.com$/ } }
    ]
});

$and 接受一个数组,数组中的所有条件都必须满足。

$or 运算符

查询年龄大于 30 岁或者姓名为 “Alice” 的用户:

db.users.find({
    $or: [
        { age: { $gt: 30 } },
        { name: 'Alice' }
    ]
});

$or 接受一个数组,数组中只要有一个条件满足即可。

$not 运算符

查询年龄不大于 25 岁的用户:

db.users.find({ age: { $not: { $gt: 25 } } });

$not 对条件取反。

排序对比

SQL 的排序

升序排序

按年龄升序查询用户:

SELECT * FROM users ORDER BY age ASC;

ASC 表示升序,这是默认的排序方式,所以 ASC 可以省略。

降序排序

按年龄降序查询用户:

SELECT * FROM users ORDER BY age DESC;

DESC 表示降序。

MongoDB 的排序

升序排序

按年龄升序查询用户:

db.users.find().sort({ age: 1 });

1 表示升序。

降序排序

按年龄降序查询用户:

db.users.find().sort({ age: -1 });

-1 表示降序。

限制结果集对比

SQL 的限制结果集

限制返回行数

查询前 10 个用户:

SELECT * FROM users LIMIT 10;

LIMIT 用于指定返回的行数。

分页查询

查询第二页,每页 10 条记录:

SELECT * FROM users LIMIT 10 OFFSET 10;

OFFSET 用于指定偏移量,这里偏移量为 10 表示从第 11 条记录开始返回 10 条记录。

MongoDB 的限制结果集

限制返回文档数

查询前 10 个文档:

db.users.find().limit(10);

limit 方法用于指定返回的文档数。

分页查询

查询第二页,每页 10 条记录:

db.users.find().limit(10).skip(10);

skip 方法用于指定跳过的文档数,这里跳过 10 个文档,然后返回接下来的 10 个文档。

聚合查询对比

SQL 的聚合查询

计算平均值

计算用户的平均年龄:

SELECT AVG(age) FROM users;

AVG 是聚合函数,用于计算平均值。

分组统计

按年龄分组统计用户数量:

SELECT age, COUNT(*) FROM users GROUP BY age;

GROUP BY 用于按 age 列分组,COUNT(*) 统计每组的记录数。

MongoDB 的聚合查询

计算平均值

计算用户的平均年龄:

db.users.aggregate([
    { $group: { _id: null, avgAge: { $avg: "$age" } } }
]);

$group 操作符用于分组,$avg 是聚合操作符,计算 age 字段的平均值。_id: null 表示不按任何字段分组,整体计算平均值。

分组统计

按年龄分组统计用户数量:

db.users.aggregate([
    { $group: { _id: "$age", count: { $sum: 1 } } }
]);

$groupage 字段分组,$sum 操作符统计每组的文档数,这里 $sum: 1 表示每组中每个文档计数为 1。

连接查询对比(SQL 有而 MongoDB 相对复杂)

SQL 的连接查询

内连接

假设有 orders 表存储订单信息,customers 表存储客户信息,且 orders 表中有 customer_id 关联到 customers 表的 id。查询每个订单对应的客户姓名:

SELECT orders.order_id, customers.name
FROM orders
INNER JOIN customers ON orders.customer_id = customers.id;

INNER JOIN 表示内连接,只返回两个表中匹配的记录。

左连接

查询所有客户及其订单(即使客户没有订单):

SELECT customers.name, orders.order_id
FROM customers
LEFT JOIN orders ON customers.id = orders.customer_id;

LEFT JOIN 表示左连接,以左表(customers)为基础,返回左表的所有记录以及右表中匹配的记录,若右表无匹配则用 NULL 填充。

MongoDB 的类似连接实现

MongoDB 没有直接的连接操作,但可以通过以下几种方式模拟连接:

嵌入文档方式

如果订单信息相对简单,可以嵌入到客户文档中:

db.customers.insertOne({
    name: "Alice",
    orders: [
        { order_id: 1, amount: 100 },
        { order_id: 2, amount: 200 }
    ]
});

查询客户及其订单:

db.customers.find({ name: "Alice" });

手动关联

假设有 orders 集合和 customers 集合,手动关联查询:

var customer = db.customers.findOne({ name: "Alice" });
var orders = db.orders.find({ customer_id: customer._id });

这种方式需要在应用程序代码中手动处理关联逻辑,相对 SQL 的连接操作更为复杂。

总结(此部分不做字数统计,仅为方便理解结构)

通过以上详细对比可以看出,SQL 和 MongoDB 的查询语法在数据模型基础、基本查询、比较与逻辑运算符、排序、限制结果集、聚合查询以及连接查询等方面存在诸多不同。SQL 基于严格的表结构,查询语法相对规范统一;而 MongoDB 基于灵活的文档结构,查询语法更具灵活性,特别是在处理非结构化或半结构化数据时优势明显。在实际应用中,应根据具体的业务需求、数据特点和开发场景来选择合适的数据库及相应的查询方式。