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¶
- Tables resource reference
tablesAPI reference- Data pipeline guide — loading data into the table