Pandas read_sql with pyodbc SQL Server returns partial results

When I run even simple queries via pd.read_sql to a SQL Server instance using pyodbc, I keep getting partial results with no errors.

python: 3.8.2
pandas: 1.0.3
pyodbc: 4.0.30

conn = pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};'

dt = pd.read_sql("SELECT * from db.table", conn)

The above returns some results -- say, rows with 'field1' 0-201 -- but field1 values go up to 2000 (with breaks, so not all values between 0-2000 are represented):

dt = pd.read_sql("SELECT * from db.table WHERE field1 > 250", conn)

Calling the above to specifically grab some of the missing rows, it returns an empty dataframe. If I run the same query via e.g. DataGrip, the full result set is returned, so the issue isn't the SQL.

I don't get any errors, and until recently I was able to interact fine with the server via pymssql through my python code. It just seems that the query via read_sql/pyodbc is missing some of the data that is in the table. I'm at a total loss for what's going on, or how to generate more information to debug.


cursor = conn.cursor()
cursor.execute('SELECT * FROM db.table')
for row in cursor.fetchall():

Fetches the same set of rows from the table then stops. Again, no errors, no indication that anything went wrong, just less than the expected number of records returned.

The table is extremely simple, and could be reproduced via e.g.:

create table db.test_table
    field1    int     not null,
    field2    char(2) not null,
    field3    int     not null,
primary key (field1, field2)

And I've checked that the data in the table is as expected -- again, the only strangeness is via pyodbc with the SQL driver. The table behaves perfectly normally when interacting via e.g. DataGrip.

One odd detail is that the max field1 value any permutation of my selection query will return is 201. The next highest value in the DB is 300. So

SELECT * from db.table where field1 >= 201

returns only the rows where field1 = 201. But without the WHERE, or with a lower comparison value, all rows up to the ones where field1 = 201 are returned.

So basically, when querying through ODBC with the SQL server driver, the table seems effectively truncated to rows where field1 <= 201.