How does SQL compare two character strings with = and <, and what varies by engine?
answer
- the operator is not the whole story
- two identical-looking strings can differ
- something configurable decides the outcome
- case, accents and trailing blanks vary
- the collation decides equality and ordering
basics
~20 sCharacter comparison follows the collation in effect, not numeric value: the collation decides case sensitivity, accent handling and sort order. So '10' < '9' is true because '1' sorts before '9', and whether 'ANNA' = 'anna' holds depends on the collation.
solid answer
~50 sComparing two character strings is a collation-driven operation. A collation is the rule set that says which characters sort before which, whether case and accents matter, and how trailing blanks are treated; it is attached to the column, the literal or the session, and the standard requires the two operands to share a collation for the comparison to be well defined. Two consequences trip people up. First, ordering is character-by-character, not numeric: `'10' < '9'` is true because `'1'` precedes `'9'`, so a numeric identifier stored as text sorts in an order nobody expects. Second, equality is not byte equality — under a case-insensitive collation `'ANNA' = 'anna'` is true, and whether trailing blanks are ignored depends on the type and the collation's padding attribute, so engines can legitimately disagree. Never assume a string comparison behaves the same on two systems; check the collation, and store numbers in numeric columns.
code
sql · 6 linesCREATE TABLE parts (part_no VARCHAR(10));
INSERT INTO parts (part_no) VALUES ('9'), ('10'), ('100');
-- Text ordering, not numeric ordering
SELECT part_no FROM parts WHERE part_no < '9' ORDER BY part_no;
-- returns '10' and '100': '1' sorts before '9'go deeper
Know that text compares character by character, so '10' < '9' is true, and that string equality can be case-insensitive depending on configuration. Expect a result-prediction question on a text column of digits.
Name the collation as the deciding rule set and list what it governs: ordering, case, accents, blank padding. Explain why the same predicate can differ between two servers holding identical data.
Discuss the operational fallout: identifiers stored as text sorting unexpectedly, collation-mismatch errors between columns, and case-insensitive matching that must be a declared requirement rather than an inherited default.
Own collation as a schema-wide decision: one declared collation policy for comparable columns, numeric data in numeric types, normalisation at the boundary, and portability assumptions documented rather than discovered during a migration.
## What a comparison of two strings actually does When SQL evaluates `a = b` or `a < b` for character strings, it does not compare bytes; it compares under a **collation**. A collation is a named rule set covering: - the relative order of characters (which letters and symbols sort before which), - whether case differences matter (case-sensitive vs case-insensitive), - whether accents and other diacritics matter, - how trailing blanks are handled in comparison. The collation comes from the operands: a column has one, a literal has one, and the session or database supplies a default. The standard requires the two operands of a comparison to be coercible to a common collation; if they are not, the comparison is not well defined and engines typically raise an error about mismatched collations. This is why the same predicate can be true on one system and false on another with identical data. The operator is the same; the rule set behind it is not. ## Ordering is lexicographic, not numeric Comparison operators on character strings work position by position under the collation's ordering. That produces the single most reported surprise: ```sql CREATE TABLE parts (part_no VARCHAR(10)); INSERT INTO parts (part_no) VALUES ('9'), ('10'), ('100'); SELECT part_no FROM parts WHERE part_no < '9' ORDER BY part_no; -- '10' and '100': the first character '1' sorts before '9' ``` It is not a bug and no conversion took place — `'10'` really is less than `'9'` as text. The same effect makes version strings, zero-padded codes and numeric identifiers stored as `VARCHAR` sort in an order that looks scrambled. Zero-padding to a fixed width (`'0009'`, `'0010'`) restores numeric-looking order for equal-length values; the real fix is to store numbers in a numeric column. ## Equality is not byte equality Under a case-insensitive collation, `'ANNA' = 'anna'` is true. Under a case-sensitive one it is false. Neither is more correct — it is a property of the collation in effect, and a query that assumes one behaviour is not portable to a system configured the other way. The same applies to accent sensitivity: whether `'resume' = 'résumé'` holds is a collation decision. Trailing blanks are the third variable. The standard describes padding behaviour as a collation attribute (`PAD SPACE` versus `NO PAD`), and fixed-length `CHAR` types are commonly blank-padded on storage while `VARCHAR` is not. The result is that comparing `'abc'` with `'abc '` may be true or false depending on the types and collation involved. Do not encode an assumption about it into application logic; trim on input or compare explicitly trimmed values if the distinction matters. ## Practical consequences - **Uniqueness follows the collation.** If a comparison says `'ANNA'` equals `'anna'`, so does an equality lookup for them. Whether two such values can coexist as distinct entries depends on the same rule set. - **Range filters on text are text ranges.** `WHERE code >= 'A' AND code < 'B'` selects codes starting with `'A'` under most collations, which is a genuinely useful idiom — but only if you have reasoned about the collation's ordering, including how digits, punctuation and case interleave. - **Comparisons across differently-collated columns** may fail outright rather than silently doing something. That error is friendlier than the alternative; read it as a design signal that two columns holding the same kind of value were declared differently. ## Writing portable string predicates 1. Know the collation of the columns you filter on, and state assumptions in comments when a query depends on case behaviour. 2. Do not store numbers as text if you will ever compare them with `<`, `>` or order by them. 3. If case-insensitive matching is a business requirement, make it explicit rather than inherited from a database default — either through a column defined with the appropriate collation or by comparing normalised values consistently on both sides of the predicate. 4. Be careful with trailing blanks at the boundary: normalise on input rather than hoping the comparison ignores them. ## What interviewers listen for The expected answer names the collation as the thing that decides the outcome, produces `'10' < '9'` (or an equivalent example) without hesitation, and states that case sensitivity, accent sensitivity and blank padding are configuration rather than universal SQL behaviour. A candidate who says "string comparison compares bytes and is always case sensitive" is describing one particular configuration and will be surprised by the next database they touch.
- Why does a VARCHAR column of numeric codes sort as '1', '10', '2' rather than '1', '2', '10'?Because the comparison is character by character under the collation, not numeric. `'10'` and `'2'` are compared at the first position, where `'1'` precedes `'2'`, so `'10'` sorts first and the rest of the string is never reached. Zero-padding to a common width restores the expected order for equal-length values; storing the codes in a numeric column fixes it properly.
- What happens when you compare two columns declared with different collations?The comparison has no defined meaning unless the operands can be coerced to a common collation, and engines commonly raise a collation-mismatch error rather than guessing. Treat that error as a schema signal: two columns holding the same kind of value should be declared the same way, rather than patching each query with an explicit collation clause.
saying these in an interview costs you the question
- Says string comparison is always byte-for-byte and case sensitive
- Expects '10' < '9' to be false because ten exceeds nine
- Assumes trailing blanks are always ignored in comparison
- Believes case sensitivity is fixed by the SQL standard
- Stores numeric identifiers as VARCHAR and then filters them with < and >