feat: --limit 全表扫描支持按真实 ID 排序/升降序/范围过滤

此前 --limit 仅按总排号排序取前 N 个。现扩展为:
- 排序按数据库真实 ID 列(config.id_field),新增 --order {asc,desc}(默认 asc)
- 新增 --range START,END 按真实 ID 闭区间过滤(与 --order/--limit 可组合)
- 上述三参数仅全表扫描生效,与 --id/--sn/--ids-file 互斥

db.py: fetch_all_ids 新增 order/id_min/id_max 参数,用派生表
  (内层 DISTINCT 取 (总排号,ID) 配对,外层按 ID 排序)规避
  SQL Server "SELECT DISTINCT 时 ORDER BY 列须在选择列表" 的限制;
  id_field 未配置时降级按总排号排序并告警。
write_attachments.py: 新增 --order/--range CLI 参数并接线,含 --range
  格式校验(须为 START,END 两个整数);模块 docstring 补示例。
README.md: 写入用法块与项目结构注释补充 --order/--range 说明。

自测(小数据量,只读+部分 dry-run):--limit 3 升/降序、--range 800,805
升序、--range 800,805 --order desc --limit 3 均验证返回总排号按真实 ID
正确排序且在范围内;dry-run 不落库;--range 格式错误正确报错退出。

Co-Authored-By: WorkBuddy <workbuddy@tencent.com>
This commit is contained in:
Misaka_Company
2026-07-24 16:27:16 +08:00
parent fd047e2143
commit 70b6fb3767
3 changed files with 92 additions and 18 deletions

View File

@@ -10,8 +10,11 @@
- has_attachment=null (not_found / llm_error / db_error): 不写(属"无法确定/失败")。
用法:
python write_attachments.py # 全量写入
python write_attachments.py --limit 10 # 仅前 10 个总排号(测试)
python write_attachments.py # 全量写入(按真实 ID 升序)
python write_attachments.py --limit 10 # 仅前 10 个(按真实 ID 升序,测试)
python write_attachments.py --limit 10 --order desc # 按真实 ID 降序取前 10 个
python write_attachments.py --range 800,805 # 真实 ID 在 [800,805] 闭区间内
python write_attachments.py --range 800,805 --order desc --limit 3 # 组合区间内降序取前3
python write_attachments.py --ids-file ids.txt # 指定总排号(文件,每行一个)
python write_attachments.py --sn 26B742 # 单个总排号写入(键类型 sn
python write_attachments.py --sn 26B742,26B743 # 多个总排号(逗号分隔)写入
@@ -126,7 +129,16 @@ def main() -> None:
)
parser.add_argument(
"--limit", type=int, default=None,
help="仅处理前 N 总排号(按总排号排序),测试用;与 --id/--sn/--ids-file 互斥",
help="取排序+范围过滤后的前 N 总排号,测试用;与 --id/--sn/--ids-file 互斥",
)
parser.add_argument(
"--order", type=str, default="asc", choices=["asc", "desc"],
help="全表扫描时的排序方向(按真实 ID 列排序),默认 asc与 --id/--sn/--ids-file 互斥",
)
parser.add_argument(
"--range", type=str, default=None,
help="按真实 ID 列的值域过滤,闭区间,格式 START,END如 800,805"
"与 --order/--limit 可组合;与 --id/--sn/--ids-file 互斥",
)
parser.add_argument(
"--ids-file", type=str, default=None,
@@ -174,12 +186,29 @@ def main() -> None:
n_sn = sum(1 for _, k in ids if k == "sn")
logger.info("从 --id/--sn/--ids-file 读取 %d 个标识符id=%d, sn=%d", len(ids), n_id, n_sn)
else:
sn_list = fetch_all_ids(cfg["database"], limit=args.limit)
# 全表扫描路径:--range/--order/--limit 仅在此生效
if args.range:
parts = [p.strip() for p in args.range.split(",")]
if len(parts) != 2 or not parts[0] or not parts[1]:
print("[错误] --range 需为 START,END 两个数字,如 800,805", file=sys.stderr)
sys.exit(1)
try:
id_min, id_max = int(parts[0]), int(parts[1])
except ValueError:
print("[错误] --range 的 START/END 必须为整数", file=sys.stderr)
sys.exit(1)
else:
id_min = id_max = None
sn_list = fetch_all_ids(
cfg["database"], limit=args.limit, order=args.order,
id_min=id_min, id_max=id_max,
)
# 全表扫描得到的是总排号,键类型统一为 sn
ids = [(s, "sn") for s in sn_list]
logger.info(
"源表读取 %d 个总排号%s", len(ids),
f"limit={args.limit}" if args.limit else "",
"源表读取 %d 个总排号order=%s%s%s", len(ids), args.order,
f", range=[{id_min},{id_max}]" if args.range else "",
f", limit={args.limit}" if args.limit else "",
)
if not ids: