Skip to content
Blog

Why Industrial Teams Are Moving from Flux and InfluxQL to Standard SQL

In three major versions of InfluxDB, industrial teams have been asked to learn three different query languages. InfluxQL in version 1. Flux in version 2. SQL in version 3, with InfluxQL reinstated alongside it. The cost of that sequence is not just query rewrites. It is abandoned tooling, skills that became obsolete with a version bump, and engineering hours spent on language migrations instead of production features.

Three languages in three versions

The InfluxDB query language history is worth documenting precisely, because teams evaluating time-series databases carry the operational scars of it.

InfluxDB 1.x: InfluxQL. A SQL-like language specific to InfluxDB. Teams built Grafana dashboards, wrote application queries, and documented their data pipelines in InfluxQL. It worked within the limits of what it could express.

InfluxDB 2.x: Flux replaces InfluxQL. InfluxData introduced Flux as the future of InfluxDB querying and demoted InfluxQL to legacy status. Teams that adopted version 2 were steered toward Flux, a functional data scripting language with a pipeline syntax unlike anything else in the data stack. Grafana's InfluxDB data source added a Flux mode alongside the InfluxQL mode, requiring teams to choose and maintain separate configurations.

InfluxDB 3.x: Flux deprecated, SQL added, InfluxQL reinstated. Version 3 rebuilt the storage layer on Apache Parquet and Arrow and introduced native SQL via Apache DataFusion. InfluxData deprecated Flux. InfluxQL returned for backward compatibility with version 1 workloads. Teams on version 2 with Flux queries now face a migration regardless of which direction they move.

No other database in common use for industrial IoT data has replaced its primary query language twice in a decade.

What a language migration actually costs

A query language migration is not a find-and-replace operation. The cost runs across the entire data stack.

Grafana dashboards. A Grafana dashboard built on Flux queries does not migrate to SQL automatically. Each panel must be manually rewritten. Templating variables, time range macros, and alert thresholds that reference Flux-specific syntax must be reconstructed from scratch.

Application code. Any service that reads sensor data from InfluxDB 2 using the Flux client library must be rewritten. The Go, Python, and JavaScript clients for version 2 used the Flux API. The version 3 clients use a different API.

Team knowledge. Flux is a functional pipeline language. Learning it required real investment. That investment does not transfer to SQL or InfluxQL. Engineers who became fluent in Flux now hold skills with no application outside InfluxDB version 2.

Documentation and runbooks. Every internal guide, every operational runbook, every data dictionary written in Flux syntax is now technical debt.

The teams most affected are those that adopted InfluxDB 2 early and built deeply on Flux. They made a reasonable bet on a stated roadmap. The migration they now face is not a consequence of their decisions. It is a consequence of their vendor's architecture change.

SQL has been stable for decades

ANSI SQL was standardized in 1986. The core language — SELECT, FROM, WHERE, GROUP BY, JOIN, window functions, aggregations — has been additive across versions, not replacement-based. New capabilities arrived as extensions to what already worked, not as deprecations of what teams had already built.

Every engineer who works with data already knows SQL. It is taught in university courses, covered in every data engineering certification, and used by every BI tool, ETL framework, data notebook, and ORM in the industry. There is no onboarding cost for SQL knowledge.

The SQL capabilities that matter for industrial time-series workloads are part of the standard: DATE_TRUNC and DATE_BIN for time bucketing, INTERVAL arithmetic for range queries, window functions for rolling aggregations, and JOIN for combining sensor data with production context. These are not proprietary extensions. They are part of the standard and implemented in every modern SQL database.

What SQL looks like for industrial time-series queries

The same query expressed in Flux and in SQL. The query calculates the average vibration reading per 15-minute window over the last hour.

Flux (InfluxDB 2.x):

from(bucket: "sensor_data")
  |> range(start: -1h)
  |> filter(fn: (r) => r._measurement == "vibration"
       and r.asset_type == "conveyor-motor")
  |> aggregateWindow(every: 15m, fn: mean, createEmpty: false)
  |> yield(name: "mean")

SQL (CrateDB):

SELECT DATE_BIN('15 minutes'::INTERVAL, ts, '2000-01-01') AS window_start,
       AVG(readings['vibration']) AS avg_vibration
FROM sensor_readings
WHERE asset_type = 'conveyor-motor'
  AND ts > NOW() - INTERVAL '1 hour'
GROUP BY window_start
ORDER BY window_start;

The SQL query is readable by any engineer with SQL knowledge. No pipeline operators, no functional composition, no Flux-specific syntax to learn.

Where SQL extends further is cross-system joins. Flux requires a separate join() call to combine data from different measurements, and it cannot join across external data sources without additional infrastructure. The OEE query that matters most for manufacturing analytics (sensor readings joined to a production schedule and a shift calendar) is standard SQL:

SELECT s.asset_id,
       p.line_id,
       sh.shift_name,
       AVG(s.readings['oee_score']) AS avg_oee
FROM sensor_readings s
JOIN production_schedule p
  ON s.asset_id = p.asset_id
  AND s.ts BETWEEN p.start_time AND p.end_time
JOIN shift_calendar sh
  ON s.ts BETWEEN sh.shift_start AND sh.shift_end
WHERE s.ts > NOW() - INTERVAL '24 hours'
GROUP BY s.asset_id, p.line_id, sh.shift_name
ORDER BY avg_oee DESC;

This query is not possible in Flux. It requires a SQL database that can hold both time-series sensor data and relational operational context in the same query layer.

The tooling argument

Using standard SQL means every tool in the modern data stack works without a proprietary plugin or mode switch.

Grafana: The standard PostgreSQL data source connects to any database that implements the PostgreSQL wire protocol. No InfluxDB-specific plugin, no Flux mode, no InfluxQL mode. Dashboard queries are standard SQL that any team member can read and modify.

BI tools: Tableau, Power BI, and Looker connect via standard JDBC or ODBC. No proprietary connector required.

Data pipelines: dbt models, SQLAlchemy queries, Apache Airflow SQL operators, Jupyter notebooks with pandas.read_sql(), all work against a standard SQL database without modification.

Application code: Any language with a PostgreSQL driver (Python, Go, Java, Node.js, Rust) connects via the PostgreSQL wire protocol and issues standard SQL. No proprietary client library required.

When a team uses a SQL time-series database, their existing tooling investment carries forward intact. When they onboard a new engineer, the SQL knowledge that engineer already has applies on day one.

The migration cost is the same either way

Teams on InfluxDB 2.x with Flux queries face a migration. The question is where they land.

Migrating to InfluxDB 3 requires rewriting Flux queries to SQL and updating application code to the version 3 client API. Migrating to CrateDB requires the same rewrite to SQL. For teams using Telegraf for ingest, the pipeline change is a one-line output plugin swap; the mechanics are covered in the Telegraf migration guide.

The difference is what teams get on the other side. InfluxDB 3 is a purpose-built time-series store. CrateDB is a distributed SQL database that handles the full operational analytics workload alongside sensor ingest: OEE queries, predictive maintenance, and cross-system joins in a single query layer. For teams that have already paid a migration cost once, it is worth evaluating whether the next destination is a SQL database built for industrial scale.

The CrateDB vs. InfluxDB comparison covers the architectural trade-offs in detail.

Run queries on live data on cratedb.com/explore. The Exploration paths include industrial sensor datasets where you can run the SQL patterns from this post against real data before committing to a deployment.