【连肝10个晚上 】三年开发经验的小菜 吐血整理Couchbase开发者出来的N1QL使用方法, 看完请给我一个点赞 !

N1QL是由Couchbase开发者提供的,目前唯一一个支持在NoSQL数据库上执行SQL查询的实现。

N1QL使用方法

基本的选择语句

SELECT 'Hello World' AS Greeting

结果:

{
    
    
  "results": [
    {
    
    
      "Greeting": "Hello World"
    }
  ]
}

Couchbase的N1QL选择结果由一个JSON字典给出,字典最外层是result字段,字段值是一个JSON数组,存储了所有的结果。

基础

一个叫tutorial的文档库有如下6个json文档:

{
    
    
    "age": 46,
    "children": [
        {
    
    "age": 17,"fname": "Aiden","gender": "m"},
        {
    
    "age": 2,"fname": "Bill","gender": "f"}
    ],
    "email": "[email protected]",
    "fname": "Dave",
    "hobbies": ["golf","surfing"],
    "lname": "Smith",
    "relation": "friend",
    "title": "Mr.",
    "type": "contact"
}
{
    
    
    "age": 46,
    "children": [
        {
    
    "age": 17,"fname": "Xena","gender": "f"},
        {
    
    "age": 2,"fname": "Yuri","gender": "m"}
    ],
    "email": "[email protected]",
    "fname": "Earl",
    "hobbies": ["surfing"],
    "lname": "Johnson",
    "relation": "friend",
    "title": "Mr.",
    "type": "contact"
}
{
    
    
    "age": 18,
    "children": null,
    "email": "[email protected]",
    "fname": "Fred",
    "hobbies": ["golf","surfing"],
    "lname": "Jackson",
    "relation": "coworker",
    "title": "Mr.",
    "type": "contact"
}
{
    
    
    "age": 20,
    "email": "[email protected]",
    "fname": "Harry",
    "lname": "Jackson",
    "relation": "parent",
    "title": "Mr.",
    "type": "contact"
}
{
    
    
    "age": 56,
    "children": [
        {
    
    "age": 17,"fname": "Abama","gender": "m"},
        {
    
    "age": 21,"fname": "Bebama","gender": "m"}
    ],
    "email": "[email protected]",
    "fname": "Ian",
    "hobbies": ["golf","surfing"],
    "lname": "Taylor",
    "relation": "cousin",
    "title": "Mr.",
    "type": "contact"
}
{
    
    
    "age": 40,
    "contacts": [
        {
    
    "fname": "Fred"},
        {
    
    "fname": "Sheela"}
    ],
    "email": "[email protected]",
    "fname": "Jane",
    "lname": "Edwards",
    "relation": "cousin",
    "title": "Mrs.",
    "type": "contact"
}

条件选择和从某个文档库中选择

WHERE

tutorial文档库中选出所有fnameIan的文档内容。

SELECT * FROM tutorial WHERE fname = 'Ian'

返回第5个JSON文档:

{
    
    
  "results": [
      <5JSON文档的内容>
  ]
}

tutorial文档库中选出所有fnameDave的文档中children字段的第一项的fname,并命名为child_name

SELECT children[0].fname AS child_name FROM tutorial WHERE fname='Dave'

返回结果是第一个JSON文档中children字段的第一项的fname字段值:

{
    
    
  "results": [
    {
    
    
      "child_name": "Aiden"
    }
  ]
}

LIKE

同样的,N1QL中也有LIKE语句:

SELECT fname, email FROM tutorial WHERE email LIKE '%@yahoo.com'

选出所有用雅虎邮箱的文档的fnameemail

AND

和一般的SQL一样,不多说:

SELECT fname, email, children
    FROM tutorial
        WHERE ARRAY_LENGTH(children) > 0 AND email LIKE '%@gmail.com'

选择加计算

和一般SQL的计算一样,不用多说,当被计算的字段是数值类型时可用:

SELECT fname AS name_dog, age, age/7 AS age_dog_years FROM tutorial WHERE fname = 'Dave'

如果被计算的字段不是数值类型,那就会返回null

函数

和一般的SQL函数用法一样,不用多说:

SELECT fname, age, ROUND(age/7) AS age_dog_years FROM tutorial WHERE fname = 'Dave'

聚合函数也是一样:

SELECT COUNT(*) AS count FROM tutorial

分组

