Todos os produtos
Search
Central de documentação

MaxCompute:Tables

Última atualização: Sep 22, 2026

O PyODPS permite criar, ler, gravar e excluir tabelas do MaxCompute programaticamente. Você também pode gerenciar esquemas, partições e transferências de dados em massa por meio do MaxCompute Tunnel.

Pré-requisitos

Antes de começar, verifique se você tem:

  • Um projeto do MaxCompute

  • O PyODPS instalado e configurado

  • Um AccessKey ID e um AccessKey secret

  • Um objeto de entrada do MaxCompute inicializado (o)

Início rápido

Crie uma tabela, grave dados e leia-os em seguida:

from odps.models import Schema

# Define columns
schema = Schema.from_lists(['id', 'name'], ['bigint', 'string'])

# Create the table
table = o.create_table('my_table', schema, if_not_exists=True)

# Write records
o.write_table('my_table', [[1, 'Alice'], [2, 'Bob']])

# Read records
for record in o.read_table('my_table'):
    print(record)

Resumo dos métodos da API

Operação

Método

Parâmetros principais

Descrição

Listar tabelas

o.list_tables()

prefix, type, extended

Lista todas as tabelas em um projeto

Verificar existência

o.exist_table()

table_name

Verifica se uma tabela existe

Obter tabela

o.get_table()

table_name, project

Obtém um objeto de tabela

Criar tabela

o.create_table()

schema, if_not_exists, lifecycle

Cria uma tabela

Gravar dados

o.write_table()

partition, create_partition

Adiciona registros a uma tabela

Ler dados

o.read_table()

partition

Lê registros de uma tabela

Excluir tabela

o.delete_table()

if_exists

Exclui uma tabela

Converter para DataFrame

table.to_df()

—

Converte uma tabela em um DataFrame do PyODPS

Sincronizar metadados

table.reload()

—

Atualiza o objeto de tabela local a partir do servidor

Para a referência completa dos métodos do PyODPS, consulte Python SDK overview.

Listar tabelas

Liste todas as tabelas em um projeto:

for table in o.list_tables():
    print(table)

Filtre por prefixo de nome:

for table in o.list_tables(prefix="table_prefix"):
    print(table.name)

Por padrão, list_tables() retorna apenas os nomes das tabelas. A consulta a propriedades como table_schema ou creation_time aciona solicitações adicionais e aumenta a latência. A partir do PyODPS 0.11.5, passe extended=True para recuperar essas propriedades em uma única chamada:

for table in o.list_tables(extended=True):
    print(table.name, table.creation_time)

Filtre por tipo de tabela:

# Valid types: managed_table, external_table, virtual_view, materialized_view
managed_tables = list(o.list_tables(type="managed_table"))
external_tables = list(o.list_tables(type="external_table"))
virtual_views = list(o.list_tables(type="virtual_view"))
materialized_views = list(o.list_tables(type="materialized_view"))

Verificar se uma tabela existe

print(o.exist_table('pyodps_iris'))
# Returns True if the table exists

Obter informações da tabela

Obtenha um objeto de tabela com get_table():

t = o.get_table('pyodps_iris')

Imprima o esquema da tabela:

print(t.schema)

Saída:

odps.Schema {
  sepallength           double      # Sepal length (cm)
  sepalwidth            double      # Sepal width (cm)
  petallength           double      # Petal length (cm)
  petalwidth            double      # Petal width (cm)
  name                  string      # Type
}

Acesse os detalhes das colunas:

# All columns
print(t.schema.columns)

# A specific column
print(t.schema['sepallength'])

# Column comment
print(t.schema['sepallength'].comment)

Acesse as propriedades da tabela:

print(t.lifecycle)        # Table lifecycle
print(t.creation_time)    # Creation time
print(t.is_virtual_view)  # Whether the table is a view
print(t.size)             # Table size in bytes
print(t.comment)          # Table comment

Acessar tabelas entre projetos

Passe o parâmetro project para obter uma tabela de outro projeto:

t = o.get_table('table_name', project='other_project')

Crie um esquema de tabela

Existem dois métodos para criar esquemas.

Método 1: Objetos Column e Partition

Use os objetos Column e Partition do odps.models quando precisar de controle total sobre as definições de colunas, incluindo comentários:

from odps.models import Schema, Column, Partition

