
# 语法文档
#### 符号约定
本节把需要原样输入的单词用大写表示，需要原样输入的字符用单引号括起来。
'\[x\]'表示语句'x'可以出现一次或不出现。
'(x)'表示语句'x'是个整体。'(x, ...)'表示语句'x'可以出现一次或多次，多次之间用逗号连接。
'\|'表示所有可能的替代情况。
'expression'表示任意表达式。特殊地，'bool_expression'表示任意布尔表达式。
'identifier'表示一个合法的标识符。由字符'0-9,a-z,A-Z,_'组成，且不能以数字开头。
'column_name'表示一个合法的字段名。它可以是一个'identifier'或多个嵌套，如'A.id'。
'table_name'表示一个合法的表名。ResourceQL语法规定'table_name'必须为'resources'。
用双引号括起来的单位会被认为是一个整体。例如，若需表示带有特殊字符的列名，需在其前后加双引号。
#### 查询的基本语法
```
[WITH (with_item, ...)]
SELECT [DISTINCT | ALL] (select_item, ...)
[FROM (from_item, ...)]
[WHERE bool_expression]
[GROUP BY [DISTINCT | ALL] (expression, ...)]
[HAVING booleanExpression]
[ORDER BY (expression [ASC | DESC] [NULLS (FIRST | LAST)], ...)]
[LIMIT number]
```
'select_item'支持对字段名进行重命名和运算，也支持全选。
```
select_item = (expression [[AS] column_name_aias]) | *
```
'from_item'支持join函数和嵌套子查询，且支持对表名进行重命名。
```
from_item = table_name [[AS] table_name_aias]
          | (from_item join_type from_item [(ON bool_expression) | USING(column_name, ...)])
          | '(' query ')'
```
'with_item'用来定制模板化询问，以方便后续多次调用。
```
with_item = identifier AS '(' query ')'
```
例如，查询每个区域内数量大于100的资源类型，可以使用如下语句：
```
WITH counts AS (
    SELECT region_id, provider, type, count(*) AS number FROM resources 
    GROUP BY region_id, provider, type
) SELECT * FROM counts WHERE number > 100
```
#### 数值运算和布尔运算
ResourceQL支持对整型和浮点型进行二元数学运算，运算符包括'+,-,\*,/,%'。
相同类型的值之间可以比较，比较符包括'\<,\>,\<=,\>=,=,\<\>,!='（最后两个符号都表示"不等于"）。数值之间比的是大小，字符串之间比的是字典序。数值和集合之间也可以进行比较，此时比较符右侧为'ALL \| SOME \| ANY'中的一种，用来限定比较范围。'ALL'表示集合里所有元素都要满足，'SOME/ANY'表示至少一个元素满足即可。
```
expression ('=' | '<>' | '!=' | '<' | '>' | '<=' | '>=') 
expression
expression ('=' | '<>' | '!=' | '<' | '>' | '<=' | '>=') 
[ALL | SOME | ANY] '(' query ')'
```
'bool_expression'表示任意布尔表达式（运算后返回True或False），包括以下几种语法：
```
NOT bool_expression
bool_expression (AND | OR) bool_expression
expression [NOT] BETWEEN expression AND expression
expression [NOT] IN '(' query ')'
EXISTS '(' query ')'
expression [NOT] LIKE pattern [ESCAPE escape_characters]
expression IS [NOT] NULL
expression IS [NOT] DISTINCT FROM expression
```
特别地，运算符'\|\|'会对左右两边的值进行连接并返回连接后的新值，左右两侧类型相同且均为数组或字符串。
#### 时间类型
ResourceQL支持查询时间类型的字段。查询结果会折算成零时区并以ISODate的标准格式返回，保留至毫秒。
时间类型可以用比较运算符连接。如果想使用表示时间的字面量，请写作timestamp 'time'的形式。其中的'time'可以是任意的ISODate格式或者常用时间格式。以下的'time'写法都是被允许的。
2019-06-17T12:55:42.233Z
2019-06-17T12:55:42Z
2019-06-17 12:55:42
2019-06-17T12:55:42.00 + 08:00
2019-06-17 05:55:40 - 06:00
2019-06-17
2019
如果不加时区则默认为零时区，不加24小时时刻则默认为0:00，不加月份则默认为1月1日。
例如，把2020年9月12日12:55:00以来创建的资源按更新时间降序排序，可以使用如下语句：
```
select name, created, updated from resources 
where created >= timestamp '2020-09-12T12:55:00Z' 
order by updated DESC
```
#### 模糊查询
```
string LIKE pattern [ESCAPE escape_characters]
```
'LIKE'用来判断字符串是否符合某种pattern。如果pattern里想表达'%'或者'_'这两种字符的字面量，可以在'ESCAPE'后指定转义符（如'#'），在pattern里写成'#%'和'#_'即可。
通配符'%'表示匹配0或多个字符。
通配符'_'表示正好匹配一个字符。
对象存储桶的模糊查询，可以写成如下形式：
```
SELECT name, id FROM resources 
WHERE provider = 'obs' AND type = 'buckets' AND name LIKE '%figure%'
```
或
```
SELECT name, id FROM resources 
WHERE provider = 'obs' AND type = 'buckets' AND name LIKE '%figure#_%' ESCAPE '#'
```
#### 条件函数
CASE关键字可以根据情况选择不同的返回值。它有以下两种用法。
- 计算给定表达式expression的值，根据不同的值返回对应的结果。
- 依次计算每一个bool_expression的值，找到第一条符合要求的expression并返回对应的结果。
```
CASE expression
    WHEN value1 THEN result1
    [WHEN value2 THEN result2]
    [...]
    [ELSE result]
END
CASE
    WHEN condition1 THEN result1
    WHEN condition2 THEN result2
    [...]
    [ELSE result]
END
```
IF关键字的用法有以下两种。
- 'IF(bool_expression, value)'：如果布尔表达式值为真就返回'value'，否则返回NULL。
- 'IF(bool_expression, value1, value2)'：如果布尔表达式值为真就返回'value1'，否则返回 'value2'。
 
