Skip to content

Iterating a cursor or calling fetchone() in a loop is 100x+ slower than fetchall() (affects SQLAlchemy) #981

Open

Description

@maharanay22

Iterating a cursor (for row in cursor) or calling fetchone() in a loop costs about 0.6 to 0.8 ms per row, more than 100x slower than fetchall(). SQLAlchemy's default Result iteration calls cursor.fetchone() once per row, so a 1M-row read through SQLAlchemy takes many minutes instead of seconds

Example (20,000 rows of bigint + string from an Arrow result, connector main at 61b9a7f):

fetchall for row in cursor fetchone loop
pandas 3.0.6 0.07s 16.1s 11.6s
pandas 2.3.3 0.06s 11.9s 14.8s

Cause: ResultSet.__iter__ calls fetchone(), and every backend's fetchone() (Thrift, SEA and kernel) fetches a single row and converts it on its own. On the Arrow path that goes through _convert_arrow_table, which builds a pandas DataFrame for each row

The change is larger than 100 lines because the three backends each had their own fetchone, so I'm opening this issue alongside the fix. I have a fix with tests ready and am opening the PR now. It converts rows arraysize at a time and serves fetchone() from a small buffer that every other fetch method drains first, so results and ordering stay the same. With it, the same 20,000 rows take about 0.1s for both iteration and the fetchone() loop

Activity

  1. added
    enhancementNew feature or request
    engineer-botMaintainer-applied gate: triggers engineer-bot (bug-fix on issue / take-over on PR).
    on Oct 29, 2026
  2. peco-engineer-bot commented on Oct 29, 2026

    @peco-engineer-bot
    Contributor

    🤖 engineer-bot opened a fix PR: #1007

    Review before merge.

  3. maharanay22 commented on Oct 30, 2026

    @maharanay22
    Author

    Thanks! There's already a fix for this in #982, open since Oct 16, rebased on 4.8.0 with all unit tests passing. Happy to fold anything useful from #1007 into it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    engineer-botMaintainer-applied gate: triggers engineer-bot (bug-fix on issue / take-over on PR).enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions