> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chaintable.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Python SDK 快速上手

> BlockDB-py 客户端安装、核心 CookBook 与高阶调优指南

***

本指南旨在帮助数据组同学在 3 分钟内掌握 `blockdb-py` SDK 的核心使用方法。通过本 SDK，你可以直接在 Notebook 或本地脚本中以纯 Python 原生对象操作 BlockDB，**平台全域只暴露 SDK API，不暴露原始 SQL。**

***

## 3 分钟极速热身

在 Chaintable 中，无论是普通在线表（L1）还是区块链特化表（L2），其核心开发范式均为：**「通过构造器传入表名创建实例 -> 调用语义化方法」**。所有查询方法均返回标准的 Python `dict` 或 `list[dict]`，可直接无缝喂给 `pandas.DataFrame`。

```python theme={null}
from blockdb import Table

# 1) 构造一个指向具体 table_id 的 L1（行级）普通在线表实例
events = Table("demo.event_log")

# 2) 按业务主键快速读取单行；记录不存在时返回 None
row = events.get_row("evt_001")
print(f"Row data: {row}")

# 3) 批量读取多个主键（生产环境强烈推荐，单次 RPC 效率极高）
rows = events.batch_get_rows(["evt_001", "evt_002", "evt_003"])

```

***

## 核心数据业务场景 Cookbook

### 场景 1 — 全表扫描与大规模条件检索 (L1 表)

日常分析中，当你需要从一张大规模 L1 在线表中捞取特定条件的数据时，SDK 提供了两套依据数据量级分流的精准"姿势"：

```python theme={null}
from blockdb import Table
events = Table("demo.event_log")

# 姿势 A：条件简单、结果集行数可控（≤ 10万行） -> 一次性拿回 list
hits = events.filter_rows(
    filters="created_at >= 1700000000 AND name IN ('login', 'signup')",
    order_by="created_at asc",
    limit=200,
)

# 姿势 B：结果集极其庞大（百万/千万级） -> 流式逐行 yield，内存保持恒定 O(1)
# 警告：不要用 list(events.scan_iter(...)) 强转，否则会导致内存撑爆（OOM）
for row in events.scan_iter("SELECT * FROM demo.event_log WHERE name = 'login'"):
    # row 为单行 dict，可在此处进行增量过滤或写出
    pass

```

* **最佳实践**：大表（>10万行）检索必须走 `scan_iter`。另外，传入的 SQL 字符串\*\*结尾绝不能带有分号 `;**`，系统不支持多条 SQL 拼接执行。

### 场景 2 — 物理时序状态查询与级联写入 (TimeTable)

针对随物理挂钟时间（而非区块高度）动态变化的指标（如：代币分钟喂价、每分钟 TVL 快照），BlockDB 提供了特化的 `TimeTable`，其**时间桶固定为 60 秒**。

#### 核心特性：向后取整，秒归零

不管你传入的业务时间是 `17:04:35` 还是 `17:04:59`，SDK 内部在读写时都会自动将其进位并归到 `17:05:00` 这个固定的分钟桶。这确保了分布式计算下，所有端到端的读写口径天然对齐。

```python theme={null}
from blockdb import TimeTable
from blockdb.time_table import align_time_at

t = TimeTable("analytics.token_price_1m")

# 1) 写：往特定时间桶批量 Upsert（传 17:04:35 会自动进位到 17:05:00）
job_id = t.upsert_time_rows(
    "2026-05-18 17:04:35",
    [{"id": "0xabc...", "value": 123.456}],
    sync=True
)

# 2) 读：计算场景下，强烈建议使用 L2 的 get_value() 获取具备分钟对齐、历史回溯能力的状态
# 避坑：直接去查 L1 物理表只能拿到最新瞬时值，无法享受时间桶和历史版本压缩的红利
row = t.get_value("0xabc...", "2026-05-18 17:04:40") 
# 返回 -> {"id": "0xabc...", "value": 123.456, "time_at": "2026-05-18 17:05:00"}

```

* **手动回填后门**：如果需要补录大量的历史时序数据，可以通过 L1 普通表的 `upsert_rows` 接口直接灌入物理底层，TimeTable 的后台引擎会自动触发异步计算，将历史快照重算并刷新到 `latest` 视图中。

***

## 高阶参数与性能调优白皮书

### 1. 写入权衡：`sync=True` vs `sync=False`

所有写入方法（`upsert_rows`, `delete_rows` 等）均支持 `sync` 参数控制行为：

* `sync=True`（默认）：等待 BlockDB 服务端物理落盘并确认后再返回。**慢，但具备强一致性。** 适用于单测、交互式分析、或下一步紧接着要读取该结果的依赖任务。
* `sync=False`：Fire-and-forget（触发即忘），提交完立刻拿 `job_id` 返回，由后台异步处理。**极快，吞吐量爆炸。** 适用于大规模历史区块清洗和百万级批回填。

### 2. 读写黄金定律

* **杜绝 For 循环点查**：需要查询 $N$ 个主键时，\*\*永远使用 `batch_get_rows([...])**`，严禁在 `for` 循环中重复调用 `get_row()`。一次批量 RPC 与 $N$ 次独立 RPC 的时延差距可达数个数量级。

***

## 沙箱 Debug 模式（写入意图预演）

为了防止处于联调期的实验脚本往生产环境脏写数据，SDK 内置了只影响写入操作的 `Debug` 熔断开关。

### 如何开启

可以通过配置系统环境变量（推荐，支持 GitOps 统一管控）或在代码运行时强行切换：

```bash theme={null}
export BLOCKDB_DEBUG=1  # 1 / true / yes / on 均可触发开启

```

```python theme={null}
from blockdb._grpc_client import debug
debug.set_debug(True)  # 运行时强制开启

```

### 行为特征

开启后，任何写入函数均不会对 BlockDB 的实际物理数据产生任何修改。

* 读操作（如 `get_row`, `scan`）照常执行，读取真实生产数据。
* 所有写入操作全部被拦截，并固定返回 `job_id = "debug-job"`。
* 写入的真实内容（操作表名、行数、前 10 行样例）会被序列化为标准 JSON，转发给平台的日志展示系统。**允许你在正式跑数前，肉眼自检"我究竟会往数据库里写进什么"。**
