AMQL Docs
Modeling patterns

Composite keys pattern

Learn how to join two models on several columns at once

What is a composite key?

A composite key is a join key made of more than one column. No single column is unique on its own, but the combination is.

For example, a shipment is identified by its order and the warehouse it ships from. An order can ship from several warehouses, so order_id alone repeats in shipments. The pair (order_id, warehouse_id) is unique.

Sample data:

order_lines

idorder_idwarehouse_idqty
l1o1w12
l2o1w21
l3o2w14

shipments

order_idwarehouse_idshipped_oncarrier
o1w12025-01-03DHL
o1w22025-01-05UPS
o2w12025-01-04DHL

The question: how many units did each carrier ship?

Solution: one key pair per column

Declare a many_to_one relationship. Put the first column pair in from and to, and add one on {} block for each extra pair. A row matches only when every pair is equal.

Step 1: Define models

First, define the underlying models for both tables to expose their dimensions and measures. This ensures that the fields are correctly typed and ready to be joined.

order_lines.model.aml
Model order_lines {
  type: 'table'
  table_name: 'ecommerce.order_lines'
  data_source_name: 'your_datasource_name'

  dimension id {
    type: 'text'
    primary_key: true
  }

  dimension order_id { type: 'text' }
  dimension warehouse_id { type: 'text' }
  dimension qty { type: 'number' }

  measure total_qty {
    label: 'Total Quantity'
    type: 'number'
    definition: @aql sum(order_lines.qty);;
  }
}
shipments.model.aml
Model shipments {
  type: 'table'
  table_name: 'ecommerce.shipments'
  data_source_name: 'your_datasource_name'

  dimension order_id { type: 'text' }
  dimension warehouse_id { type: 'text' }
  dimension shipped_on { type: 'date' }
  dimension carrier { type: 'text' }
}

Step 2: Define the relationship

Next, define the relationship between the two models using the composite key columns. This tells AMQL how to join the tables when they are queried together.

order_line_shipment.relationship.aml
Relationship order_line_shipment {
  type: 'many_to_one'
  from: r(order_lines.order_id)
  to: r(shipments.order_id)
  on { from: r(order_lines.warehouse_id) to: r(shipments.warehouse_id) }
}

The many_to_one claim applies to the pair. Each order line matches at most one shipment, because (order_id, warehouse_id) is unique in shipments. For a three-column key, add a second on {} block.

Step 3: Add it to a dataset

Then, add the models and relationship to your dataset to make them available for querying. This exposes the unified data structure to your business users.

fulfillment.dataset.aml
Dataset fulfillment {
  data_source_name: 'your_datasource_name'

  models: [order_lines, shipments]

  relationships: [
    relationship(order_line_shipment, true)
  ]
}

Step 4: Query

Finally, run a query to see how AMQL joins the tables and aggregates the data. You can now explore the combined fields seamlessly.

explore {
  dimensions { carrier: shipments.carrier }
  measures { qty: sum(order_lines.qty) }
}

AMQL emits one join with both conditions:

LEFT JOIN shipments
  ON  order_lines.order_id     = shipments.order_id
  AND order_lines.warehouse_id = shipments.warehouse_id

Result:

carrierqty
DHL6
UPS1

The total is 7, the same as the sum of order_lines.qty. Each order line is counted once.

Key points

  • Declare every column of the key. The many_to_one claim is only true over the full combination. Leave a column out and the join fans out.
  • Uniqueness is your claim. AMQL does not check that the combination is unique on the to side. If it is not, numbers are wrong in the same way as any false many_to_one.
  • Works on every database. The emitted SQL is a plain multi-column equality join.

Additional resources

On this page