columns = [
    Column(name='num', type='bigint', comment='the column'),
    Column(name='num2', type='double', comment='the column2'),
]
partitions = [Partition(name='pt', type='string', comment='the partition')]
schema = Schema(columns=columns, partitions=partitions)

Acesse as propriedades do esquema:

# All columns including partition columns
print(schema.columns)

# Partition columns only
print(schema.partitions)

# Non-partition column names
print(schema.names)

# Non-partition column types
print(schema.types)

Método 2: Schema.from_lists()

O Schema.from_lists() é mais simples, mas não suporta comentários de coluna:

from odps.models import Schema

schema = Schema.from_lists(
    ['num', 'num2'],           # Column names
    ['bigint', 'double'],      # Column types
    ['pt'],                    # Partition names
    ['string']                 # Partition types
)
print(schema.columns)

Crie uma tabela

A partir de um objeto de esquema

from odps.models import Schema

schema = Schema.from_lists(['num', 'num2'], ['bigint', 'double'], ['pt'], ['string'])

# Create a table
table = o.create_table('my_new_table', schema)

# Skip creation if the table already exists
table = o.create_table('my_new_table', schema, if_not_exists=True)

# Set the lifecycle (days before auto-deletion)
table = o.create_table('my_new_table', schema, lifecycle=7)

A partir de definições de colunas como strings

# Partitioned table (columns, partition columns)
table = o.create_table('my_new_table', ('num bigint, num2 double', 'pt string'), if_not_exists=True)

# Non-partitioned table
table = o.create_table('my_new_table02', 'num bigint, num2 double', if_not_exists=True)

Ative tipos de dados estendidos

Por padrão, há suporte apenas para os seguintes tipos de dados: BIGINT, DOUBLE, DECIMAL, STRING, DATETIME, BOOLEAN, MAP e ARRAY.

Para usar tipos estendidos como TINYINT e STRUCT, ative a extensão de tipos de dados do MaxCompute V2.0:

from odps import options

options.sql.use_odps2_extension = True
table = o.create_table('my_new_table', 'cat smallint, content struct<title:varchar(100), body:string>')

Sincronizar atualizações de tabela

Quando outro programa modificar uma tabela, chame reload() para atualizar o objeto local com os metadados mais recentes do servidor:

from odps.models import Schema

schema = Schema.from_lists(['num', 'num2'], ['bigint', 'double'], ['pt'], ['string'])
table = o.create_table('my_new_table', schema)

# Fetch the latest table metadata from the server
table.reload()

Gravar dados em uma tabela

write_table()

Use write_table() para gravações simples e pontuais, quando todos os registros já estiverem prontos. O método acrescenta dados à tabela e lida com partições em uma única chamada.

records = [
    [111, 1.0],
    [222, 2.0],
    [333, 3.0],
    [444, 4.0]
]

# Write to a partition; create the partition if it does not exist
o.write_table('my_new_table', records, partition='pt=test', create_partition=True)
Importante

Cada chamada para write_table() cria um arquivo no servidor. Essa operação é demorada, e o excesso de arquivos pequenos degrada o desempenho da consulta. Grave vários registros por chamada ou passe um objeto gerador.

O write_table() sempre acrescenta dados. Para substituir os dados existentes:

  • Tabelas não particionadas: Chame table.truncate().

  • Tabelas particionadas: Exclua e recrie a partição.

open_writer()

Use open_writer() para gravações em streaming ou para gravar registros incrementalmente dentro de uma sessão gerenciada.

t = o.get_table('my_new_table')

with t.open_writer(partition='pt=test02', create_partition=True) as writer:
    records = [
        [1, 1.0],
        [2, 2.0],
        [3, 3.0],
        [4, 4.0]
    ]
    writer.write(records)  # Accepts any iterable

Grave em partições de vários níveis:

t = o.get_table('test_table')

with t.open_writer(partition='pt1=test1,pt2=test2') as writer:
    records = [
        t.new_record([111, 'aaa', True]),
        t.new_record([222, 'bbb', False]),
        t.new_record([333, 'ccc', True]),
        t.new_record([444, 'Chinese', False])
    ]
    writer.write(records)

Gravação paralela com múltiplos processos

Vários processos podem gravar na mesma tabela simultaneamente ao compartilhar um ID de sessão e gravar em blocos separados. Cada bloco corresponde a um arquivo no servidor. O processo principal executa o commit após a conclusão de todos os workers.