说到聚合函数就要说GROUP BY分组查询,N1QL和SQL里面的也是一样:

SELECT relation, COUNT(*) AS count FROM tutorial GROUP BY relation

返回:

{
    
    
  "results": [
    {
    
    "count": 1,"relation": "parent"},
    {
    
    "count": 2,"relation": "cousin"},
    {
    
    "count": 2,"relation": "friend"},
    {
    
    "count": 1,"relation": "coworker"}
  ]
}

选择分组

在分组聚合后取部分查询结果。比如在上面那个查亲属人数的语句基础上加一个HAVING子句:

SELECT relation, COUNT(*) AS count FROM tutorial GROUP BY relation HAVING COUNT(*) > 1

将只从亲属计数中返回人员总数大于1的查询结果:

{
    
    
  "results": [
    {
    
    "count": 2,"relation": "cousin"},
    {
    
    "count": 2,"relation": "friend"}
  ]
}

字符串连接

N1QL的字符串不是像SQL中的连接函数,而是用||符号:

SELECT fname || " " || lname AS full_name FROM tutorial

DISTINCT

和SQL一样,N1QL也有DISTINCT

SELECT DISTINCT relation FROM tutorial

返回所有的relation字段:

{
    
    
  "results": [
    {
    
    "relation": "friend"},
    {
    
    "relation": "coworker"},
    {
    
    "relation": "parent"},
    {
    
    "relation": "cousin"}
  ]
}

找空值

字段不存在和字段显式地指定为null都算空值:

SELECT fname, children FROM tutorial WHERE children IS NULL

返回children字段不存在或为null的文档的fnamechildren

{
  "results": [
    {
      "children": null,
      "fname": "Fred"
    }
  ]
}

排序和指定个数

和一般的SQL中排序一样,不多说:

SELECT fname, age FROM tutorial ORDER BY age LIMIT 2

跳过

OFFSET用于跳过结果,比如上面的N1QL语句加上OFFSET之后:

SELECT fname, age FROM tutorial ORDER BY age LIMIT 2 OFFSET 4

则返回按age排序,跳过前面4个结果,返回第5和6名。

进阶

返回数据库的元数据

Document databases such as Couchbase often store meta-data about a document outside of the document.

比如数据库中的文档ID是典型的数据库元数据:

SELECT META(tutorial) AS meta FROM tutorial

返回:

{
    
    
  "results": [
    {
    
    "meta": {
    
    "id": "dave"}},
    {
    
    "meta": {
    
    "id": "earl"}},
    {
    
    "meta": {
    
    "id": "fred"}},
    {
    
    "meta": {
    
    "id": "harry"}},
    {
    
    "meta": {
    
    "id": "ian"}},
    {
    
    "meta": {
    
    "id": "jane"}}
  ]
}

复合条件语句

复合条件语句的作用是判断一个JSON数组格式字段的所有值,有两种:

  • ANY [循环变量] IN [数组] SATISFIES [条件] END
  • EVERY [循环变量] IN [数组] SATISFIES [条件] END

顾名思义,ANY是指只要有一项满足就是trueEVERY必须要所有条件满足才返回true

ANY

选出家里有至少一个孩子在十岁以上的家庭的fname

SELECT fname
    FROM tutorial
        WHERE ANY child IN tutorial.children SATISFIES child.age > 10 END

EVERY

选出家里有全部孩子都在十岁以上的家庭的fname

SELECT fname
    FROM tutorial
        WHERE EVERY child IN tutorial.children SATISFIES child.age > 10 END

USE KEYS []

这个语句的功能和WHERE一样,都是按照某个字段找文档,但是WHERE是按照文档内的字段找文档,而USE KEYS []使用文档元数据中的文档ID找。显然,找文档ID比找文章内的值快:

SELECT * FROM tutorial USE KEYS ["dave", "ian"]

按照前面的META的结果,这个语句会返回第一个和第五个文档。

数组切片

N1QL中的数组切片和python一样,不多说,看看就懂:

SELECT children[0:2] FROM tutorial

返回每个文档的children字段的前两个值,如果文档的children字段值是null,那么返回字段值也是null;如果原文档没有children字段,则返回空字典{}

