> ## Documentation Index
> Fetch the complete documentation index at: https://docs.activeviam.com/llms.txt
> Use this file to discover all available pages before exploring further.

# atoti.Table.join()

#### Table.join(target, mapping=None, /, \*, target\_optionality='optional')

Define a join between this source table and the *target* table.

There are two kinds of joins:

* full join if all the key columns of the *target* table are mapped and the joined tables share the same locality (either both [`Table`](./atoti.table#atoti.Table) or both `ExternalTable`).
* partial join otherwise.

Depending on the cube creation mode, the join will also generate different hierarchies and measures:

* `manual`: No hierarchy is automatically created.
  For partial joins, creating a hierarchy for each mapped key column is necessary before creating hierarchies for the other columns.
  Once these required hierarchies exist, hierarchies for the un-mapped key columns of the *target* table will automatically be created.

* `no_measures`: All the key columns and non-numeric columns of the *target* table will be converted into hierarchies.
  No measures will be created in this mode.

* `auto`: The same hierarchies as in the `no_measures` mode will be created.
  Additionally, columns of the fact table containing numeric values (including arrays), except for columns which are keys, will be converted into measures.
  Columns of the *target* table with these types will not be converted into measures.

* **Parameters:**
  * **target** ([*Table*](./atoti.table#atoti.Table)) – The other table to join.
  * **mapping** (*TableJoinMappingCondition* *|* *None*) – An equality-based condition from columns of this table to columns of the *target* table.
    If `None`, the key columns of the *target* table with the same name as columns in this table will be used.
  * **target\_optionality** (*RelationshipOptionality*) –

    The relationship optionality on the *target* table side.

    * `"optional"` declares no constraints: a row in the source table does not need to have a matching row in the *target* table.
    * `"mandatory"` declares that every row in the source table has at least one matching row in the *target* table at all time.
      In the future, this hint will enable some optimizations when incrementally refreshing DirectQuery data.

* **Return type:**
  None

### Example

```pycon theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
>>> sales_table = session.create_table(
...     "Sales",
...     data_types={"ID": "String", "Product ID": "String", "Price": "int"},
... )
>>> products_table = session.create_table(
...     "Products",
...     data_types={"ID": "String", "Name": "String", "Category": "String"},
... )
>>> sales_table.join(
...     products_table, sales_table["Product ID"] == products_table["ID"]
... )
```
