【问题标题】:MAX(), DISTINCT and group by in CassandraCassandra 中的 MAX()、DISTINCT 和 group by
【发布时间】:2013-06-24 21:31:45
【问题描述】:

我正在尝试改造一个 SQL 数据库 Cassandra,以便我可以找到与 SQL 查询等效的 Cassandra。我使用 CQL 3 和 Cassandra v1.2。我在 cassandra 中对 db 设计进行了建模,使其支持 order by 子句和非规范化表以支持连接操作。但是,当涉及到 DISTINCT、SUM() 和 GROUPBY 等价物时,我感到很困惑

SELECT a1,MAX(b1) FROM demo1 group by a1.
SELECT DISTINCT (a2) FROM demo2 where b2='sea'
SELECT sum(a3), sum(b3) from demo3 where c3='water' and d3='ocean'

这对我过去几天的工作来说就像是一个炫耀。 Cassandra 中有没有一种方法可以对数据库模式进行建模以支持此类查询?我想不出 Cassandra 有什么办法。如何使用 Cassandra 实现此类查询?

我读到 Cassandra 上的蜂巢层可能会使这些查询工作。我只是想知道这是否是 Cassandra 支持此类查询的唯一方法..?请建议任何其他可能的方法..

【问题讨论】:

    标签: nosql cassandra cql cql3 nosql-aggregation


    【解决方案1】:

    使用 Cassandra,您可以通过在插入数据时做更多工作来解决这类问题 - 这听起来会很慢,但 Cassandra 专为快速写入而设计,您可能会读取更多数据比你写它的时间要多,所以当你考虑整个系统时它是有意义的。

    我无法准确告诉您如何创建表格来模拟您的问题,因为这在很大程度上取决于细节。您需要制定一个架构,让您无需执行任何即时聚合即可获取数据。考虑如何为 RDBMS 中的查询创建视图,然后尝试考虑如何将数据直接插入到这些视图中,而不是插入到基础表中。这就是您在 Cassandra 中建模事物的方式。

    【讨论】:

      【解决方案2】:

      虽然这是一个老问题,但它在 Google 搜索结果中的出现率很高。所以我想提供一个更新。

      Cassandra 2.2+ 支持用户定义的函数和用户定义的聚合。 警告:这并不意味着您不必再进行数据建模(正如@Theo 所指出的那样),它只是允许您在检索时稍微预处理数据。

      SELECT DISTINCT (a2) FROM demo2 where b2='sea'

      要实现DISTINCT,您应该定义一个函数和一个聚合门。我将调用函数和聚合 uniq 而不是 distinct 来强调它是用户定义的事实。

      CREATE OR REPLACE FUNCTION uniq(state set<text>, val text)
        CALLED ON NULL INPUT RETURNS set<text> LANGUAGE java
        AS 'state.add(val); return state;';
      CREATE OR REPLACE AGGREGATE uniq(text)
        SFUNC uniq STYPE set<text> INITCOND {};
      

      那你按如下方式使用:

      SELECT uniq(a2) FROM demo2 where b2='sea';
      

      SELECT sum(a3), sum(b3) from demo3 where c3='water' and d3='ocean'

      SUM 是开箱即用的,可以按照您的预期工作。见system.sum

      SELECT a1,MAX(b1) FROM demo1 group by a1

      GROUP BY 是一个棘手的问题。实际上,没有办法按某列对结果行进行分组。但是您可以做的是创建一个map&lt;text, int&gt; 并在地图中手动将它们分组。基于 Christopher Batey 博客中的一个示例,group-by 和 max:

      CREATE OR REPLACE FUNCTION state_group_and_max(state map<text, int>, type text, amount int)
        CALLED ON NULL INPUT
        RETURNS map<text, int>
        LANGUAGE java AS '
          Integer val = (Integer) state.get(type);
          if (val == null) val = amount; else val = Math.max(val, amount);
          state.put(type, val);
          return state;
        ' ;
      
      CREATE OR REPLACE AGGREGATE state_group_and_max(text, int) 
        SFUNC state_group_and_max
        STYPE map<text, int> 
        INITCOND {};
      

      那你按如下方式使用:

      SELECT state_group_and_max(a1, b1) FROM demo1;
      

      注意事项

      • 如上所述,您仍然需要在数据建模上投入一些时间,不要过度使用这些功能
      • 您必须在cassandra.yaml 中设置enable_user_defined_functions=true 才能启用这些功能
      • 您可以重载函数以支持按不同类型的列进行分组。

      参考资料:

      【讨论】:

      • 谢谢,这对我很有帮助。无论如何,我不妨在这里问。就我而言,我有一个计数器列族,我想获得一些带有最大计数器的字段,我想知道为什么默认的 max() 函数适用于计数器数据类型,但是当我使用你的 UDA 时它不起作用。它说它需要 int,并且计数器数据类型没有归类为 int。你有什么主意吗?之前谢谢。
      • 您好,根据docs.datastax.com/en/cql/3.0/cql/cql_reference/… 计数器类型是唯一的。这意味着您还必须为计数器类型重载函数。
      • 例如,自定义 max 函数将是: CREATE FUNCTION maxCustom(current counter, Candidate counter) CALLED ON NULL INPUT RETURNS counter LANGUAGE java AS 'if (current == null) return Candidate;否则返回 Math.max(current, Candidate);' CREATE AGGREGATE maxCustom(counter) SFUNC maxCustom STYPE counter INITCOND null;
      【解决方案3】:

      Cassandra 3.10 现在支持按分区键和集群键分组。详情可以参考this link

      【讨论】:

        【解决方案4】:

        Cassandra 不支持这样的操作。您可以在顶部使用 Hive 之类的东西,或者 Acunu 的(非免费)产品可以满足您的需求。

        另一种解决方案是自己完成工作。例如,您可以通过从某些行中读取所有数据并求和来求和。或者维护一个 Cassandra 计数器以动态递增。

        【讨论】:

        • 我确实想到了实现 sum() 方法的计数器选项。但是我必须为许多可以有很多值的列维护计数器。但是,这看起来像是 cassandra 可以支持 sum() 函数的唯一方式。感谢您的输入。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-09-20
        • 2010-11-20
        • 2012-10-30
        • 2022-01-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多