Bootstrap

ElasticSearch 7 SQL 详解

平时使用Elasticsearch的时候,会在Kibana中使用Query DSL来查询数据.每次要用到Query DSL时都基本忘光了,需要重新在回顾一遍,最近发现Elasticsearch已经支持SQL查询了(6.3版本以后),整理了下一些用法.

简介

Elasticsearch SQL是一个X-Pack组件,它允许针对Elasticsearch实时执行类似SQL的查询.无论使用REST接口,命令行还是JDBC,任何客户端都可以使用SQL对Elasticsearch中的数据进行原生搜索和聚合数据.可以将Elasticsearch SQL看作是一种翻译器,它可以将SQL翻译成Query DSL.

Elasticsearch SQL具有如下特性:

  • 原生支持:Elasticsearch SQL是专门为Elasticsearch打造的.
  • 没有额外的零件:无需其他硬件,处理器,运行环境或依赖库即可查询Elasticsearch,Elasticsearch SQL直接在Elasticsearch内部运行.
  • 轻巧高效:Elasticsearch SQL并未抽象化其搜索功能,相反的它拥抱并接受了SQL来实现全文搜索,以简洁的方式实时运行全文搜索.

准备

先安装好Elasticsearch和Kibana,这里安装的是7.17.0版本

安装完成后在Kibana中 http://127.0.0.1:5601/app/dev_tools#/console{target=“_blank”}
导入测试数据,数据地址: https://github.com/macrozheng/mall-learning/blob/master/document/json/accounts.json{target=“_blank”}

直接在Kibana的Dev Tools中运行如下命令即可:

POST /account/_bulk
{"index":{"_id":"1"}}
{"account_number":1,"balance":39225,"firstname":"Amber","lastname":"Duke","age":32,"gender":"M","address":"880 Holmes Lane","employer":"Pyrami","email":"[email protected]","city":"Brogan","state":"IL"}
{"index":{"_id":"6"}}
{"account_number":6,"balance":5686,"firstname":"Hattie","lastname":"Bond","age":36,"gender":"M","address":"671 Bristol Street","employer":"Netagy","email":"[email protected]","city":"Dante","state":"TN"}
{"index":{"_id":"13"}}
{"account_number":13,"balance":32838,"firstname":"Nanette","lastname":"Bates","age":28,"gender":"F","address":"789 Madison Street","employer":"Quility","email":"[email protected]","city":"Nogal","state":"VA"}
{"index":{"_id":"18"}}
{"account_number":18,"balance":4180,"firstname":"Dale","lastname":"Adams","age":33,"gender":"M","address":"467 Hutchinson Court","employer":"Boink","email":"[email protected]","city":"Orick","state":"MD"}
{"index":{"_id":"20"}}
{"account_number":20,"balance":16418,"firstname":"Elinor","lastname":"Ratliff","age":36,"gender":"M","address":"282 Kings Place","employer":"Scentric","email":"[email protected]","city":"Ribera","state":"WA"}
{"index":{"_id":"25"}}
{"account_number":25,"balance":40540,"firstname":"Virginia","lastname":"Ayala","age":39,"gender":"F","address":"171 Putnam Avenue","employer":"Filodyne","email":"[email protected]","city":"Nicholson","state":"PA"}
{"index":{"_id":"32"}}
{"account_number":32,"balance":48086,"firstname":"Dillard","lastname":"Mcpherson","age":34,"gender":"F","address":"702 Quentin Street","employer":"Quailcom","email":"[email protected]","city":"Veguita","state":"IN"}
{"index":{"_id":"37"}}
{"account_number":37,"balance":18612,"firstname":"Mcgee","lastname":"Mooney","age":39,"gender":"M","address":"826 Fillmore Place","employer":"Reversus","email":"[email protected]","city":"Tooleville","state":"OK"}
{"index":{"_id":"44"}}
{"account_number":44,"balance":34487,"firstname":"Aurelia","lastname":"Harding","age":37,"gender":"M","address":"502 Baycliff Terrace","employer":"Orbalix","email":"[email protected]","city":"Yardville","state":"DE"}
{"index":{"_id":"49"}}
{"account_number":49,"balance":29104,"firstname":"Fulton","lastname":"Holt","age":23,"gender":"F","address":"451 Humboldt Street","employer":"Anocha","email":"[email protected]","city":"Sunriver","state":"RI"}

第一个SQL查询

我们使用SQL来查询下前10条记录,可以通过format参数控制返回结果的格式,txt表示文本格式,看起来更直观点,默认为json格式.

在Kibana的Dev Tools中输入如下命令:

POST /_sql?format=txt
{
  "query": "SELECT account_number,address,age,balance FROM account LIMIT 10"
}

查询结果显示如下.

account_number |      address       |      age      |    balance    
---------------+--------------------+---------------+---------------
1              |880 Holmes Lane     |32             |39225          
6              |671 Bristol Street  |36             |5686           
13             |789 Madison Street  |28             |32838          
18             |467 Hutchinson Court|33             |4180           
20             |282 Kings Place     |36             |16418          
25             |171 Putnam Avenue   |39             |40540          
32             |702 Quentin Street  |34             |48086          
37             |826 Fillmore Place  |39             |18612          
44             |502 Baycliff Terrace|37             |34487          
49             |451 Humboldt Street |23             |29104          

如上实例,使用 _sql 指明使用SQL模块,在 query 字段中指定要执行的SQL语句.使用 format 指定返回数据的格式,数据格式可选项有以下几个,它们都是见名识意的:

formatAccept Http header说明
csvtext/csv逗号分隔
jsonapplication/jsonJson 格式
tsvtext/tab-separated-valuestab 分隔
txttext/plain文本格式
yamlapplication/yamlyaml
cborapplication/cbor简洁的二进制对象表示格式
smileapplication/smile类似于 cbor 的另一种二进制格式

将SQL转化为DSL

当我们需要使用Query DSL时,也可以先使用SQL来查询,然后通过Translate API转换即可.

例如我们翻译以下查询语句:

POST /_sql/translate
{
  "query": "SELECT account_number,address,age,balance FROM account WHERE age>32 LIMIT 10"
}

最终获取到Query DSL结果如下.

{
  "size" : 10,
  "query" : {
    "range" : {
      "age" : {
        "from" : 32,
        "to" : null,
        "include_lower" : false,
        "include_upper" : false,
        "boost" : 1.0
      }
    }
  },
  "_source" : false,
  "fields" : [
    {
      "field" : "account_number"
    },
    {
      "field" : "address"
    },
    {
      "field" : "age"
    },
    {
      "field" : "balance"
    }
  ],
  "sort" : [
    {
      "_doc" : {
        "order" : "asc"
      }
    }
  ]
}

然后可以用Query DSL 语法来查询:

GET /account/_search
{
  "size": 10,
  "query": {
    "range": {
      "age": {
        "from": 32,
        "to": null,
        "include_lower": false,
        "include_upper": false,
        "boost": 1
      }
    }
  },
  "_source": false,
  "fields": [
    {
      "field": "account_number"
    },
    {
      "field": "address"
    },
    {
      "field": "age"
    },
    {
      "field": "balance"
    }
  ],
  "sort": [
    {
      "_doc": {
        "order": "asc"
      }
    }
  ]
}

SQL和DSL混合使用

我们还可以将SQL和Query DSL混合使用,比如使用Query DSL来设置过滤条件.

例如查询 age在30-35 之间的记录,可以使用如下查询语句:

POST /_sql?format=txt
{
  "query": "SELECT account_number,address,age,balance FROM account",
  "filter": {
    "range": {
      "age": {
        "gte": 30,
        "lte": 35
      }
    }
  },
  "fetch_size": 10
}

SQL和ES对应关系

虽然 SQL 和 Elasticsearch 对于数据的组织方式(以及不同的语义)有不同的术语,但本质上它们的用途是相同的.下面是它们的映射关系表:

SQLElasticsearch说明
columnfield在 Elasticsearch 字段时,SQL 将这样的条目调用为 column.注意,在 Elasticsearch,一个字段可以包含同一类型的多个值(本质上是一个列表) ,而在 SQL 中,一个列可以只包含一个表示类型的值.Elasticsearch SQL 将尽最大努力保留 SQL 语义,并根据查询的不同,拒绝那些返回多个值的字段.
rowdocument列和字段本身不存在; 它们是行或文档的一部分.两者的语义略有不同: 行row往往是严格的(并且有更多的强制执行),而文档往往更灵活或更松散(同时仍然具有结构).
tableindex在 SQL 还是 Elasticsearch 中查询针对的目标
schemaimplicit在关系型数据库中,schema 主要是表的名称空间,通常用作安全边界.Elasticsearch没有为它提供一个等价的概念.

