Skip to content

Create a table

Tables live inside namespaces and are defined by a column schema plus optional sorting, partitioning, and descriptive metadata. This guide builds a table step by step; for namespace management itself, see Manage namespaces & tables.

1. Create the namespace

A table's table_id is dot-separated: <namespace>.<table>. The namespace must exist first:

    namespace, _ = client.namespaces.create(
        namespace_id='raw.finance',
        title='Finance',
        description='Raw financial data',
    )

2. Define the columns

Columns can be declared with ColumnSchema models (importable from ineepy.models):

    from ineepy.models import ColumnSchema

    columns = [
        ColumnSchema(name='invoice_id', data_type='int64', required=True),
        ColumnSchema(name='customer', data_type='string'),
        ColumnSchema(name='issued_at', data_type='datetime'),
        ColumnSchema(name='amount', data_type='float64'),
    ]

or as plain dicts with the same fields — both forms are accepted everywhere a schema is expected:

    columns = [
        {'name': 'invoice_id', 'data_type': 'int64', 'required': True},
        {'name': 'customer', 'data_type': 'string'},
        {'name': 'issued_at', 'data_type': 'datetime'},
        {'name': 'amount', 'data_type': 'float64'},
    ]

3. Create the table

create() takes the schema plus optional metadata (title, description, tags, sensitivity, refresh frequency) and returns the created TableResponse together with its ETag:

    table, etag = client.tables.create(
        table_id='raw.finance.invoices',
        columns=columns,
        title='Invoices',
        description='One row per issued invoice',
        tags=['finance', 'invoices'],
        sensitivity='internal',
        refresh_frequency='daily',
    )
    print(f'Created {table.id} (etag {etag})')

4. Sorting and partitioning

sort_by and partition_by shape how data is laid out for reads. Both accept models (ColumnSorting, ColumnPartitioning from ineepy.models) or equivalent dicts. Partitioning supports transforms (TransformationType from ineepy.helpers) such as year, month, day, bucket, and truncate — a monthly partition on a timestamp column is a common choice:

    payments, _ = client.tables.create(
        table_id='raw.finance.payments',
        columns=[
            {'name': 'payment_id', 'data_type': 'int64'},
            {'name': 'paid_at', 'data_type': 'datetime'},
            {'name': 'amount', 'data_type': 'float64'},
        ],
        sort_by=[ColumnSorting(name='paid_at', direction='desc')],
        partition_by=[
            ColumnPartitioning(
                name='paid_at',
                transform=TransformationType.MONTH,
            )
        ],
    )

5. Evolve the schema

update_schema() replaces the full column list, guarded by the table's ETag:

    table, etag = client.tables.get_metadata(
        table_id='raw.finance.invoices'
    )
    updated, _ = client.tables.update_schema(
        table_id='raw.finance.invoices',
        etag=etag,
        columns=[
            {'name': 'invoice_id', 'data_type': 'int64', 'required': True},
            {'name': 'customer', 'data_type': 'string'},
            {'name': 'issued_at', 'data_type': 'datetime'},
            {'name': 'amount', 'data_type': 'float64'},
            {'name': 'currency', 'data_type': 'categorical'},
        ],
    )
    print(f'Columns now: {[c.name for c in updated.columns]}')

Danger

update_schema() is destructive: any column you omit from the new list is permanently deleted together with its data. Always send the complete schema you want to keep.

Supported data types

Type Description
'boolean' True / False
'string' UTF-8 text
'categorical' Low-cardinality string
'int32', 'int64' Integer
'float32', 'float64' Floating-point
'decimal' High-precision decimal
'date' Calendar date
'datetime' Date and time
'time' Time of day
'unknown' Untyped / deferred

See also