修复积分榜入库 UniqueViolationError
根因:ingest_bzzoiro_standings 批量处理时逐行 SELECT 检查 Standing 是否存在, 但新 INSERT 的 Standing 未 flush 就被同批下一条 SELECT 判定为不存在, 导致同一 (league_id, season, team_id) 插入两次 → UniqueViolation。 修复: - 预加载该 league+season 的所有已有 standings team_id 到内存 set - 同批内按 team.id 去重,同一 team 仅处理一次 - 命中已有 standing 时 UPDATE,否则 INSERT 并立即加入 set 防止同批重复 Co-Authored-By: new-provider/LongCat-2.0 <<EMAIL>>
This commit is contained in:
co-authored by
new-provider/LongCat-2.0 <
parent
19f17145cc
commit
acac856b61
+28
-12
@@ -417,7 +417,16 @@ async def ingest_bzzoiro_standings(db, *, leagues: Iterable[str], season: str |
|
|||||||
for t in (await db.execute(stmt)).scalars():
|
for t in (await db.execute(stmt)).scalars():
|
||||||
team_map[t.name] = t
|
team_map[t.name] = t
|
||||||
|
|
||||||
|
# 预加载该 league+season 已有的 standings(避免同批内重复 INSERT 导致 UniqueViolation)
|
||||||
|
existing_standings: set[int] = set()
|
||||||
|
stmt = select(Standing.team_id).where(
|
||||||
|
Standing.league_id == league.id, Standing.season == season_label,
|
||||||
|
)
|
||||||
|
for (tid,) in (await db.execute(stmt)).all():
|
||||||
|
existing_standings.add(tid)
|
||||||
|
|
||||||
now = datetime.now(timezone.utc)
|
now = datetime.now(timezone.utc)
|
||||||
|
seen_teams: set[int] = set() # 同批内去重:同一 team 只处理一次
|
||||||
for r in rows:
|
for r in rows:
|
||||||
team_name = normalize_name(str(r.get("team_name", "")))
|
team_name = normalize_name(str(r.get("team_name", "")))
|
||||||
if not team_name:
|
if not team_name:
|
||||||
@@ -430,6 +439,11 @@ async def ingest_bzzoiro_standings(db, *, leagues: Iterable[str], season: str |
|
|||||||
team_map[team_name] = team
|
team_map[team_name] = team
|
||||||
league_r["teams_created"] += 1
|
league_r["teams_created"] += 1
|
||||||
|
|
||||||
|
# 同批内同一 team 仅处理第一次
|
||||||
|
if team.id in seen_teams:
|
||||||
|
continue
|
||||||
|
seen_teams.add(team.id)
|
||||||
|
|
||||||
zone = r.get("zone") or {}
|
zone = r.get("zone") or {}
|
||||||
values = dict(
|
values = dict(
|
||||||
position=_to_int_or_none(r.get("position")) or 0,
|
position=_to_int_or_none(r.get("position")) or 0,
|
||||||
@@ -449,20 +463,22 @@ async def ingest_bzzoiro_standings(db, *, leagues: Iterable[str], season: str |
|
|||||||
retrieved_at=now,
|
retrieved_at=now,
|
||||||
)
|
)
|
||||||
|
|
||||||
stmt = select(Standing).where(
|
if team.id in existing_standings:
|
||||||
Standing.league_id == league.id,
|
# 已有 → 仍需 UPDATE:回查对象(少量,可接受)
|
||||||
Standing.season == season_label,
|
stmt = select(Standing).where(
|
||||||
Standing.team_id == team.id,
|
Standing.league_id == league.id,
|
||||||
)
|
Standing.season == season_label,
|
||||||
standing = (await db.execute(stmt)).scalar_one_or_none()
|
Standing.team_id == team.id,
|
||||||
if standing is None:
|
|
||||||
standing = Standing(
|
|
||||||
league_id=league.id, season=season_label, team_id=team.id, **values
|
|
||||||
)
|
)
|
||||||
db.add(standing)
|
standing = (await db.execute(stmt)).scalar_one_or_none()
|
||||||
|
if standing:
|
||||||
|
for k, v in values.items():
|
||||||
|
setattr(standing, k, v)
|
||||||
else:
|
else:
|
||||||
for k, v in values.items():
|
db.add(Standing(
|
||||||
setattr(standing, k, v)
|
league_id=league.id, season=season_label, team_id=team.id, **values,
|
||||||
|
))
|
||||||
|
existing_standings.add(team.id) # 防止同批内重复
|
||||||
league_r["upserted"] += 1
|
league_r["upserted"] += 1
|
||||||
|
|
||||||
league_r["rows"] = len(rows)
|
league_r["rows"] = len(rows)
|
||||||
|
|||||||
Reference in New Issue
Block a user