虽然这些概念之间的映射在语义上有些不同,但它们间更多的是有共同点,而不是不同点.

词法结构

ES SQL 的词法结构很大程度上类似于 ANSI SQL 本身.ES SQL 当前一次只能接受一个命令,这里的命令是由输入流结尾结束的 token 序列.这些 token 可以是关键字,标识符(带引号或者不带引号),文本(或者常量),特殊字符符号(通常是分隔符).

关键字

关键词这个其实跟我们写 SQL 语句那种关键字的定义是一样的,例如 SELECT,FROM 等都是关键字,需要注意的是,关键字不区分大小写.
SELECT * FROM my_table

如上示例,共有 4 个 token:SELECT, * ,FROM ,my_table,其中 SELECT,* ,FROM 是关键词,表示在 SQL 具有固定含义的词.而 my_table 是一个标识符,其表示了 SQL 中实体,如表,列等

标识符

标识符有两种类型:带引号的和不带引号的,示例如下:

SELECT ip_address FROM "hosts-*"

如上示例,查询中有两个标识符分别为不带引号的 ip_address 和带引号的 hosts-*(通配符模式).
因为 ip_address 不与任何关键字冲突,所以可以不带引号.而 hosts-*- (减号操作)和 * 冲突,所以要加引号.

📝注意: 对于标识符来说,应该尽量避免使用复杂的命名和与关键字冲突的命名,并且在输入的时候使用引号作为标识符,这样可以消除歧义.

直接常量

ES SQL 支持两种隐式的类型常量:字符串数字.

  • 字符串,字符串可以用单引号进行限定,例如: 'mysql' .如果在字符串中包含了单引号,则需要使用另一个单引号进行转义,例如: 'Captain EO''s Voyage' .
  • 数值常量,数值常量可以使用十进制和科学计数法进行表示,其示例如下:
1969    -- integer notation
3.14    -- decimal notation
.1234   -- decimal notation starting with decimal point
4E5     -- scientific notation (with exponent marker)
1.2e-3  -- scientific notation with decimal point

一个包含小数点的数值常量会被解析为 Double 类型.如果适合解析为整型,则解析为 Integer,否则解析为长整型(Long).

单引号,双引号

在 SQL 中,单引号和双引号具有不同的含义,不能互换使用.单引号用于声明字符串,而双引号用于表示标识符.示例如下:

SELECT "first_name" FROM "musicians"  WHERE "last_name"  = 'Carroll'    

如上示例,first_name,musicians,last_name 都是标识符,用双引号.而 Carroll 是字符串,用单引号.

特殊字符

一些非数字和字母的字符具有不同于运算符的专用含义,特殊字符有:

字符描述
*在一些上下文中表示数据表的所有字段,也可以表示某些聚合函数的参数.
,用于列举列表的元素
.用于数字常量或者分隔标识符限定符(表,列等)
()用于特定的 SQL 命令,函数声明,或者强制优先级.

运算符

ES SQL 中大多数的运算符它们的优先级都是相同的,并且是左关联.如果需要修改优先级,则要用括号来强制改变其优先级.下表是 ES SQL 支持的运算符和其优先级:

运算符结合性说明
.左结合限定符或者分割符
::左结合PostgreSQL-style 风格的类型转换符
+ -右结合一元加减符
* / %左结合乘法,除法,取模
+ -左结合加法,减法运算
BETWEEN IN LIKE范围包含,字符匹配
< > <= >= = <=> <> !=比较运算
NOT右结合逻辑非
AND左结合逻辑与
OR 左结合逻辑或

注释

ES SQL 支持两种注释:单行和多行注释,其示例如下:

-- single line comment,单行注释

/* multi
   line
   comment
   that supports /* nested comments */
   多行注释
   */

常用SQL操作

语法介绍

在ES中使用SQL查询的语法与在数据库中使用基本一致,具体格式如下:

