Why Your AI's Search Misses Rows That Clearly Match
Asked for customers with status 'Active' and it missed rows spelled 'active'. Whether that filter matches depends on which database you're connected to — Postgres string comparison is case-sensitive by default, MySQL usually isn't, SQL Server depends on collation.
You ask for "all customers with status Active." Some rows with active (lowercase) don't show up. Nothing errored. The query looks completely reasonable. And whether this happens to you at all depends entirely on which database you're connected to — the same filter, word for word, behaves differently across Postgres, MySQL, and SQL Server.
Same SQL, three different outcomes
WHERE status = 'Active' is unambiguous SQL — but "does 'Active' match 'active'" isn't decided by the SQL itself, it's decided by the collation of the column being compared, and collation defaults vary by database:
- PostgreSQL — string comparison with
=orLIKEis case-sensitive by default.'Active' = 'active'is false unless you explicitly use a case-insensitive collation. - MySQL — most common default collations (
utf8mb4_general_ci,utf8mb4_0900_ai_ci— thatcisuffix means case-insensitive) treat'Active'and'active'as equal. A column created with a_csor binary collation is the exception, and behaves like Postgres. - SQL Server — depends on the database or column collation, which on many default installs is case-insensitive, but is a setting, not a guarantee — the same query can behave either way depending on how that specific database was set up.
None of this is visible from the query text. WHERE status = 'Active' reads identically whether it's about to match case-insensitively or not — the actual behavior lives in a collation setting that describe_table doesn't typically surface alongside column names and types.
Why this is easy to miss entirely
If your data happens to be consistently capitalized — every row genuinely says Active, never active — this never shows up, on any database. The bug only appears once the data itself has mixed casing, which tends to happen gradually: an import job, a manual edit, an API that didn't normalize input, a second developer who typed lowercase by habit. At that point, a query that "always worked" starts quietly excluding a growing slice of rows, with the missing rows the only symptom — no error, no warning, just a total that's lower than reality.
How to catch it
If a count or filtered list looks lower than expected and the filter is a text comparison, check for casing variation directly:
SELECT DISTINCT status FROM customers;
If that returns both Active and active as separate values, you've found it — and it's worth deciding whether that variation is itself a data-quality problem worth fixing at the source, separate from the query.
The fix that works everywhere, regardless of collation
Postgres has a dedicated case-insensitive operator for pattern matching:
WHERE status ILIKE 'active'
For exact-match comparisons (not just pattern matching), wrapping both sides in LOWER() works identically across Postgres, MySQL, and SQL Server, regardless of the underlying collation:
WHERE LOWER(status) = LOWER('active')
This is the safer default to ask for specifically when you don't know (or the AI doesn't know) what collation is in play — it removes the ambiguity instead of relying on a setting neither of you inspected.
The prompt that heads this off
When filtering or joining on text columns, don't assume case sensitivity
either way. Use ILIKE (Postgres) or wrap both sides in LOWER() for exact
matches, unless I've confirmed the column's collation is already
case-insensitive. If a count looks lower than expected, check
SELECT DISTINCT on the filtered column for casing variants before
concluding the data itself is smaller than I think.
The short version
The exact same WHERE status = 'Active' is a case-sensitive filter on Postgres and very possibly a case-insensitive one on MySQL — not because either database is doing anything wrong, but because they made different defaults, and nothing in the query text tells you which one you're getting. When a count looks too low, checking for casing variants is worth doing before checking anything else.
Read-only MCP gateway: mcpserver.design.
Related reading: