This MySQL error occurs when using the RANGE frame specification in a window function without a proper ORDER BY clause that uses numeric or temporal data types. The error indicates that MySQL cannot determine the range boundaries because the ordering column is not compatible with range-based window frames.
The ER_WINDOW_RANGE_FRAME_ORDER_TYPE (3587) error is specific to MySQL window functions. When you use the RANGE frame specification (as opposed to ROWS), MySQL needs to understand how to calculate the range boundaries relative to the current row. RANGE frames work by comparing values in the ORDER BY column(s). For example, "RANGE BETWEEN 10 PRECEDING AND 10 FOLLOWING" means include rows where the ORDER BY column value is within ±10 of the current row's value. This only makes sense when the ORDER BY column has numeric or temporal (date/time) data types, as these types support arithmetic operations needed for range calculations. When you use RANGE with ORDER BY columns that are strings, enums, or other non-numeric/temporal types, MySQL cannot perform the necessary value comparisons to determine which rows fall within the specified range, resulting in this error.
If you don't specifically need RANGE semantics, switch to ROWS which works with any data type:
-- Problematic query with RANGE
SELECT
employee_id,
salary,
AVG(salary) OVER (
PARTITION BY department_id
ORDER BY employee_name -- String column!
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) as avg_salary
FROM employees;
-- Fixed query using ROWS instead
SELECT
employee_id,
salary,
AVG(salary) OVER (
PARTITION BY department_id
ORDER BY employee_name
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW -- Changed to ROWS
) as avg_salary
FROM employees;ROWS frames work by counting rows (physical offset), not by comparing values, so they work with any ORDER BY data type.
If you need RANGE semantics, ensure ORDER BY uses numeric or temporal columns:
-- Problem: ORDER BY on string column
SELECT
order_id,
amount,
SUM(amount) OVER (
ORDER BY customer_name -- String column causes error
RANGE BETWEEN 1000 PRECEDING AND 1000 FOLLOWING
) as rolling_sum
FROM orders;
-- Solution: ORDER BY on numeric column
SELECT
order_id,
amount,
SUM(amount) OVER (
ORDER BY amount -- Numeric column works with RANGE
RANGE BETWEEN 1000 PRECEDING AND 1000 FOLLOWING
) as rolling_sum
FROM orders;
-- Temporal example with dates
SELECT
date,
revenue,
SUM(revenue) OVER (
ORDER BY date -- Date column works with RANGE
RANGE BETWEEN INTERVAL 7 DAY PRECEDING AND CURRENT ROW
) as weekly_revenue
FROM daily_sales;If your string column represents numeric data, convert it explicitly:
-- Problem: String column that looks numeric
SELECT
product_id,
price_string, -- VARCHAR like '99.99'
AVG(CAST(price_string AS DECIMAL(10,2))) OVER (
ORDER BY price_string -- Still a string!
RANGE BETWEEN 10 PRECEDING AND 10 FOLLOWING
) as avg_price
FROM products;
-- Solution: Convert in ORDER BY
SELECT
product_id,
price_string,
AVG(CAST(price_string AS DECIMAL(10,2))) OVER (
ORDER BY CAST(price_string AS DECIMAL(10,2)) -- Converted to numeric
RANGE BETWEEN 10 PRECEDING AND 10 FOLLOWING
) as avg_price
FROM products;
-- Or better: Store as proper numeric type
ALTER TABLE products MODIFY COLUMN price DECIMAL(10,2);
SELECT
product_id,
price,
AVG(price) OVER (
ORDER BY price -- Now a proper numeric column
RANGE BETWEEN 10 PRECEDING AND 10 FOLLOWING
) as avg_price
FROM products;Understand when to use RANGE vs ROWS:
-- ROWS: Count 3 rows before current row
SELECT
timestamp,
temperature,
AVG(temperature) OVER (
ORDER BY timestamp
ROWS BETWEEN 3 PRECEDING AND CURRENT ROW
) as moving_avg
FROM sensor_readings;
-- Includes exactly 3 previous rows, regardless of time gaps
-- RANGE: Include rows within 5 minutes
SELECT
timestamp,
temperature,
AVG(temperature) OVER (
ORDER BY timestamp
RANGE BETWEEN INTERVAL 5 MINUTE PRECEDING AND CURRENT ROW
) as moving_avg
FROM sensor_readings;
-- Includes all rows within 5-minute window, even if many rows
-- Common use cases for RANGE:
-- 1. Time-based windows (dates, timestamps)
-- 2. Value-based windows (amounts, scores, measurements)
-- 3. When you want to include "peer" rows with identical values
-- RANGE with peer rows example:
SELECT
student_id,
test_score,
RANK() OVER (
ORDER BY test_score
RANGE BETWEEN CURRENT ROW AND CURRENT ROW -- Includes peers with same score
) as rank_with_peers
FROM test_results;Ensure your MySQL version properly supports window functions:
-- Check MySQL version
SELECT VERSION();
-- Window functions require MySQL 8.0+ or MariaDB 10.2+
-- MySQL 5.7 and earlier do not support window functions at all
-- Test basic window function support
SELECT
1 as test,
ROW_NUMBER() OVER () as row_num;
-- If this fails, you need to upgrade MySQL
-- Upgrade path: MySQL 5.7 → 8.0
-- Check for known bugs or limitations in your version
-- Some early MySQL 8.0 releases had window function bugs
-- For MariaDB users:
SELECT @@version, @@version_comment;
-- MariaDB 10.2+ supports window functions
-- Consider compatibility mode if migrating from other databases
SET SESSION sql_mode = 'ANSI';
-- Or explicitly set window function behavior
SET SESSION windowing_use_high_precision = ON;The RANGE frame specification has specific requirements and behaviors in MySQL:
1. Peer Rows: RANGE includes "peer" rows (rows with identical ORDER BY values) in the frame. This is different from ROWS, which treats each row individually even with identical values.
2. Performance Considerations: RANGE frames can be slower than ROWS frames, especially with large datasets, because MySQL needs to evaluate value comparisons rather than simple row offsets.
3. Data Type Limitations: Only numeric (INT, DECIMAL, FLOAT, etc.) and temporal (DATE, DATETIME, TIMESTAMP) types work with RANGE. Even numeric-like strings (VARCHAR containing numbers) won't work without explicit casting.
4. NULL Handling: NULL values in ORDER BY columns with RANGE frames can have unexpected behavior. All NULLs are considered "peers" and included together in RANGE calculations.
5. Multiple ORDER BY Columns: When using multiple columns in ORDER BY with RANGE, only the first column's data type matters for RANGE compatibility. Subsequent columns can be any type.
6. Fractional Ranges: With decimal/numeric types, fractional ranges work correctly (RANGE BETWEEN 0.5 PRECEDING AND 0.5 FOLLOWING).
7. Temporal Intervals: MySQL supports complex temporal intervals with RANGE: INTERVAL 1 DAY, INTERVAL 2 HOUR, INTERVAL 30 MINUTE, etc.
8. Migration from Other Databases: PostgreSQL and SQL Server have different RANGE semantics (especially regarding EXCLUDE clause support). Test thoroughly when migrating window function queries.
ERROR 1064: You have an error in your SQL syntax
How to fix "ERROR 1064: You have an error in your SQL syntax" in MySQL
ERROR 1054: Unknown column in field list
Unknown column in field list
ER_WINDOW_RANGE_FRAME_NUMERIC_TYPE (3589): RANGE frame requires numeric ORDER BY expression
RANGE frame requires numeric ORDER BY expression in MySQL window functions
CR_ALREADY_CONNECTED (2058): Handle already connected
How to fix "CR_ALREADY_CONNECTED (2058): Handle already connected" in MySQL
ER_WINDOW_DUPLICATE_NAME (3591): Duplicate window name
How to fix ER_WINDOW_DUPLICATE_NAME (3591) in MySQL window functions