【问题标题】:SQL like GROUP BY AND HAVINGSQL 类似于 GROUP BY AND HAVING
【发布时间】:2018-04-05 02:57:24
【问题描述】:

我想获取满足特定条件的组的计数。在 SQL 术语中,我想在 Elasticsearch 中执行以下操作。

SELECT COUNT(*) FROM
(
   SELECT
    senderResellerId,
    SUM(requestAmountValue) AS t_amount
   FROM
    transactions
   GROUP BY
    senderResellerId
   HAVING
    t_amount > 10000 ) AS dum;

到目前为止,我可以通过术语聚合按 senderResellerId 进行分组。但是当我应用过滤器时,它不能按预期工作。

弹性请求

{
  "aggregations": {
    "reseller_sale_sum": {
      "aggs": {
        "sales": {
          "aggregations": {
            "reseller_sale": {
              "sum": {
                "field": "requestAmountValue"
              }
            }
          }, 
          "filter": {
            "range": {
              "reseller_sale": { 
                "gte": 10000
              }
            }
          }
        }
      }, 
      "terms": {
        "field": "senderResellerId", 
        "order": {
          "sales>reseller_sale": "desc"
        }, 
        "size": 5
      }
    }
  }, 
  "ext": {}, 
  "query": {  "match_all": {} }, 
  "size": 0
}

实际反应

{
  "took" : 21,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "failed" : 0
  },
  "hits" : {
    "total" : 150824,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "reseller_sale_sum" : {
      "doc_count_error_upper_bound" : -1,
      "sum_other_doc_count" : 149609,
      "buckets" : [
        {
          "key" : "RES0000000004",
          "doc_count" : 8,
          "sales" : {
            "doc_count" : 0,
            "reseller_sale" : {
              "value" : 0.0
            }
          }
        },
        {
          "key" : "RES0000000005",
          "doc_count" : 39,
          "sales" : {
            "doc_count" : 0,
            "reseller_sale" : {
              "value" : 0.0
            }
          }
        },
        {
          "key" : "RES0000000006",
          "doc_count" : 57,
          "sales" : {
            "doc_count" : 0,
            "reseller_sale" : {
              "value" : 0.0
            }
          }
        },
        {
          "key" : "RES0000000007",
          "doc_count" : 134,
          "sales" : {
            "doc_count" : 0,
            "reseller_sale" : {
              "value" : 0.0
            }
          }
        }
          }
        }
      ]
    }
  }
}

从上面的响应中可以看出,它正在返回经销商,但 reseller_sale 聚合的结果为零。

更多详情请联系here

【问题讨论】:

标签: elasticsearch elasticsearch-5


【解决方案1】:

HAVING-like 行为的实现

您可以使用pipeline aggregations 之一,即bucket selector aggregation。查询如下所示:

POST my_index/tdrs/_search
{
   "aggregations": {
      "reseller_sale_sum": {
         "aggregations": {
            "sales": {
               "sum": {
                  "field": "requestAmountValue"
               }
            },
            "max_sales": {
               "bucket_selector": {
                  "buckets_path": {
                     "var1": "sales"
                  },
                  "script": "params.var1 > 10000"
               }
            }
         },
         "terms": {
            "field": "senderResellerId",
            "order": {
               "sales": "desc"
            },
            "size": 5
         }
      }
   },
   "size": 0
}

将以下文档放入索引后:

  "hits": [
     {
        "_index": "my_index",
        "_type": "tdrs",
        "_id": "AV9Yh5F-dSw48Z0DWDys",
        "_score": 1,
        "_source": {
           "requestAmountValue": 7000,
           "senderResellerId": "ID_1"
        }
     },
     {
        "_index": "my_index",
        "_type": "tdrs",
        "_id": "AV9Yh684dSw48Z0DWDyt",
        "_score": 1,
        "_source": {
           "requestAmountValue": 5000,
           "senderResellerId": "ID_1"
        }
     },
     {
        "_index": "my_index",
        "_type": "tdrs",
        "_id": "AV9Yh8TBdSw48Z0DWDyu",
        "_score": 1,
        "_source": {
           "requestAmountValue": 1000,
           "senderResellerId": "ID_2"
        }
     }
  ]

查询的结果是:

"aggregations": {
      "reseller_sale_sum": {
         "doc_count_error_upper_bound": 0,
         "sum_other_doc_count": 0,
         "buckets": [
            {
               "key": "ID_1",
               "doc_count": 2,
               "sales": {
                  "value": 12000
               }
            }
         ]
      }
   }

即只有那些累积销售额为>10000senderResellerId

计算桶数

要实现SELECT COUNT(*) FROM (... HAVING) 的等效项,可以使用bucket script aggregationsum bucket aggregation 的组合。尽管似乎没有直接的方法来计算bucket_selector 实际选择了多少个桶,但我们可以定义一个bucket_script,根据条件产生01,以及产生@987654342 的sum_bucket @:

POST my_index/tdrs/_search
{
   "aggregations": {
      "reseller_sale_sum": {
         "aggregations": {
            "sales": {
               "sum": {
                  "field": "requestAmountValue"
               }
            },
            "max_sales": {
               "bucket_script": {
                  "buckets_path": {
                     "var1": "sales"
                  },
                  "script": "if (params.var1 > 10000) { 1 } else { 0 }"
               }
            }
         },
         "terms": {
            "field": "senderResellerId",
            "order": {
               "sales": "desc"
            }
         }
      },
      "max_sales_stats": {
         "sum_bucket": {
            "buckets_path": "reseller_sale_sum>max_sales"
         }
      }
   },
   "size": 0
}

输出将是:

   "aggregations": {
      "reseller_sale_sum": {
         "doc_count_error_upper_bound": 0,
         "sum_other_doc_count": 0,
         "buckets": [
            ...
         ]
      },
      "max_sales_stats": {
         "value": 1
      }
   }

所需的桶数位于max_sales_stats.value

重要注意事项

我必须指出两点:

  1. 该功能是实验性的(从 ES 5.6 开始,它仍然是实验性的,尽管它是在 2.0.0-beta1 中添加的。)
  2. 管道聚合应用于先前聚合的结果:

管道聚合作用于其他聚合产生的输出,而不是 从文档集中,将信息添加到输出树。

这意味着bucket_selector 聚合将在terms 聚合对senderResellerId 的结果之后应用。例如,如果senderResellerId 的数量比terms 聚合定义的size 多,您将不会获得所有 集合中sum(sales) > 10000 的ID,而只会获得那些出现在terms 聚合的输出。考虑使用排序和/或设置足够的size 参数。

这也适用于第二种情况,COUNT() (... HAVING),它只会计算聚合输出中实际存在的那些桶。

如果这个查询太重或者桶的数量太大,考虑denormalizing你的数据或者直接把这个和存储在文档中,这样你就可以使用普通的range查询来实现你的目标。

【讨论】:

  • 非常感谢。 :) 如果我提到 INT_MAX 作为我的大小,你能告诉我如何计算桶的数量吗?
  • 我只想要桶的数量,而不是桶的内容。
  • @Akram 手动写入值而不是 INT_MAX。 AFAIK。
  • @Akram 现在我想我明白你需要什么了。如果您只关心COUNT(*) 部分,并希望省略它所依赖的存储桶,您可以使用filter_path 指定要显示响应的哪一部分。请告诉我这是否适合您,然后我会将其添加到答案中。
  • @Akram 如果您需要过滤生成的存储桶(HAVING 部分),ES 必须生成它们,我认为没有办法解决这个问题。也许您可以针对您的设置提出另一个问题?有一个描述和几个例子会更容易回答。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-31
  • 2018-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多