import random
from multiprocessing import Pool
from odps.tunnel import TableTunnel

def write_records(tunnel, table, session_id, block_id):
    # Reuse the existing session
    local_session = tunnel.create_upload_session(table.name, upload_id=session_id)
    # Write to this process's block
    with local_session.open_record_writer(block_id) as writer:
        for i in range(5):
            record = table.new_record([random.randint(1, 100), random.random()])
            writer.write(record)

if __name__ == '__main__':
    N_WORKERS = 3

    table = o.create_table('my_new_table', 'num bigint, num2 double', if_not_exists=True)
    tunnel = TableTunnel(o)
    upload_session = tunnel.create_upload_session(table.name)

    # Share the session ID across processes
    session_id = upload_session.id

    pool = Pool(processes=N_WORKERS)
    futures = []
    block_ids = []
    for i in range(N_WORKERS):
        futures.append(pool.apply_async(write_records, (tunnel, table, session_id, i)))
        block_ids.append(i)
    [f.get() for f in futures]

    # Commit all blocks
    upload_session.commit(block_ids)

Ler dados de uma tabela

read_table()

Use read_table() para percorrer todos os registros em uma tabela ou partição:

for record in o.read_table('my_new_table', partition='pt=test'):
    print(record)

head()

Visualize até 10.000 registros sem varrer a tabela inteira:

t = o.get_table('my_new_table')

for record in t.head(3):
    print(record)

open_reader()

Use open_reader() quando precisar de acesso por fatias ou da contagem de registros antes da leitura. Ele expõe o atributo count e suporta fatiamento por índice:

t = o.get_table('my_new_table')

with t.open_reader(partition='pt=test') as reader:
    count = reader.count
    for record in reader[5:10]:  # Read a slice of records
        print(record)

Sem o bloco with:

reader = t.open_reader(partition='pt=test')
count = reader.count
for record in reader[5:10]:
    print(record)

Exclua uma tabela

# Delete only if the table exists
o.delete_table('my_table_name', if_exists=True)

# Or call drop() on a table object
t.drop()

Converter uma tabela em um DataFrame

O to_df() converte uma tabela em um DataFrame do PyODPS. Para mais detalhes, consulte DataFrame (Deprecated).

table = o.get_table('my_table_name')
df = table.to_df()

Gerencie partições

Verificar se uma tabela é particionada

table = o.get_table('my_new_table')
if table.schema.partitions:
    print('Table %s is partitioned.' % table.name)

Iterar pelas partições

table = o.get_table('my_new_table')

# All partitions
for partition in table.partitions:
    print(partition.name)

# Sub-partitions under pt=test
for partition in table.iterate_partitions(spec='pt=test'):
    print(partition.name)

# Partitions matching a condition (PyODPS 0.11.3 and later)
for partition in table.iterate_partitions(spec='dt>20230119'):
    print(partition.name)

A partir do PyODPS 0.11.3, o iterate_partitions() aceita expressões lógicas como dt>20230119.

Verificar se uma partição existe

table = o.get_table('my_new_table')
table.exist_partition('pt=test,sub=2015')

Obter informações da partição

table = o.get_table('my_new_table')
partition = table.get_partition('pt=test')
print(partition.creation_time)
print(partition.size)

Crie uma partição

t = o.get_table('my_new_table')
t.create_partition('pt=test', if_not_exists=True)

Exclua uma partição

t = o.get_table('my_new_table')
t.delete_partition('pt=test', if_exists=True)

# Or call drop() on a partition object
partition.drop()

Registros e mapeamentos de tipos de dados

Um registro representa uma única linha em uma tabela do MaxCompute. Os quatro métodos de E/S — open_reader(), open_writer(), open_record_reader() e open_record_writer() — utilizam registros.

Crie um registro chamando new_record() em um objeto de tabela.

Considere o seguinte esquema de tabela:

odps.Schema {
  c_int_a                 bigint
  c_string_a              string
  c_bool_a                boolean
  c_datetime_a            datetime
  c_array_a               array<string>
  c_map_a                 map<bigint,string>
  c_struct_a              struct<a:bigint,b:string>
}

Crie e manipule registros:

import datetime

t = o.get_table('mytable')  # o is the MaxCompute entry object

