也许有点晚了,但我想分享一下我基于@moshevi和@Seb的答案所构建的东西:
在我的物联网应用场景中,我需要实际的子分区(第一级为“year”,第二级为“nodeid”)。此外,我还希望稍微进行一些泛化。
这就是我想出来的内容:
from sqlalchemy.ext.declarative import DeclarativeMeta
from sqlalchemy.sql.ddl import DDL
from sqlalchemy import event
class PartitionByMeta(DeclarativeMeta):
def __new__(cls, clsname, bases, attrs, *, partition_by, partition_type):
@classmethod
def get_partition_name(cls_, suffix):
return f'{cls_.__tablename__}_{suffix}'
@classmethod
def create_partition(cls_, suffix, partition_stmt, subpartition_by=None, subpartition_type=None):
if suffix not in cls_.partitions:
partition = PartitionByMeta(
f'{clsname}{suffix}',
bases,
{'__tablename__': cls_.get_partition_name(suffix)},
partition_type = subpartition_type,
partition_by=subpartition_by,
)
partition.__table__.add_is_dependent_on(cls_.__table__)
event.listen(
partition.__table__,
'after_create',
DDL(
f"""
ALTER TABLE {cls_.__tablename__}
ATTACH PARTITION {partition.__tablename__}
{partition_stmt};
"""
)
)
cls_.partitions[suffix] = partition
return cls_.partitions[suffix]
if partition_by is not None:
attrs.update(
{
'__table_args__': attrs.get('__table_args__', ())
+ (dict(postgresql_partition_by=f'{partition_type.upper()}({partition_by})'),),
'partitions': {},
'partitioned_by': partition_by,
'get_partition_name': get_partition_name,
'create_partition': create_partition
}
)
return super().__new__(cls, clsname, bases, attrs)
假设由 @moshevi 引入的 VehicleDataMixin
类已被创建,下面介绍如何使用:
class VehicleData(VehicleDataMixin, Project, metaclass=PartitionByMeta, partition_by='timestamp',partition_type='RANGE'):
__tablename__ = 'vehicle_data'
__table_args__ = (
Index('ts_ch_nod_idx', "timestamp", "nodeid", "channelid", postgresql_using='brin'),
UniqueConstraint('timestamp','nodeid','channelid', name='ts_ch_nod_constr')
)
然后可以像这样迭代地进行子分区(需进行适当调整)
for y in range(2017, 2021):
tbl_vehid_y = VehicleData.create_partition(
f"{y}", partition_stmt=f"""FOR VALUES FROM ('{y}-01-01') TO ('{y+1}-01-01')""",
subpartition_by='nodeid', subpartition_type='LIST'
)
for i in {3, 4, 7, 9}:
tbl_vehid_y.create_partition(
f"nid{i}", partition_stmt=f"""FOR VALUES IN ('{i}')"""
)
tbl_vehid_y.create_partition("def", partition_stmt="DEFAULT")
VehicleData.create_partition("def", partition_stmt="DEFAULT")
partition_by='timestamp'
<= 这是用于分区的列
partition_type='RANGE'
<= 这是(特定于PSQL)的分区类型
partition_stmt=f"""FOR VALUES IN ('{i}')"""
<= 这是(特定于PSQL)的分区语句。