PAYATHON 2026

DDBDataLoader的where sym in列表超过约130个元素时为何报错?

排障赵师傅

结论

仅凭这条报错,还不能认定 DDBDataLoader 的 in 列表存在最多约 130 项的固定限制。报错表明,创建 sqlDS 时,生成的表达式中出现了 array40159f6800000000,后续解析将它当作列名,却找不到对应的列。

如果训练目标是为每只股票分别构造时间窗口,建议优先用 groupCol="sym" 和 groupScheme 指定股票范围,避免在 SQL 中嵌入长列表。如果需要保留多只股票混合查询的语义,可以尝试上传筛选向量,再通过服务端对象引用进行过滤,但要验证当前版本和执行会话是否支持这种用法。

把列表截断为 100 项只能临时避开错误,还会缩小训练数据范围,不宜长期使用。

为什么会出现 array... 列名

原代码中的:

sql=f"""select ...
where sym in {stock_name}
..."""

会先由 Python 将 stock_name 转成字符串,再传给 DDBDataLoader。也就是说,调用 DDBDataLoader 时,Python list 已经变成了 SQL 中的列表文本,不能据此判断 DataLoader 直接接收并转换了这个 list。

DDBDataLoader 会拆分查询,sqlDS 则根据 SQL 元代码创建数据源。因此,直接执行查询和构造 DataLoader 所经过的处理步骤并不完全相同。DDBDataLoader 工作原理、sqlDS 文档

从报错形式看,问题可能出在长向量参与表达式转换或查询重建的过程中:某个内部对象名称进入了后续 SQL,却没有被正确解析。但仅凭报错,还无法确定具体是哪一步出了问题,也无法确定阈值是 128、130 或其他固定值。要确认根因,需要结合 DolphinDB Server、Python API 和 dolphindb-tools 的具体版本,以及最小复现。

按股票构造窗口时,使用分组参数

groupCol 和 groupScheme 可以将查询划分为不同组,每组代表一只股票的时间序列。官方示例也采用了这种方式,用带反引号的股票代码作为分组值。分组参数与股票训练示例

假设 sym 为 SYMBOL 列,可以将原代码改为:

from dolphindb_tools.dataloader import DDBDataLoader

stock_name = [f"{i:06d}.SZ" for i in range(131)]

base_sql = """
select *
from loadTable(
    'dfs://stock_snapshot_level2',
    'level2_snapshot_zscore_index_component'
)
where trade_date between 2025.12.08:2025.12.26
and TickTime between 09:40:00.000:14:49:59.999
"""

# 每个元素表示一个 DolphinDB SYMBOL 字面量,例如 `000001.SZ
group_scheme = ["`" + sym for sym in stock_name]

train_loader = DDBDataLoader(
    ddbSession=s,
    sql=base_sql,
    targetCol=["label"],
    batchSize=256,
    shuffle=True,
    windowSize=100,
    windowStride=1,
    groupCol="sym",
    groupScheme=group_scheme,
    excludeCol=["sym", "trade_date", "TickTime", "label"],
    device="cuda",
)

股票范围改由 groupScheme 指定,SQL 中不再保留原来的 where sym in ...。

分组后,不同股票之间不会跨组构造窗口。如果模型需要把多只股票放进同一个样本,这种改法就不符合训练语义。如果 sym 的实际类型不同,应按所用版本的要求构造对应的分组表达式。

需要保留 in 筛选时,验证服务端向量方案

Python API 的 Session.upload 可以把本地变量上传到服务端,objByName 可以在执行时按名称取得对象。可以尝试用这种方式让 SQL 引用筛选向量,避免直接嵌入长列表文本。Session.upload 文档、objByName 文档

沿用上面的 stock_name 和 base_sql:

s.upload({"trainSymbols": stock_name})

filter_sql = base_sql + """
and sym in objByName("trainSymbols")
"""

# 先验证当前环境能否创建数据源
data_source_count = s.run(
    "size(sqlDS(<" + filter_sql + ">))"
)
print(data_source_count)

检查通过后,将 filter_sql 传给保留原有参数的 DDBDataLoader,再实际迭代读取一批数据。

使用这一方案,需要满足两个条件:

  • 执行过滤的会话或节点能够访问 trainSymbols。变量上传到 s 后,不能默认其他连接也能访问。
  • 当前版本能够在查询拆分和后续读取中正确处理该对象引用。单独通过 sqlDS 检查,并不代表后台读取也一定成功。

上传向量可以作为一种待验证的替代方案,但不能保证适用于所有版本。训练结束前,不要删除或覆盖正在使用的筛选变量。

如何进一步定位

先保存实际传入的 SQL,再分别检查普通查询和数据源构造是否成功:

# original_sql 是原先包含完整列表的 SQL 字符串
print(original_sql)

# 在可控的数据范围内执行,避免一次下载大量训练数据
s.run(original_sql)

# 单独检查数据源构造
s.run("size(sqlDS(<" + original_sql + ">))")

如果普通查询成功、sqlDS 失败,应重点排查元代码和数据源构造阶段。如果两者都成功,只有 DataLoader 初始化或迭代失败,就继续检查 DataLoader 的查询拆分过程。

提交问题时,请附上组件版本、完整异常,以及阈值附近不同列表长度的复现结果。对于当前的滑动窗口训练,还要确认每只股票的数据按预期的时间顺序进入窗口;仅仅不再报错,还不足以说明结果正确。

备注:内容仅供参考。