# Create a record with initial values
# The number of values must match the number of fields in the schema
r = t.new_record([1024, 'val1', False, datetime.datetime.now(), None, None])

# Create an empty record
r2 = t.new_record()

# Set values by index
r2[0] = 1024

# Set values by field name
r2['c_string_a'] = 'val1'

# Set values by attribute
r2.c_string_a = 'val1'

# Set ARRAY value
r2.c_array_a = ['val1', 'val2']

# Set MAP value
r2.c_map_a = {1: 'val1'}

# Set STRUCT value (PyODPS 0.11.5 and later)
r2.c_struct_a = (1, 'val1')             # tuple
r2.c_struct_a = {"a": 1, "b": 'val1'}   # dict

# Get values
print(r[0])                          # By index
print(r['c_string_a'])               # By field name
print(r.c_string_a)                  # By attribute
print(r[0: 3])                       # Slice
print(r[0, 2, 3])                    # Multiple indices
print(r['c_int_a', 'c_double_a'])    # Multiple field names

Mapeamentos de tipos de dados

Tipo do MaxCompute

Tipo do Python

TINYINT, SMALLINT, INT, BIGINT

int

FLOAT, DOUBLE

float

STRING

str

BINARY

bytes

DATETIME

datetime.datetime

DATE

datetime.date

BOOLEAN

bool

DECIMAL

decimal.Decimal

MAP

dict

ARRAY

list

STRUCT

tuple / namedtuple

TIMESTAMP

pandas.Timestamp

TIMESTAMP_NTZ

pandas.Timestamp

INTERVAL_DAY_TIME

pandas.Timedelta

Tratamento de STRING

Por padrão, o tipo STRING é mapeado para strings Unicode (str no Python 3, unicode no Python 2). Para armazenar dados binários em uma coluna STRING, defina options.tunnel.string_as_binary = True.

Comportamento do fuso horário

O PyODPS usa o fuso horário local por padrão. O MaxCompute não armazena valores de fuso horário — ele converte valores datetime em carimbos de data e hora UNIX para armazenamento.

Para usar o UTC:

options.local_timezone = False

Para usar um fuso horário específico:

options.local_timezone = 'Asia/Shanghai'

DECIMAL no Python 2

Quando o pacote cdecimal está instalado, o PyODPS usa cdecimal.Decimal em vez de decimal.Decimal no Python 2.

Comportamento do tipo STRUCT

Antes do PyODPS 0.11.5, o tipo STRUCT era mapeado para dict. A partir do PyODPS 0.11.5, o tipo STRUCT é mapeado para namedtuple por padrão.

Para restaurar o comportamento anterior de dict:

options.struct_as_dict = True
Em ambientes do DataWorks, struct_as_dict tem False como padrão para compatibilidade histórica. O PyODPS 0.11.5 e versões posteriores aceitam tanto dict quanto tuple para valores STRUCT. Versões anteriores aceitam apenas dict.

MaxCompute Tunnel

O MaxCompute Tunnel é o canal de dados de baixo nível para uploads e downloads em massa. Para a maioria dos casos de uso, write_table() e read_table() são mais simples. Use o Tunnel quando precisar de gravações multiprocesso ou controle granular de sessão.

O PyODPS não suporta o upload de dados por meio de tabelas externas (por exemplo, tabelas baseadas em OSS ou Tablestore).

Se o CPython estiver instalado, o PyODPS compila código C durante a instalação para acelerar as transferências baseadas no Tunnel.

Fazer upload de dados

from odps.tunnel import TableTunnel

table = o.get_table('my_table')

tunnel = TableTunnel(o)
upload_session = tunnel.create_upload_session(table.name, partition_spec='pt=test')

with upload_session.open_record_writer(0) as writer:
    record = table.new_record()
    record[0] = 'test1'
    record[1] = 'id1'
    writer.write(record)

    record = table.new_record(['test2', 'id2'])
    writer.write(record)

# Commit outside the with block. Committing before all data is written causes an error.
upload_session.commit([0])

Baixe dados

from odps.tunnel import TableTunnel

tunnel = TableTunnel(o)
download_session = tunnel.create_download_session('my_table', partition_spec='pt=test')

with download_session.open_record_reader(0, download_session.count) as reader:
    for record in reader:
        print(record)