How to convert database column values to lowercase before import
- Step 1Export the source data as CSV — From your legacy system, spreadsheet, or previous database (
psql \copy,mysqldump, SQLite.mode csv)..csv/.tsv/.txtload directly;.xlsx/.xls/.odsfrom the first sheet. - Step 2Drop the file onto the converter — The header row becomes a column-checkbox list. Identify the enum-like text columns you intend to query case-insensitively.
- Step 3Tick the enum / status / category columns — Check
status,type,category,country_code, etc. Leave primary-key IDs, timestamps, money, and free-text comment columns unticked unless you specifically want them folded. - Step 4Choose lowercase — Select the lowercase radio (preview
john smith). Every data cell in the ticked columns is lowercased. - Step 5Convert and confirm — Click Convert case. The Cells converted count tells you how many values were not already lowercase; the preview verifies the columns.
- Step 6Bulk-load the normalised file — Download the
.lower-case.csvand run yourCOPY,LOAD DATA INFILE, or.import. Add aCHECK (status = lower(status))constraint if you want to enforce it going forward.
Why lowercasing before import matters per database
Default case behaviour of common databases and the query that breaks without normalisation. Behaviour reflects documented defaults; collation can change it.
| Database | Default text comparison | Breaks without lowercasing |
|---|---|---|
| PostgreSQL | Case-sensitive (= is exact) | WHERE status = 'active' misses Active; GROUP BY status splits buckets |
MySQL *_ci collation | Case-insensitive compare | Compare matches, but GROUP BY / DISTINCT may still show the stored casing inconsistently in output |
MySQL *_bin collation | Case-sensitive (binary) | Same as PostgreSQL — exact-match queries miss case variants |
| SQLite | Case-sensitive (ASCII NOCASE only on opt-in) | = misses case variants unless the column/index uses COLLATE NOCASE |
| SQL Server | Depends on DB collation (often *_CI_*) | On a *_CS_* (case-sensitive) collation, exact-match queries miss variants |
What lowercase does — and does not — touch
The pass affects only the data cells of selected columns, letters only.
| Cell content | After lowercase | Behaviour |
|---|---|---|
Active | active | Letters folded |
PENDING_REVIEW | pending_review | Underscore preserved, letters folded |
123 | 123 | Numbers unaffected |
\N (Postgres NULL marker) | \n | Caution: the backslash stays but N lowercases — keep NULL columns UNticked |
| `` (empty string) | | Empty stays empty |
Active (padded) | active | Spaces NOT trimmed — chain a trim if needed |
Cookbook
Before/after rows from real database exports, showing enum columns lowercased while IDs and timestamps are left alone.
Status enum normalised so PostgreSQL queries match
ExampleThe legacy system stored status in three casings. After import into case-sensitive PostgreSQL, WHERE status = 'active' only finds a third of the rows. Lowercasing the column first fixes every query.
Input: id,status,created_at 1,Active,2026-01-02 2,active,2026-01-03 3,ACTIVE,2026-01-04 Convert case → lowercase, column: status (id/created_at unticked) Output (.lower-case.csv): id,status,created_at 1,active,2026-01-02 2,active,2026-01-03 3,active,2026-01-04
Lowercase status + type, leave the ID and timestamp
ExampleTwo enum columns need folding; the numeric ID and ISO timestamp must stay exactly as exported. Tick only the two text enums.
Input: id,status,type,updated_at 42,Open,BUG,2026-05-01T09:00:00Z Convert case → lowercase, columns: status + type Output: id,status,type,updated_at 42,open,bug,2026-05-01T09:00:00Z
Keep the NULL column unticked to protect the marker
ExamplePostgreSQL text-format COPY writes \N for NULL. Lowercasing that column would turn \N into \n and corrupt the NULL marker, so leave NULL-bearing columns out of the selection.
Input (psql text-format COPY): id,status,note 1,ACTIVE,\N Convert case → lowercase, column: status ONLY (note unticked) Output (NULL marker safe): id,status,note 1,active,\N
Header row preserved for the COPY column list
ExampleLowercasing skips the header, so the column names your COPY / LOAD DATA statement references stay intact even after the values are folded.
Input: ID,Status 1,Active Convert case → lowercase, column: Status Output (headers ID, Status unchanged): ID,Status 1,active
Whitespace survives — trim before loading
ExampleA CHAR(N) column from PostgreSQL pads values with trailing spaces. Lowercase folds the case but keeps the padding, so a follow-up trim is needed for clean enum values.
Input (CHAR(10) status, space-padded): status "ACTIVE " Convert case → lowercase: status "active " ← still padded Then csv-whitespace-trimmer: status active
Errors and edge cases
Real errors and silent failures sourced from each platform's own documentation. Match the wording to the row, fix what the row says to fix.
Lowercasing a NULL-marker column corrupts `\N`
Data corruption riskPostgreSQL text-format COPY uses \N for NULL. Lowercase turns it into \n because the N is a letter, which your importer will read as a literal string, not NULL. Keep columns that contain \N UNticked, or convert the markers with csv-find-replace first.
Whitespace padding from CHAR(N) columns survives
PreservedLowercase changes case only; it does not trim. A space-padded CHAR(10) value stays padded, so 'active ' != 'active' in a later comparison. Run csv-whitespace-trimmer before the load.
Lowercasing a case-significant value column
AvoidSome columns are legitimately case-sensitive — base64 tokens, password hashes, JWT segments, case-significant codes. Lowercasing them destroys the value. Tick only enum/category/status columns where case carries no meaning, and leave token/hash columns unticked.
Primary-key or foreign-key columns ticked by mistake
Avoid for some keysNumeric IDs are unaffected (no letters), but UUID and slug keys are case-significant in some schemas — folding them can break joins if the referenced table is not also folded. Lowercase keys only when both sides of the relationship are normalised together.
Empty string vs NULL after lowercasing
PreservedAn empty cell stays empty; lowercase does not convert empty string to NULL or vice versa. If your import needs empty-to-NULL coercion, that is a separate step — use csv-find-replace or the database's NULLIF/NULL AS option.
No column ticked lowercases the entire dataset
By designLeaving all checkboxes unchecked applies lowercase to ALL columns — including IDs, timestamps, and free text. For database prep you almost always want a specific subset ticked. Confirm the selection first.
Accented enum values
SupportedCafé→café folds correctly with Unicode-aware case methods. If your column collation treats é and e as equal but the stored bytes differ, lowercasing alone won't reconcile them — that's an accent-folding concern, not a case one.
Export exceeds the free 2 MB / 500-row limit
BlockedLarge table dumps are blocked on free. Split with csv-row-limiter or upgrade to Pro (100 MB / 100,000 rows). For a one-off load, splitting and concatenating the COPY runs also works.
Frequently asked questions
Should I lowercase primary-key columns too?
For numeric IDs there is nothing to lowercase. For UUID or slug-type keys, lowercasing is common but only safe if you fold both sides of every relationship together — otherwise a foreign key may stop matching its parent. Lowercase keys deliberately, as a coordinated migration, not casually.
Does this modify numeric columns?
No. Case conversion affects letters only, so numbers, prices, and counts pass through unchanged. There is no reason to tick numeric columns, but doing so is harmless to the digits.
Is this different from using LOWER() in SQL after import?
The end result is the same value, but normalising before import means your stored data is clean and every query, index, and JOIN is correct without wrapping columns in LOWER() (which also disables index usage). Pre-import normalisation is the more robust pattern; add a CHECK constraint to keep it that way.
Will it break my PostgreSQL NULL markers?
It can. Text-format COPY writes \N for NULL, and lowercase turns that into \n, which your importer reads as a literal. Keep any column containing \N unticked, or replace the markers with csv-find-replace before converting case.
Does it trim the trailing spaces from CHAR(N) columns?
No — it changes case only. A space-padded CHAR(10) value stays padded. Run csv-whitespace-trimmer before the load so your enum values compare cleanly.
Will the header row get lowercased?
No. The converter skips the first row, so your column names stay exactly as exported and your COPY ... (col_list) or LOAD DATA column mapping keeps working. Only the data values in selected columns change.
Can I lowercase several enum columns at once?
Yes. Tick status, type, category, country_code, and any other enum columns together and they are all folded in the same pass. Leave IDs, timestamps, and free-text columns unticked.
What about columns where case is meaningful, like hashes or tokens?
Never lowercase those — folding a base64 token, password hash, or JWT segment destroys it. Tick only columns where case is incidental (status, category). Leave anything case-significant unticked.
Can I import an Excel export of my table?
Yes — .xlsx, .xls, and .ods are accepted (first sheet), and the result comes back as .xlsx. Plain .csv, .tsv, and .txt work too. Watch for Excel having reformatted dates or numbers on export.
How big a table can I process for free?
Up to 2 MB and 500 data rows on the free tier. For a larger dump, split it with csv-row-limiter or upgrade to Pro (100 MB / 100,000 rows).
Is my data uploaded to a server?
No. PapaParse runs in your browser; the export never leaves your machine. Signed-in users get a single anonymous run counter for dashboard stats — no cell content is transmitted.
Can I make this part of an ETL pipeline?
Yes. GET /api/v1/tools/csv-case-converter returns the schema (caseType, columnIndices); pair the JAD runner and POST the payload to lowercase the enum columns on each export before the bulk load. Chain a whitespace-trim step and a find-replace for NULL handling for a complete pre-load clean.
Privacy first
Processing runs locally in your browser with PapaParse. No file is uploaded — only metadata counters are saved for signed-in dashboard stats.