SELECT select_expr [, ...]
[ FROM table_name ]
[ WHERE condition ]
[ GROUP BY grouping_element [, ...] ]
[ HAVING condition]
[ ORDER BY expression [ ASC | DESC ] [, ...] ]
[ LIMIT [ count ] ]
[ PIVOT ( aggregation_expr FOR column IN ( value [ [ AS ] alias ] [, ...] ) ) ]

WHERE

可以使用WHERE语句设置查询条件,比如查询state字段为VA的记录,查询语句如下.

POST /_sql?format=txt
{
  "query": "SELECT account_number,address,age,balance,state FROM account WHERE state='VA' LIMIT 10"
}

查询结果如下:

account_number |      address       |      age      |    balance    |     state     
---------------+--------------------+---------------+---------------+---------------
13             |789 Madison Street  |28             |32838          |VA             
486            |991 Applegate Court |22             |35902          |VA             
703            |489 Flatlands Avenue|29             |27443          |VA             
835            |641 Royce Street    |25             |46558          |VA             
897            |731 Poplar Street   |25             |45973          |VA             
564            |842 Congress Street |22             |43631          |VA             
588            |301 Anna Court      |31             |43531          |VA             
660            |916 Amersfort Place |33             |46427          |VA             
797            |919 Quay Street     |26             |6854           |VA             
836            |953 Dinsmore Place  |25             |20797          |VA   

GROUP BY

我们可以使用 GROUP BY 语句对数据进行分组,统计出分组记录数量,最大age和平均balance等信息,查询语句如下.

POST /_sql?format=txt
{
  "query": "SELECT state,COUNT(*),MAX(age),AVG(balance) FROM account GROUP BY state LIMIT 10"
}

HAVING

我们可以使用 HAVING 语句对分组数据进行二次筛选,比如筛选分组记录数量大于15的信息,查询语句如下.

POST /_sql?format=txt
{
  "query": "SELECT state,COUNT(*),MAX(age),AVG(balance) FROM account GROUP BY state HAVING COUNT(*)>15 LIMIT 10"
}

查询结果如下:

     state     |   COUNT(*)    |   MAX(age)    |   AVG(balance)   
---------------+---------------+---------------+------------------
AK             |22             |40             |26131.863636363636
AL             |25             |40             |25739.56          
AR             |18             |39             |27238.166666666668
CA             |17             |40             |22517.882352941175
CT             |16             |39             |28278.4375        
DC             |24             |40             |23180.583333333332
FL             |18             |38             |20443.444444444445

ORDER BY

我们可以使用ORDER BY语句对数据进行排序,比如按照balance字段从高到低排序,查询语句如下.

POST /_sql?format=txt
{
  "query": "SELECT account_number,address,age,balance,state FROM account ORDER BY balance DESC LIMIT 10 "
}

查询结果如下:

account_number |       address        |      age      |    balance    |     state     
---------------+----------------------+---------------+---------------+---------------
248            |717 Hendrickson Place |36             |49989          |WA             
854            |603 Cooper Street     |25             |49795          |AL             
240            |659 Highland Boulevard|35             |49741          |NH             
97             |512 Cumberland Walk   |40             |49671          |MO             
842            |833 Bushwick Court    |23             |49587          |TX             
168            |975 Flatbush Avenue   |20             |49568          |IL             
803            |963 Highland Avenue   |25             |49567          |MS             
926            |833 Quincy Street     |21             |49433          |VT             
954            |688 Hart Street       |22             |49404          |MD             
572            |994 Chester Court     |20             |49355          |UT    

DESCRIBE

我们可以使用 DESCRIBE 语句查看表(ES中为索引)中有哪些字段,比如查看account表的字段,查询语句如下.

POST /_sql?format=txt
{
  "query": "DESCRIBE account"
}

查询结果如下:

     column      |     type      |    mapping    
-----------------+---------------+---------------
account_number   |BIGINT         |long           
address          |VARCHAR        |text           
address.keyword  |VARCHAR        |keyword        
age              |BIGINT         |long           
balance          |BIGINT         |long           
city             |VARCHAR        |text           
city.keyword     |VARCHAR        |keyword        
email            |VARCHAR        |text           
email.keyword    |VARCHAR        |keyword        
employer         |VARCHAR        |text           
employer.keyword |VARCHAR        |keyword        
firstname        |VARCHAR        |text           
firstname.keyword|VARCHAR        |keyword        
gender           |VARCHAR        |text           
gender.keyword   |VARCHAR        |keyword        
lastname         |VARCHAR        |text           
lastname.keyword |VARCHAR        |keyword        
state            |VARCHAR        |text           
state.keyword    |VARCHAR        |keyword  