#### 用函数来简化查询
ResourceQL提供丰富的函数来简化查询。详细函数说明请参见[函数列表](https://support.huaweicloud.com/usermanual-rms/rms_06_0703.html)。
ResourceQL支持lambda表达式。某些函数的参数可能是另一个函数，此时用lambda表达式就很方便。
例如，查询与所有ECS关联的EVS，可以使用如下的语句：
```
SELECT ECS.id AS ecs_id, EVS.id AS evs_id FROM
(SELECT id, transform(properties.ExtVolumesAttached, x -> x.id) AS evs_list 
FROM resources WHERE provider = 'ecs' AND type = 'cloudservers') ECS,
    (SELECT id FROM resources WHERE provider = 'evs' AND type = 'volumes') EVS
    WHERE contains(ecs.evs_list, evs.id)
```
其中'contains(a, element) →boolean'：可以判断某元素是否出现在数组a中。
'transform(array(T), function(T, S))→array(S)'能够把某个类型的数组变换成另一个类型的数组。
#### Join和Unnest
ResourceQL支持'JOIN'和'UNNEST'。'JOIN'分为以下四种类型。
- \[INNER\] JOIN
- LEFT \[OUTER\] JOIN
- RIGHT \[OUTER\] JOIN
- FULL \[OUTER\] JOIN
'JOIN'后需紧跟'USING(...)'或'ON \<bool_expression\>'。
'USING'用来指定参与join的若干个列名。
'ON'接受一个布尔表达式，若值为真则合并。出于性能考虑，布尔表达式的合取范式里需保证至少有一个等式，且该等式左右两端的运算内容被左右两张表独立提供。
'JOIN'前可以冠上'NATURAL'关键词表示自然连接，这样后面不用'USING'或'ON'连接。
'UNNEST'能把数组解包成表，加上'WITH ORDINALITY'会有一个自动计数的列，格式如下：
```
table_name CROSS JOIN UNNEST '(' (expression, ...) ')' [WITH ORDINALITY]
```
注意，'CROSS JOIN'只能用于和 'UNNEST'连接，ResourceQL不支持其他格式的'CROSS JOIN'。
上述查询ECS和EVS关联的例子还可以写成如下形式：
```
SELECT ECS_EVS.id AS ecs_id, EVS.id AS evs_id FROM 
(SELECT id, evs_id FROM (SELECT id, transform(properties.ExtVolumesAttached, x ->x.id) AS evs_list
         FROM resources WHERE provider = 'ecs' AND type = 'cloudservers') ECS 
     CROSS JOIN UNNEST(evs_list) AS t (evs_id)) ECS_EVS, 
     (SELECT id FROM resources WHERE provider = 'evs' AND type = 'volumes') EVS 
     WHERE ECS_EVS.evs_id = EVS.id
```
