Data Types
YDB SQLAlchemy provides comprehensive support for YDB data types through custom SQLAlchemy types. This guide covers the available types and their usage.
Overview
YDB has a rich type system that includes primitive types, optional types, containers, and special types. The YDB SQLAlchemy dialect maps these types to appropriate SQLAlchemy types and provides YDB-specific types for optimal performance. For more information about YDB data types, see the YDB Type System Documentation.
Type Mapping Summary
The following table shows the complete mapping between YDB native types, YDB SQLAlchemy types, standard SQLAlchemy types, and Python types:
YDB Native Type |
YDB SA Type |
SA Type |
Python Type |
Notes |
|---|---|---|---|---|
|
|
|
||
|
|
-2^7 to 2^7-1 |
||
|
|
-2^15 to 2^15-1 |
||
|
|
-2^31 to 2^31-1 |
||
|
|
|
-2^63 to 2^63-1, default integer type |
|
|
|
0 to 2^8-1 |
||
|
|
0 to 2^16-1 |
||
|
|
0 to 2^32-1 |
||
|
|
0 to 2^64-1 |
||
|
|
|
||
|
|
|
Available in SQLAlchemy 2.0+ |
|
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
Extended date range support |
||
|
|
|
||
|
|
Extended datetime range |
||
|
|
|
||
|
|
Extended timestamp range |
||
|
|
|
||
|
|
|
||
|
|
|||
|
|
|
Standard SQLAlchemy Types
Most standard SQLAlchemy types work with YDB:
from sqlalchemy import Column, Integer, String, Boolean, Float, Text
class MyTable(Base):
__tablename__ = 'my_table'
id = Column(Integer, primary_key=True)
name = Column(String(100))
description = Column(Text)
is_active = Column(Boolean)
price = Column(Float)
YDB-Specific Integer Types
YDB provides specific integer types with defined bit widths:
from ydb_sqlalchemy.sqlalchemy.types import (
Int8, Int16, Int32, Int64,
UInt8, UInt16, UInt32, UInt64
)
class IntegerTypesExample(Base):
__tablename__ = 'integer_types'
id = Column(UInt64, primary_key=True) # Unsigned 64-bit integer
small_int = Column(Int16) # Signed 16-bit integer
byte_value = Column(UInt8) # Unsigned 8-bit integer (0-255)
counter = Column(UInt32) # Unsigned 32-bit integer
For detailed API reference, see:
Int8, Int16, Int32, Int64,
UInt8, UInt16, UInt32, UInt64.
Binary Type
YDB maps standard SQLAlchemy BINARY (or LargeBinary) type to its String type, which supports storing raw bytes.
from sqlalchemy import LargeBinary
class BinaryData(Base):
__tablename__ = 'binary_data'
id = Column(UInt64, primary_key=True)
data = Column(LargeBinary)
# Usage
session.add(BinaryData(
id=1,
data=b'raw bytes data \x00\x01'
))
Decimal Type
YDB supports high-precision decimal numbers:
from ydb_sqlalchemy.sqlalchemy.types import Decimal
import decimal
class FinancialData(Base):
__tablename__ = 'financial_data'
id = Column(UInt64, primary_key=True)
# Default: Decimal(22, 9) - 22 digits total, 9 after decimal point
amount = Column(Decimal())
# Custom precision and scale
precise_amount = Column(Decimal(precision=15, scale=4))
# Return as float instead of Decimal object
percentage = Column(Decimal(precision=5, scale=2, asdecimal=False))
# Usage
session.add(FinancialData(
id=1,
amount=decimal.Decimal('1234567890123.123456789'),
precise_amount=decimal.Decimal('12345678901.1234'),
percentage=99.99
))
For detailed API reference, see: Decimal.
Date and Time Types
YDB provides several date and time types:
from ydb_sqlalchemy.sqlalchemy.types import (
YqlDate, YqlDateTime, YqlTimestamp,
YqlDate32, YqlDateTime64, YqlTimestamp64
)
from sqlalchemy import DateTime
import datetime
class EventLog(Base):
__tablename__ = 'event_log'
id = Column(UInt64, primary_key=True)
# Date only (YYYY-MM-DD) - standard range
event_date = Column(YqlDate)
# Date32 - extended date range support
extended_date = Column(YqlDate32)
# DateTime with timezone support - standard range
created_at = Column(YqlDateTime(timezone=True))
# DateTime64 - extended range
precise_datetime = Column(YqlDateTime64(timezone=True))
# Timestamp (high precision) - standard range
precise_time = Column(YqlTimestamp)
# Timestamp64 - extended range with microsecond precision
extended_timestamp = Column(YqlTimestamp64)
# Standard SQLAlchemy DateTime also works
updated_at = Column(DateTime)
# Usage
now = datetime.datetime.now(datetime.timezone.utc)
today = datetime.date.today()
session.add(EventLog(
id=1,
event_date=today,
extended_date=today,
created_at=now,
precise_datetime=now,
precise_time=now,
extended_timestamp=now,
updated_at=now
))
For detailed API reference, see:
YqlDate, YqlDateTime, YqlTimestamp,
YqlDate32, YqlDateTime64, YqlTimestamp64.
Struct Type
YDB supports structured data types via StructType. This type maps to a Python dictionary.
The StructType allows defining complex nested structures and is often used with AS_TABLE for bulk operations.
It supports nullable fields using the Optional wrapper.
You can create a StructType manually or generate it from an existing SQLAlchemy Table using the helper method from_table().
from ydb_sqlalchemy.sqlalchemy.types import StructType, Optional, Int32, UInt64, Utf8
# 1. Manual StructType definition
# Represents a structure: Struct<id: Int32, name: Utf8, data: Optional<Utf8>>
my_struct = StructType({
"id": Int32,
"name": Utf8,
"data": Optional(Utf8) # Nullable field
})
You can also generate a StructType directly from a SQLAlchemy Table definition. This is particularly useful for bulk operations like UPSERT where the structure needs to match the table schema exactly.
# 2. Generating StructType from a Table
# Useful for AS_TABLE bulk operations where the structure matches a table
from sqlalchemy import Table, Column, Integer, String, MetaData
metadata = MetaData()
user_table = Table(
"users",
metadata,
Column("id", Integer, primary_key=True),
Column("name", String),
Column("email", String, nullable=True)
)
# Generates: Struct<id: Integer, name: String, email: Optional<String>>
user_struct = StructType.from_table(user_table)
# 3. Using StructType with AS_TABLE for efficient bulk updates (Upsert)
# This example demonstrates how to perform a bulk UPSERT using a list of structs.
import sqlalchemy as sa
from ydb_sqlalchemy.sqlalchemy import upsert
from ydb_sqlalchemy.sqlalchemy.types import ListType
# Define the list of data to be upserted
data_to_upsert = [
{"id": 1, "name": "Alice", "email": "alice@example.com"},
{"id": 2, "name": "Bob", "email": None},
]
# Define bind parameter
# Wrap the StructType in ListType because we are passing a list of structs
bind_param = sa.bindparam("data", value=data_to_upsert, type_=ListType(user_struct))
# Define columns to select from the AS_TABLE source
# AS_TABLE returns a table-like structure, so we need to select columns from it
cols = [sa.column(c.name, type_=c.type) for c in user_table.columns]
# Create the upsert statement
# We use from_select to insert data selected from the bound parameter :data
stmt = upsert(user_table).from_select(
[c.name for c in user_table.columns],
sa.select(*cols).select_from(sa.func.AS_TABLE(bind_param))
)
# Execute the statement
# session.execute(stmt)
For detailed API reference, see: StructType and Optional.