饮冰十年-人工智能-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 中,使用 FOR 和 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
    }

 下一篇:饮冰十年-人工智能-ArangoDB-03-AQL

posted @ 2024-01-04 20:46  逍遥小天狼  阅读(58)  评论(0编辑  收藏  举报