Skip to content

Instantly share code, notes, and snippets.

@brianr
Created December 4, 2012 19:59
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brianr/4208061 to your computer and use it in GitHub Desktop.
Save brianr/4208061 to your computer and use it in GitHub Desktop.
WTF of the day: MySQL integer<->string comparison gotcha

To implement Ratchet.io's Person Tracking feature, we have a MySQL table like this:

create table person (
  id int unsigned not null,
  project_id int unsigned not null
  environment varchar(255) not null,
  person_id varchar(40) not null,
  username varchar(255),
  email varchar(255),
  primary key (id),
  unique (project_id, environment, person_id)
) engine=innodb default charset=utf8;

I was getting some grief today from the following SQLAlchemy query:

person = model.Person.query().filter_by(project_id=project_id, environment=environment, 
    person_id=person_id).one()

It's filtering on all of the columns in the unique key, so it should always return just one row, right? As it turns out, not necessarily:

mysql> select * from person where project_id=1 and person_id=1 and environment='production';
+--------+------------+-------------+-----------+------------+----------------------+
| id     | project_id | environment | person_id | username   | email                |
+--------+------------+-------------+-----------+------------+----------------------+
|      2 |          1 | production  | 1         | brianr     | brianrue@gmail.com   |
| 542982 |          1 | production  | 1test     | brianrtest | brian+test@gmail.com |
+--------+------------+-------------+-----------+------------+----------------------+

The bug here is that in my query I have the person_id as an int, but the column type is varchar, so mysql converts the varchar to an int before doing the comparison. And apparently the string '1test' converts to the integer 1.

The fix: cast to a string. Works as expected:

mysql> select * from person where project_id=1 and person_id='1' and environment='production';
+----+------------+-------------+-----------+----------+--------------------+
| id | project_id | environment | person_id | username | email              |
+----+------------+-------------+-----------+----------+--------------------+
|  2 |          1 | production  | 1         | brianr   | brianrue@gmail.com |
+----+------------+-------------+-----------+----------+--------------------+
1 row in set (0.00 sec)

And in python:

person = model.Person.query().filter_by(project_id=project_id, environment=environment, 
    person_id=str(person_id)).one()
@emacinty
Copy link

Thanks for writing this up and confirming that (today) I am not mad; fell over the same problem in a query which was entirely wrong, but still returned results. Phew...

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