--- id: a99673ab-2ded-48c5-9563-30df7bf4a465 type: workflow title: "PR review: paper-dynasty-database#44 — batch-fetch PitchingCardRatings N+1 fix" tags: [pr-reviewer, paper-dynasty-database, python, peewee, pandas, performance, n+1-queries, fix] importance: 0.4 confidence: 0.8 created: "2026-03-03T23:17:50.809941+00:00" updated: "2026-03-03T23:17:51.214127+00:00" relations: - target: 0fdd32ea-6b6a-4cd0-aa4b-117184e0c81d type: RELATED_TO direction: outgoing strength: 0.73 edge_id: c81fc052-1f7d-41ec-851b-4185c9b34c4f - target: b9375a89-6e0f-4722-bca7-f1cd655de81a type: RELATED_TO direction: outgoing strength: 0.73 edge_id: 91cf1aa0-b820-4578-aba8-0a5f90df07cb - target: 6abad694-c8aa-4455-9762-0175d6bc77d4 type: RELATED_TO direction: outgoing strength: 0.73 edge_id: 157f3b01-d301-46f6-94ca-9ae632c9046d --- ## PR #44 Review: batch-fetch PitchingCardRatings instead of per-row queries **Verdict**: APPROVED (posted as COMMENT — Gitea blocks self-approval) **Files reviewed**: `app/routers_v2/teams.py` **What changed**: Replaced 2N per-row `PitchingCardRatings.get_or_none()` calls with a single batch `SELECT ... WHERE pitchingcard_id IN (...)` upfront. A `ratings_map` dict keyed by `(pitchingcard_id, vs_hand)` is built once; `get_total_ops` closure does O(1) dict lookups. Applied to both `sort_pitchers()` and `sort_starters()` (inside `get_team_sp`). **Key findings**: - Implementation is correct — key alignment between batch fetch and lookups verified - Empty input edge case safe — existing `return None` guard fires before batch fetch - Pre-existing bug: `vlval`/`vrval` can be None → AttributeError on `.obp`/`.slg`; unchanged by this PR - No security issues — Peewee `<<` uses parameterized IN clause **Note**: Gitea does not allow approving your own PRs. Review posted as COMMENT state instead of APPROVED.