饮冰十年-人工智能-ArangoDB-02-AQL vs SQL
上一篇: 饮冰十年-人工智能-ArangoDB-01-初识ArangoDB
一、SQL 和 AQL
如果您使用过 MySQL、MariaDB 或 PostgreSQL 等关系数据库管理系统 (RDBMS),那么您熟悉它们的查询语言,即 SQL(结构化查询语言)。
ArangoDB 的查询语言称为 AQL(ArangoDB’ query language)。它的用途类似于 SQL。
两者都支持读取和修改数据库系统中的记录。但是,AQL 不支持数据定义操作,例如创建和删除数据库、集合和索引。
尽管数据库系统的数据模型不同,但两种语言之间还是有一些相似之处。它们是声明性语言。查询表示您想要的结果,而不是您希望如何获得结果。它们的目标是人类可读,使用英语中的关键字和读起来像句子的语法。它们还尝试独立于客户端,这意味着无论客户端使用何种编程语言或环境,所有客户端的语言和语法都是相同的。
最显着的区别可能是 AQL 中循环的概念,这使得它感觉更像是一种编程语言。AQL 一开始比普通 SQL 复杂一点,但从长远来看,它提供了更大的灵活性。它更自然地适合无架构模型,并使查询语言非常强大,同时保持易于读取和编写。
例如,SQL 的子句和 AQL 的操作是等效的,因为它们都定义了返回结果的条件。但 SQL 对查询子句使用固定顺序,确定子句在语句中必须出现的位置。在 AQL 中,操作可以以不同的顺序进行,甚至可以多次发生,从而为您提供很大的自由度。
AQL 支持复杂的查询模式,包括子查询、联接、聚合、地理空间查询、全文搜索和排名,以及图形遍历,其中一些在标准 SQL 中是有限的,甚至是不可能的。
尽管存在任何差异,但任何具有 SQL 背景的人在学习 AQL 时都应该没有困难。
二、基本查询
1、INSERT
它使用以下语法:
INSERT document INTO collection
1.1 插入单行/文档
SQL格式:
INSERT INTO users (name, gender) VALUES ("John Doe", "m");
AQL:
INSERT { name: "John Doe", gender: "m" } INTO users
1.2 插入多行/文档
SQL格式:
INSERT INTO users (name, gender) VALUES ("John Doe", "m"),("Jane Smith", "f");
AQL:
FOR user IN [{ name: "John Doe", gender: "m" },{ name: "Jane Smith", gender: "f" }] INSERT user INTO users
1.3 从表/集合中插入行/文档
SQL格式:
INSERT INTO backup (uid, name, gender) SELECT uid, name, gender FROM users WHERE active = 1;
AQL:
FOR user IN users FILTER user.active == 1 INSERT user INTO backup
1.4 生成测试行/文档
SQL格式:
使用脚本或存储过程,或者从现有表进行填充。
AQL:
FOR i IN 1..1000 INSERT { name: CONCAT("test", i), gender: (i % 2 == 0 ? "f" : "m") } INTO users
2、UPDATE 查询
AQL 中的 UPDATE
关键字可部分修改集合中的文档。此操作有两种语法可用:
UPDATE document IN collection
UPDATE keyExpression WITH document IN collection
2.1 更新单行/文档
SQL格式:
UPDATE users SET name = "John Smith" WHERE id = 1;
AQL:
UPDATE { _key: "1" } WITH { name: "John Smith" } IN users
2.2 添加具有默认值的新列/属性
SQL格式:
ALTER TABLE users ADD COLUMN numberOfLogins INTEGER NOT NULL default 0;
AQL:
FOR user IN users UPDATE user WITH { numberOfLogins: 0 } IN users
2.3 添加具有计算值的新列/属性
SQL格式:
ALTER TABLE users ADD COLUMN numberOfLogins INTEGER NOT NULL default 0; UPDATE users
SET numberOfLogins = (SELECT COUNT(*) FROM logins WHERE user = users.id) WHERE active = 1;
AQL:
FOR user IN users FILTER user.active == 1 UPDATE user WITH { numberOfLogins: LENGTH( FOR login IN logins FILTER login.user == user._key COLLECT WITH COUNT INTO numLogins RETURN numLogins ) } IN users
2.4 添加可选列/属性
SQL格式:不可直接实现,必须将不符合条件的行的列设置为默认值(例如 NULL)。
ALTER TABLE users ADD COLUMN isImportantUser INTEGER default NULL, ADD COLUMN dateBecameImportant INTEGER default NULL; UPDATE users SET isImportantUser = 1, dateBecameImportant = UNIX_TIMESTAMP() WHERE isImportantUser IS NULL
AND ( SELECT COUNT(*) FROM logins WHERE user = user.id ) > 50;
AQL:
LET date = DATE_NOW() FOR user IN users FILTER user.isImportantUser == null LET numberOfLogins = ( FOR login IN logins FILTER login.user == user._key COLLECT WITH COUNT INTO numLogins RETURN numLogins ) FILTER numberOfLogins > 50 UPDATE user WITH { isImportantUser: 1, dateBecameImportant: date } IN users
2.5 删除列/属性
SQL格式:
ALTER TABLE users DROP COLUMN numberOfLogins;
AQL:
FOR user IN users UPDATE user WITH { numberOfLogins: null } IN users OPTIONS { keepNull: false }
2.6 仅删除某些行/文档的列/属性
SQL格式:
不可直接,必须将符合条件的行的列设置为默认值(例如 NULL)。
UPDATE users SET isImportantUser = NULL, dateBecameImportant = NULL WHERE isImportantUser = 1 AND active = 0;
AQL:
FOR user IN users FILTER user.isImportantUser == 1 AND user.active == 0 UPDATE user WITH { isImportantUser: null, dateBecameImportant: null } IN users OPTIONS { keepNull: false }
3、REPLACE 查询
REPLACE
关键字可完全修改集合中的文档。此操作有两种语法可用:
REPLACE document IN collection
REPLACE keyExpression WITH document IN collection
3.1 替换单行/文档
SQL格式:
REPLACE INTO users (name, gender) VALUES ("Jane Smith", "f") WHERE id = 1;
AQL:
REPLACE { _key: "1" } WITH {name: "Jane Smith", gender: "f" } IN users
3.2 替换表/集合中的多个行/文档
SQL格式:
REPLACE INTO users (name, gender) SELECT name, gender FROM backup;
AQL:
FOR user IN backup
REPLACE user
WITH {name: backup.name, gender: backup.gender}
IN users
4、DELETE/REMOVE 查询
SQL 使用 DELETE 语句从表中删除行。在 AQL 中,REMOVE
关键字允许您从集合中删除文档。
4.1 删除单行/文档
SQL格式:
DELETE FROM users WHERE id = 1;
AQL:
REMOVE { _key:"1" } IN users
4.2 删除多行/文档
SQL格式:
DELETE FROM users WHERE active = 1;
AQL:
FOR user IN users FILTER user.active == 1 REMOVE user IN users
5、SELECT 查询
如果要从 SQL 中的表中检索行,请使用SELECT语句查询数据库。在 AQL 中,使用 F
O
R
和 RETURN
关键字查询集合中的文档。
FOR
循环访问集合中的文档。 RETURN确定查询返回给客户端的内容。
5.1 从表/集合中选择所有行/文档,包括所有列/属性
SQL格式:
SELECT * FROM users;
AQL:
FOR user IN users RETURN user
5.2 从表/集合中筛选行/文档
SQL格式:
SELECT CONCAT(firstName, " ", lastName) AS name, gender
FROM users WHERE active = 1;
AQL:
FOR user IN users FILTER user.active == 1 RETURN { name: CONCAT(user.firstName, " ", user.lastName), gender: user.gender}
5.3 对表/集合中的行/文档进行排序
SQL格式:
SELECT * FROM users WHERE active = 1 ORDER BY name, gender;
AQL:
FOR user IN users FILTER user.active == 1 SORT user.name, user.gender RETURN user
三、高级查询
1、聚合查询
SQL 和 AQL 中都有一系列函数和子句,用于对结果集进行分组或进一步优化,以获取所需的信息。例如,计算文档、查找最小值或最大值等。
1.1 对表/集合中的行/文档进行计数
SQL 和 AQL 都可以计算结果集中的行或文档,并告诉您它找到了多少行或文档。AQL 使用 WITH
关键字管理计数,以将文档计数到返回变量中。
SQL格式:
SELECT gender, COUNT(*) AS number FROM users WHERE active = 1 GROUP BY gender;
AQL:
FOR user IN users FILTER user.active == 1 COLLECT gender = user.gender WITH COUNT INTO number RETURN { gender: gender, number: number }
1.2 对表/集合中的行/文档进行分组
在 SQL 中,GROUP BY子句根据给定的列收集结果集。AQL 将其替换为 COLLECT
关键字。
SQL格式:
SELECT YEAR(dateRegister) AS year, MONTH(dateRegister) AS month, COUNT(*) AS number FROM users WHERE active = 1 GROUP BY year, month HAVING number > 20;
AQL:
FOR user IN users FILTER user.active == 1 COLLECT year = DATE_YEAR(user.dateRegistered), month = DATE_MONTH(user.dateRegistered) WITH COUNT INTO number FILTER number > 20 RETURN { year: year, month: month, number: number }
1.3 表/集合中行/文档的最小值、最大值计算
SQL 和 AQL 都使用函数来查找给定字段的最小值和最大值。在 AQL 中,它使用 COLLECT
关键字进行处理。
SQL格式:
SELECT MIN(dateRegistered) AS minDate, MAX(dateRegistered) AS maxDate FROM users WHERE active = 1;
AQL:
FOR user IN users FILTER user.active == 1 COLLECT AGGREGATE minDate = MIN(user.dateRegistered), maxDate = MAX(user.dateRegistered) RETURN { minDate, maxDate }
1.4 生成水平列表
SQL格式:
实际上并不适用 – 使用串联的字符串列或特殊数据类型(不可移植)。
SELECT gender, GROUP_CONCAT(id) AS userIds FROM users WHERE active = 1 GROUP BY gender;
AQL:
FOR user IN users FILTER user.active == 1 COLLECT gender = user.gender INTO usersByGender RETURN { gender: gender, userIds: usersByGender[*].user._key }
2、使用联接的查询
与关系数据库中的联接类似,ArangoDB 也有自己的联接实现。来自 SQL 背景的你可能会发现 AQL 语法与你的期望大不相同。
2.1 内部联接
SQL格式:
SELECT * FROM users
INNER JOIN friends
ON (friends.user = users.id);
AQL:
FOR user IN users FOR friend IN friends FILTER friend.user == user._key RETURN MERGE(user, friend)
注意:在 AQL 中,首选方法是从各个子属性中的不同集合中返回文档部分,以避免属性名称冲突,例如:
FOR user IN users FOR friend IN friends FILTER friend.user == user._key RETURN { user: user, friend: friend }
也可以在水平列表中返回匹配的文档:
FOR user IN users RETURN { user: user, friends: ( FOR friend IN friends FILTER friend.user == user._key RETURN friend ) }
2.2 外部连接
AQL 不直接支持外部联接,但可以使用子查询实现。
SQL格式:
SELECT * FROM users
LEFT JOIN friends
ON (friends.user = users.id);
AQL:
FOR user IN users LET friends = ( FOR friend IN friends FILTER friend.user == user._key RETURN friend ) FOR friendToJoin IN ( LENGTH(friends) > 0 ? friends : [ { /* no match exists */ } ] ) RETURN { user: user, friend: friend }