Skip to content
Common mistakes / SQL: Why Dates Stored as Text Filter Wrong
Common mistake

SQL: Why Dates Stored as Text Filter Wrong

A date kept as text like 11/03/2024 compares character by character, so it sorts after 11/01/2025. Parse the text into a date before you filter or sort on it.

The symptom

An export from another system stored Ecommerce Orders' order dates as text in the US format, MM/DD/YYYY. The bench rebuilds that column with strftime. You ask for the orders placed since November 1, 2025 with where order_date >= '11/01/2025' and get 581 orders. There are 211.

Why it happens

Text compares one character at a time, from the left, so the month decides first and the year last. '11/03/2024' is greater than '11/01/2025' because 3 beats 1 in the fifth character, and the year is never reached. So the filter also takes November 2 to December 31 of 2023 (189 orders) and of 2024 (181 orders). November 1 itself is left out in both years: '11/01/2023' and '11/01/2025' differ only in the year, and the earlier year is the smaller text.

The fix

Turn the text into a date with strptime, naming the format it was written in, and compare it to a real date:

Wrong: compared as text
with imported as (
  select order_id,
         strftime(ordered_at, '%m/%d/%Y') as order_date
  from orders
)
select count(*) as orders
from imported
where order_date >= '11/01/2025'
Right: parsed into a date first
with imported as (
  select order_id,
         strftime(ordered_at, '%m/%d/%Y') as order_date
  from orders
)
select count(*) as orders
from imported
where strptime(order_date, '%m/%d/%Y') >= date '2025-11-01'

strptime returns a TIMESTAMP; cast it with ::date when you want a plain date column. A value that doesn't fit the format stops the query, with Could not parse string "13/45/2025" according to format specifier. try_strptime returns NULL for those instead, so you can find and count them.

Better: fix the column once

Parsing in every query is easy to forget. Convert the column when the data arrives, and every later query compares real dates:

Parse once, then filter normally
with imported as (
  select order_id,
         strftime(ordered_at, '%m/%d/%Y') as order_date
  from orders
),
cleaned as (
  select order_id,
         strptime(order_date, '%m/%d/%Y')::date as order_date
  from imported
)
select count(*) as orders
from cleaned
where order_date >= date '2025-11-01'

DuckDB won't compare the text column to a real date without a cast: order_date >= date '2025-11-01' on the text column fails with Cannot compare values of type VARCHAR and type DATE - an explicit cast is required. That error is the safer outcome; the quoted string version runs and is wrong.

Watch out

Text in ISO format, YYYY-MM-DD, does sort and compare correctly, because the year comes first and every part has a fixed width. The US MM/DD/YYYY and European DD/MM/YYYY forms don't. Each engine parses with its own function: to_date in PostgreSQL and Oracle, STR_TO_DATE in MySQL, and CONVERT(date, value, 101) in SQL Server.

Learn more