You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Cube.js如何确定JOIN的根(左)表?规则与确定性探究

Cube.js JOIN根表选择规则及确定性实现问题

问题背景

给定如下Schema:

account:
   joins:
      - account_list
      - person
      - company

person:
   joins:
      - account
      - contact

company:
   joins: 
      - account
      - contact      

执行以下查询:

{
  "query": {
    "measures": ["contact.count"],
    "filters": [
      { "member": "contact.id", "operator": "set" },
      { "member": "account.list_id", "operator": "equals", "values": ["34475"] },
      { "member": "person.id", "operator": "notSet" }
    ]
  }
}

实际生成的JOIN关系为account -> account_list, person -> contact,而非预期的contact -> person -> account -> account_list,与统计contact数量的需求不符。

在过滤器中添加{ "member": "company.id", "operator": "set" }后,JOIN关系变为contact -> company, account -> person, account_list,临时解决了当前问题,但担忧过滤器变化会改变JOIN关系。

现询问:Cube.js选择JOIN根(左)表的规则是什么?如何实现可预测且确定的根表选择?


回答

Cube.js选择JOIN根表的核心规则

  • 度量所属表优先:查询中的度量归属表会被优先考虑作为根表起点,但该规则会受过滤器、关联关系约束影响,并非绝对。
  • 过滤器驱动调整:当过滤器引用其他表字段时,Cube.js会调整JOIN链起点以满足过滤关联需求,这也是添加company.id过滤器后JOIN关系变化的原因——过滤器强制关联company表,间接让contact成为根表链的一部分。
  • 关联方向识别:Cube.js会识别Schema中表的外键关联方向,若某表是被引用方(如account被person关联),无强制约束时可能被选为根表。
  • 性能优化导向:Cube.js会倾向选择数据量更小、能减少JOIN次数的表作为根表以优化性能,这也会带来根表选择的不确定性。

实现可预测且确定的根表选择的方法

  • 明确关联关系定义:在Schema的JOIN配置中,不要仅写表名,需通过relationshipprimaryKeyforeignKey明确关联方向,让Cube.js清晰识别表间依赖。示例修改后的Schema:
    account:
      joins:
        - join: account_list
          relationship: belongsTo
          primaryKey: id
          foreignKey: account_id
        - join: person
          relationship: hasMany
          primaryKey: id
          foreignKey: account_id
        - join: company
          relationship: hasMany
          primaryKey: id
          foreignKey: account_id
    
    person:
      joins:
        - join: account
          relationship: belongsTo
          primaryKey: id
          foreignKey: account_id
        - join: contact
          relationship: hasMany
          primaryKey: id
          foreignKey: person_id
    
    company:
      joins:
        - join: account
          relationship: belongsTo
          primaryKey: id
          foreignKey: account_id
        - join: contact
          relationship: hasMany
          primaryKey: id
          foreignKey: company_id
    
  • 手动指定JOIN逻辑:在Schema中通过sql字段直接定义JOIN语句,完全控制关联链,不受查询参数影响。示例:
    contact:
      sql: >
        SELECT * FROM contacts
        JOIN persons ON contacts.person_id = persons.id
        JOIN accounts ON persons.account_id = accounts.id
        JOIN account_lists ON accounts.id = account_lists.account_id
    
  • 查询中强制指定根表:在查询参数里通过from字段明确根表,确保Cube.js以指定表为起点构建JOIN链。示例修改后的查询:
    {
      "query": {
        "from": "contact",
        "measures": ["contact.count"],
        "filters": [
          { "member": "contact.id", "operator": "set" },
          { "member": "account.list_id", "operator": "equals", "values": ["34475"] },
          { "member": "person.id", "operator": "notSet" }
        ]
      }
    }
    

内容的提问来源于stack exchange,提问作者Dima

火山引擎 最新活动