SQL 语句示例:

SQL 语句示例:

基本语法举例

  • 从 topic 为 “t/a” 的消息中提取所有字段:
SELECT * FROM "t/a"
  • 从 topic 为 “t/a” 或 “t/b” 的消息中提取所有字段:
SELECT * FROM "t/a","t/b"
  • 从 topic 能够匹配到 ‘t/#’ 的消息中提取所有字段。
SELECT * FROM "t/#"
  • 从 topic 能够匹配到 ‘t/#’ 的消息中提取 qos, username 和 clientid 字段:
SELECT qos, username, clientid FROM "t/#"
  • 从任意 topic 的消息中提取 username 字段,并且筛选条件为 username = ‘Steven’:
SELECT username FROM "#" WHERE username='Steven'
  • 从任意 topic 的 JSON 消息体(payload) 中提取 x 字段,并创建别名 x 以便在 WHERE 子句中使用。WHERE 子句限定条件为 x = 1。下面这个 SQL 语句可以匹配到消息体 {“x”: 1}, 但不能匹配到消息体 {“x”: 2}:
SELECT payload as p FROM "#" WHERE p.x = 1
  • 类似于上面的 SQL 语句,但嵌套地提取消息体中的数据,下面的 SQL 语句可以匹配到 JSON 消息体 {“x”: {“y”: 1}}:
SELECT payload as a FROM "#" WHERE a.x.y = 1
  • 在 clientid = ‘c1’ 尝试连接时,提取其来源 IP 地址和端口号:
SELECT peername as ip_port FROM "$events/client_connected" WHERE clientid = 'c1'
  • 筛选所有订阅 ‘t/#’ 主题且订阅级别为 QoS1 的 clientid:
SELECT clientid FROM "$events/session_subscribed" WHERE topic = 't/#' and qos = 1
  • 筛选所有订阅主题能匹配到 ‘t/#’ 且订阅级别为 QoS1 的 clientid。注意与上例不同的是,这里用的是主题匹配操作符 ‘=~’ ,所以会匹配订阅 ‘t’ 或 ‘t/+/a’ 的订阅事件:
SELECT clientid FROM "$events/session_subscribed" WHERE topic =~ 't/#' and qos = 1
  • FROM 子句后面的主题需要用双引号 "" 引起来。
  • WHERE 子句后面接筛选条件,如果使用到字符串需要用单引号 '' 引起来。
  • FROM 子句里如有多个主题,需要用逗号 "," 分隔。例如 SELECT * FROM “t/1”, “t/2” 。
  • 可以使用使用 "." 符号对 payload 进行嵌套选择。

遍历语法(FOREACH-DO-INCASE) 举例

假设有 ClientID 为 c_steve、主题为 t/1 的消息,消息体为 JSON 格式,其中 sensors 字段为包含多个 Object 的数组:

{
    "date": "2020-04-24",
    "sensors": [
        {"name": "a", "idx":0},
        {"name": "b", "idx":1},
        {"name": "c", "idx":2}
    ]
}

示例1: 要求将 sensors 里的各个对象,分别作为数据输入重新发布消息到sensors/${idx} 主题,内容为 ${name}。即最终规则引擎将会发出 3 条消息:

1) 主题:sensors/0 内容:a 2) 主题:sensors/1 内容:b 3) 主题:sensors/2 内容:c

要完成这个规则,我们需要配置如下动作:

  • 动作类型:消息重新发布 (republish)
  • 目的主题:sensors/${idx}
  • 目的 QoS:0
  • 消息内容模板:${name}

以及如下 SQL 语句:

FOREACH
    payload.sensors
FROM "t/#"

示例解析:

这个 SQL 中,FOREACH 子句指定需要进行遍历的数组 sensors,则选取结果为:

[
  {
    "name": "a",
    "idx": 0
  },
  {
    "name": "b",
    "idx": 1
  },
  {
    "name": "c",
    "idx": 2
  }
]

FOREACH 语句将会对于结果数组里的每个对象分别执行 “消息重新发布” 动作,所以将会执行重新发布动作 3 次。

示例2: 要求将 sensors 里的idx 值大于或等于 1 的对象,分别作为数据输入重新发布消息到 sensors/${idx} 主题,内容为 clientid=${clientid},name=${name},date=${date}。即最终规则引擎将会发出 2 条消息:

1) 主题:sensors/1 内容:clientid=c_steve,name=b,date=2020-04-24 2) 主题:sensors/2 内容:clientid=c_steve,name=c,date=2020-04-24

要完成这个规则,我们需要配置如下动作:

  • 动作类型:消息重新发布 (republish)
  • 目的主题:sensors/${idx}
  • 目的 QoS:0
  • 消息内容模板:clientid=${clientid},name=${name},date=${date}

以及如下 SQL 语句:

FOREACH
    payload.sensors
DO
    clientid,
    item.name as name,
    item.idx as idx
INCASE
    item.idx >= 1
FROM "t/#"

示例解析:

这个 SQL 中,FOREACH 子句指定需要进行遍历的数组 sensors; DO 子句选取每次操作需要的字段,这里我们选了外层的 clientid 字段,以及当前 sensor 对象的 name 和 idx 两个字段,注意 item 代表 sensors 数组中本次循环的对象。INCASE 子句是针对 DO 语句中字段的筛选条件,仅仅当 idx >= 1 满足条件。所以 SQL 的选取结果为:

[
  {
    "name": "b",
    "idx": 1,
    "clientid": "c_emqx"
  },
  {
    "name": "c",
    "idx": 2,
    "clientid": "c_emqx"
  }
]

FOREACH 语句将会对于结果数组里的每个对象分别执行 “消息重新发布” 动作,所以将会执行重新发布动作 2 次。

在 DO 和 INCASE 语句里,可以使用 item 访问当前循环的对象,也可以通过在 FOREACH 使用 as 语法自定义一个变量名。所以本例中的 SQL 语句又可以写为:

FOREACH
    payload.sensors as s
DO
    clientid,
    s.name as name,
    s.idx as idx
INCASE
    s.idx >= 1
FROM "t/#"

示例3: 在示例2 的基础上,去掉 clientid 字段c_steve 中的 c_ 前缀

在 FOREACH 和 DO 语句中可以调用各类 SQL 函数,若要将 c_steve 变为 steve,则可以把例2 中的 SQL 改为:

FOREACH
    payload.sensors as s
DO
    nth(2, tokens(clientid,'_')) as clientid,
    s.name as name,
    s.idx as idx
INCASE
    s.idx >= 1
FROM "t/#"

另外,FOREACH 子句中也可以放多个表达式,只要最后一个表达式是指定要遍历的数组即可。比如我们将消息体改一下,sensors 外面多套一层 Object:

{
    "date": "2020-04-24",
    "data": {
        "sensors": [
            {"name": "a", "idx":0},
            {"name": "b", "idx":1},
            {"name": "c", "idx":2}
        ]
    }
}

则 FOREACH 中可以在决定要遍历的数组之前把 data 选取出来:

FOREACH
    payload.data as data
    data.sensors as s
...

CASE-WHEN 语法示例

示例1: 将消息中 x 字段的值范围限定在 0~7 之间。

SELECT
  CASE WHEN payload.x < 0 THEN 0
       WHEN payload.x > 7 THEN 7
       ELSE payload.x
  END as x
FROM "t/#"

假设消息为:

{"x": 8}

则上面的 SQL 输出为:

{"x": 7}