{
    
    
  "results": [
    {
    
    
      "$1": [
        {
    
    "age": 17,"fname": "Aiden","gender": "m"},
        {
    
    "age": 2,"fname": "Bill","gender": "f"}
      ]
    },
    {
    
    
      "$1": [
        {
    
    "age": 17,"fname": "Xena","gender": "f"},
        {
    
    "age": 2,"fname": "Yuri","gender": "m"}
      ]
    },
    {
    
    
      "$1": null
    },
    {
    
    },
    {
    
    
      "$1": [
        {
    
    "age": 17,"fname": "Abama","gender": "m"},
        {
    
    "age": 21,"fname": "Bebama","gender": "m"}
      ]
    },
    {
    
    }
  ]
}

IS NOT MISSING

IS NOT MISSING用于判断字段值是否存在,如果不存在则不返回。比如当上面的数组切片查询加上的这个子句之后:

SELECT children[0:2] FROM tutorial WHERE children[0:2] IS NOT MISSING

返回值中就不会有因为字段不存在而返回的空字典{}了。

ARRAY循环生成

N1QL中的ARRAY和python中的列表推导式很像:

SELECT fname AS parent_name,
ARRAY child.fname FOR child IN tutorial.children END AS child_names
FROM tutorial WHERE children IS NOT NULL

对比python中的列表推导式:

child_names = [child.fname for child in tutorial.children]

一看就懂,不多说。

高级

表连接

和SQL一样,按照结果中的数据进行连接。具体来说,Couchbase的表连接就是在一个文档库中找到特定字段为特定值的文档,然后和另一个文档连在一起:

SELECT * FROM users_with_orders usr
    JOIN orders_with_users orders
        ON KEYS ARRAY s.order_id FOR s IN usr.shipped_order_history END
  • 连接文档库users_with_ordersorders_with_users,并分别以usrorders作为其别名
  • orders_with_users文档库中文文档的ID为users_with_orders库中文档的shipped_order_history[i].order_id的值
  • 连接时,对文档库users_with_orders每个文档的shipped_order_history字段中的每一项,都取其order_id字段,以此为ID在orders_with_users文档库中查找文档
  • 对文档库users_with_orders每个文档,将orders_with_users文档库中中查找到的对应文档与之组合为结果的一项,其字段名为各自文档库的别名:
{
    
    
  "results": [
    {
    
    
        "usr": <users_with_orders中对应数据>,
        "orders":<orders_with_users中对应数据>
    },
    ...
  ]
}

LEFT JOIN

上面那个语句的连接操作没有LEFT JOIN子句,这时,如果users_with_orders中有某个文档在orders_with_users中没有对应的文库可以连接,那么这个文档就不会出现在结果中。而如果用了LEFT JOIN子句:

SELECT * FROM users_with_orders usr
    JOIN orders_with_users orders
        ON KEYS ARRAY s.order_id FOR s IN usr.shipped_order_history END

那么在orders_with_users中没有对应的文库可以连接的users_with_orders文档也会出现在结果中,只是没有orders_with_users中的字段。

LEFT JOIN将子句左边文档库的没有连接的项显示在结果中

NEST

NESTJOIN的功能完全一样,不一样的只是输出。

对于子句左边的每一项,JOIN将子句右边的连接结果与之一一相连,如果左边有一项和右边有3项可以相连,那么结果中就会有3个结果,这3个结果中左边的这个结果会重复3次。比如上面的那句N1QL可能输出:

{
    
    
  "results": [
    {
    
    
        "usr": <usr数据1>,
        "orders":<orders数据1>
    },
    {
    
    
        "usr": <usr数据1>,
        "orders":<orders数据2>
    },
    {
    
    
        "usr": <usr数据1>,
        "orders":<orders数据3>
    }
  ]
}

而如果改成NEST

SELECT * FROM users_with_orders usr
    NEST orders_with_users orders
        ON KEYS ARRAY s.order_id FOR s IN usr.shipped_order_history END

那么右边文档库orders_with_users中的那三个数据会被“nest”到一个数组里面,就像这样:

{
    
    
  "results": [
    {
    
    
        "usr": <usr数据1>,
        "orders":[<orders数据1>,<orders数据2>,<orders数据3>]
    }
  ]
}

结果的长度大大减小。

LEFT NEST

NESTLEFT NEST的区别就像JOINLEFT JOIN的区别一样,都是把左边没有连接的项也放在结果中,不再赘述。

猜你喜欢

转载自blog.csdn.net/weixin_54707168/article/details/113407919