SHOW TABLES

我们可以使用 SHOW TABLES 查看所有的表(ES中为索引).

POST /_sql?format=txt
{
  "query": "SHOW TABLES"
}

查询结果如下:

#! this request accesses system indices: [.kibana_7.17.0_001, .kibana_task_manager_7.17.0_001], but in a future major version, direct access to system indices will be prevented by default
#! this request accesses system indices: [.apm-agent-configuration, .apm-custom-link, .async-search, .kibana_7.17.0_001, .kibana_task_manager_7.17.0_001, .tasks], but in a future major version, direct access to system indices will be prevented by default
    catalog    |             name              |     type      |     kind      
---------------+-------------------------------+---------------+---------------
my-application |.apm-agent-configuration       |TABLE          |INDEX          
my-application |.apm-custom-link               |TABLE          |INDEX          
my-application |.async-search                  |TABLE          |INDEX          
my-application |.kibana                        |VIEW           |ALIAS          
my-application |.kibana_7.17.0                 |VIEW           |ALIAS          
my-application |.kibana_7.17.0_001             |TABLE          |INDEX          
my-application |.kibana_task_manager           |VIEW           |ALIAS          
my-application |.kibana_task_manager_7.17.0    |VIEW           |ALIAS          
my-application |.kibana_task_manager_7.17.0_001|TABLE          |INDEX          
my-application |.tasks                         |TABLE          |INDEX          
my-application |account                        |TABLE          |INDEX          
my-application |kibana_sample_data_flights     |TABLE          |INDEX 

支持的函数

使用SQL查询ES中的数据,不仅可以使用一些SQL中的函数,还可以使用一些ES中特有的函数.

查询支持的函数

我们可以使用 SHOW FUNCTIONS 语句查看所有支持的函数,比如搜索所有带有 DATE 字段的函数可以使用如下语句.

POST /_sql?format=txt
{
  "query": "SHOW FUNCTIONS LIKE '%DATE%'"
}

查询结果如下:

     name      |     type      
---------------+---------------
CURDATE        |SCALAR         
CURRENT_DATE   |SCALAR         
DATEADD        |SCALAR         
DATEDIFF       |SCALAR         
DATEPART       |SCALAR         
DATETIME_FORMAT|SCALAR         
DATETIME_PARSE |SCALAR         
DATETRUNC      |SCALAR         
DATE_ADD       |SCALAR         
DATE_DIFF      |SCALAR         
DATE_PARSE     |SCALAR         
DATE_PART      |SCALAR         
DATE_TRUNC     |SCALAR  

全文搜索函数

全文搜索函数是ES中特有的,当使用 MATCHQUERY 函数时,会启用全文搜索功能,SCORE 函数可以用来统计搜索评分.

MATCH()

使用MATCH函数查询address中包含Street的记录.

POST /_sql?format=txt
{
  "query": "SELECT account_number,address,age,balance,SCORE() FROM account WHERE MATCH(address,'Street') LIMIT 10"
}

查询结果如下:

account_number |        address        |      age      |    balance    |    SCORE()    
---------------+-----------------------+---------------+---------------+---------------
6              |671 Bristol Street     |36             |5686           |0.95395315     
13             |789 Madison Street     |28             |32838          |0.95395315     
32             |702 Quentin Street     |34             |48086          |0.95395315     
49             |451 Humboldt Street    |23             |29104          |0.95395315     
51             |334 River Street       |31             |14097          |0.95395315     
63             |510 Sedgwick Street    |30             |6077           |0.95395315     
87             |446 Halleck Street     |22             |1133           |0.95395315     
107            |694 Jefferson Street   |28             |48844          |0.95395315     
138            |422 Malbone Street     |39             |9006           |0.95395315     
140            |878 Schermerhorn Street|32             |26696          |0.95395315   
QUERY()

使用 QUERY 函数查询address中包含Street的记录.

POST /_sql?format=txt
{
  "query": "SELECT account_number,address,age,balance,SCORE() FROM account WHERE QUERY('address:Street') LIMIT 10"
}

查询结果如下:

account_number |        address        |      age      |    balance    |    SCORE()    
---------------+-----------------------+---------------+---------------+---------------
6              |671 Bristol Street     |36             |5686           |0.95395315     
13             |789 Madison Street     |28             |32838          |0.95395315     
32             |702 Quentin Street     |34             |48086          |0.95395315     
49             |451 Humboldt Street    |23             |29104          |0.95395315     
51             |334 River Street       |31             |14097          |0.95395315     
63             |510 Sedgwick Street    |30             |6077           |0.95395315     
87             |446 Halleck Street     |22             |1133           |0.95395315     
107            |694 Jefferson Street   |28             |48844          |0.95395315     
138            |422 Malbone Street     |39             |9006           |0.95395315     
140            |878 Schermerhorn Street|32             |26696          |0.95395315     

SQL CLI

如果你不想使用Kibana来使用ES SQL的话,也可以使用ES自带的SQL CLI来查询,该命令位于ES的bin目录下.

使用如下命令启动SQL CLI:

elasticsearch-sql-cli http://localhost:9200

然后直接输入SQL命令即可查询了,注意要加分号.

SELECT account_number,address,age,balance FROM account LIMIT 10;

ES SQL 的局限性

使用SQL查询ES有一定的局限性,没有原生的Query DSL那么强大,对于嵌套属性和某些函数的支持并不怎么好,但是平时用来查询下数据基本够用了.

ES SQL 使用实战

我们先准备数据,此处我们将使用 Kibana 提供的航班数据:

如下图,在 Kibana 中点击左边栏的 Analytics 下的 Overview ,右边的页面中选择 DashBoard 然后点击 Install some sample data 链接,
再点击 Sample flight data 即可加入航班的数据.
在这里插入图片描述
可以使用以下语句查看航班数据:

POST /kibana_sample_data_flights/_search
{
  "query": {
    "match_all": {}
  }
}

下面来看看常用的 SQL 如何编写.

1. WHERE

我们过滤出目的地为 US 的数据:

POST /_sql?format=txt
{
  "query": "SELECT FlightNum, OriginWeather, OriginCountry, Carrier FROM kibana_sample_data_flights WHERE DestCountry = 'US'"
}

查询结果如下:

   FlightNum   |   OriginWeather   | OriginCountry |    Carrier     
---------------+-------------------+---------------+----------------
R43CELD        |Cloudy             |US             |JetBeats        
3YAQM9U        |Clear              |US             |JetBeats        
8SHQI41        |Cloudy             |US             |JetBeats        
HF9AP10        |Sunny              |US             |JetBeats        
ZTL6FPB        |Heavy Fog          |IT             |ES-Air          
TF9BTQL        |Clear              |JP             |Kibana Airlines 
T9QK7GX        |Clear              |IN             |Logstash Airways
4AHGESO        |Rain               |ZA             |Kibana Airlines 
J684XSR        |Sunny              |AR             |JetBeats        
T390OH4        |Cloudy             |IN             |ES-Air          
Q33SYKK        |Sunny              |KR             |JetBeats        
JBQ50Y2        |Clear              |IT             |Logstash Airways

2. GROUP BY

可以使用 GROUP BY 语句对数据进行分组聚合统计操作,例如查询航班分组的平均飞行距离等.其示例如下:

POST /_sql?format=txt
{
  "query": "SELECT count(*),max(DistanceMiles), avg(DistanceMiles) FROM kibana_sample_data_flights GROUP BY DestCountry"
}

如上示例,我们以目的地国家进行分组,然后统计每个分组的数量,最大的飞行距离,平均飞行距离.其结果如下:

   count(*)    |max(DistanceMiles)|avg(DistanceMiles)
---------------+------------------+------------------
46             |7600.7158203125   |3233.800320625305 
305            |12140.8603515625  |6603.605808945953 
377            |9917.6455078125   |3128.910634331741 
416            |10832.3994140625  |7915.6610843951885
944            |10600.296875      |4077.664177652133 
691            |10293.208984375   |2775.8247816469493
45             |12075.3935546875  |7542.028591579861 
1096           |12353.7802734375  |5037.134736095902 
91             |10000.7255859375  |5683.497867123111 
278            |10030.87109375    |3448.2222546090325
48             |9670.9072265625   |3278.826272328695 
237            |10575.1279296875  |5419.154288118902 
15             |10346.84765625    |3214.9680114746093

3. HAVING

可以使用 HAVING 对分组的数据进行二次筛选,比如筛选分组中记录数大于 100 的数据,其结果如下:

POST /_sql?format=txt
{
  "query": "SELECT count(*),max(DistanceMiles), avg(DistanceMiles) FROM kibana_sample_data_flights GROUP BY DestCountry HAVING COUNT(*) > 100"
}

我们过滤出了分组中记录数大于 100 的数据,其结果如下:

   count(*)    |max(DistanceMiles)|avg(DistanceMiles)
---------------+------------------+------------------
305            |12140.8603515625  |6603.605808945953 
377            |9917.6455078125   |3128.910634331741 
416            |10832.3994140625  |7915.6610843951885
944            |10600.296875      |4077.664177652133 
691            |10293.208984375   |2775.8247816469493
1096           |12353.7802734375  |5037.134736095902 
278            |10030.87109375    |3448.2222546090325
237            |10575.1279296875  |5419.154288118902 
449            |10282.5048828125  |3213.2889483309536
373            |10774.0           |5064.675941446831 

4. ORDER BY

我们可以使用 ORDER BY 进行排序,例如将平均飞行距离降序排序,其结果如下:

POST /_sql?format=txt
{
  "query": "SELECT count(*),max(DistanceMiles), avg(DistanceMiles) as avgDistance FROM kibana_sample_data_flights GROUP BY DestCountry HAVING COUNT(*) > 100 ORDER BY avgDistance desc"
}

如上示例,我们将数据用平均距离排序,其结果为

   count(*)    |max(DistanceMiles)|   avgDistance    
---------------+------------------+------------------
416            |10832.3994140625  |7915.6610843951885
305            |12140.8603515625  |6603.605808945953 
283            |10556.7587890625  |6030.0211101842015
237            |10575.1279296875  |5419.154288118902 
214            |11447.2265625     |5323.084783429297 
774            |11407.380859375   |5280.042444507589 
116            |10553.98828125    |5118.16688169282  
373            |10774.0           |5064.675941446831 

5. 分页

分页有多种实现方式,可以使用 limit,top,fetch_size 来进行分页.

1,使用limit 分页操作

POST /_sql?format=txt
{
  "query": "SELECT FlightNum, OriginWeather, OriginCountry, Carrier FROM kibana_sample_data_flights WHERE DestCountry = 'US' limit 10"
}

2,使用 top 进行分页

POST /_sql?format=txt
{
  "query": "SELECT top 10 FlightNum, OriginWeather, OriginCountry, Carrier FROM kibana_sample_data_flights WHERE DestCountry = 'US'"
}

3,使用 fetch_size 进行分页

POST /_sql?format=txt
{
  "query": "SELECT FlightNum, OriginWeather, OriginCountry, Carrier FROM kibana_sample_data_flights WHERE DestCountry = 'US'",
  "fetch_size": 10
}

其结果如下:

   FlightNum   | OriginWeather | OriginCountry |    Carrier     
---------------+---------------+---------------+----------------
R43CELD        |Cloudy         |US             |JetBeats        
3YAQM9U        |Clear          |US             |JetBeats        
8SHQI41        |Cloudy         |US             |JetBeats        
HF9AP10        |Sunny          |US             |JetBeats        
ZTL6FPB        |Heavy Fog      |IT             |ES-Air          
TF9BTQL        |Clear          |JP             |Kibana Airlines 
T9QK7GX        |Clear          |IN             |Logstash Airways
4AHGESO        |Rain           |ZA             |Kibana Airlines 
J684XSR        |Sunny          |AR             |JetBeats        
T390OH4        |Cloudy         |IN             |ES-Air  

6. 子查询

ES SQL 是可以支持类似于 SELECT X FROM (SELECT * FROM Y) 这样简单的子查询的

POST /_sql?format=txt
{
  "query": "SELECT avg(data.DistanceMiles) from (SELECT FlightNum, OriginWeather, OriginCountry, Carrier, DistanceMiles FROM kibana_sample_data_flights WHERE DestCountry = 'US') as data"
}

其结果如下:

avg(data.DistanceMiles)
-----------------------
4714.944895442431      

参考资料

官方文档:xpack-sql{target=“_blank”}

;