Documentation

Compare values in SQL queries

Use SQL window functions to compare values across different rows in your time series data. Window functions like LAG and LEAD let you access values from previous or subsequent rows without using self-joins, making it easy to calculate changes over time.

Common use cases for comparing values include:

  • Calculating the difference between the current value and a previous value
  • Computing rate of change or percentage change
  • Detecting significant changes or anomalies
  • Comparing values at specific time intervals
  • Handling counter metrics that reset to zero

To compare values across rows:

  1. Use a window function such as LAG or LEAD with an OVER clause.
  2. Include a PARTITION BY clause to group data by tags (like room or sensor_id).
  3. Include an ORDER BY clause to define the order for comparisons (typically by time).
  4. Use arithmetic operators to calculate differences, ratios, or percentage changes.

Examples of comparing values

Sample data

The following examples use the Home sensor sample data. To run the example queries and return results, write the sample data to your InfluxDB 3 Core database before running the example queries.

Calculate the difference from the previous value

Use the LAG function to access the value from the previous row and calculate the difference. This is useful for detecting changes over time.

SELECT
  time,
  room,
  temp,
  temp - LAG(temp, 1) OVER (
    PARTITION BY room
    ORDER BY time
  ) AS temp_change
FROM home
WHERE
  time >= '2022-01-01T08:00:00Z'
  AND time < '2022-01-01T11:00:00Z'
ORDER BY room, time
timeroomtemptemp_change
2022-01-01T08:00:00Kitchen21.0NULL
2022-01-01T09:00:00Kitchen23.02.0
2022-01-01T10:00:00Kitchen22.7-0.3
2022-01-01T08:00:00Living Room21.1NULL
2022-01-01T09:00:00Living Room21.40.3
2022-01-01T10:00:00Living Room21.80.4

The first row in each partition returns NULL for temp_change because there’s no previous value. To use a default value instead of NULL, provide a third argument to LAG:

LAG(temp, 1, 0) -- Returns 0 if no previous value exists

Calculate the percentage change

Calculate the percentage change between the current value and a previous value by dividing the difference by the previous value.

SELECT
  time,
  room,
  temp,
  ROUND(
    ((temp - LAG(temp, 1) OVER (PARTITION BY room ORDER BY time)) /
     LAG(temp, 1) OVER (PARTITION BY room ORDER BY time)) * 100,
    2
  ) AS percent_change
FROM home
WHERE
  time >= '2022-01-01T08:00:00Z'
  AND time < '2022-01-01T11:00:00Z'
ORDER BY room, time
timeroomtemppercent_change
2022-01-01T08:00:00Kitchen21.0NULL
2022-01-01T09:00:00Kitchen23.09.52
2022-01-01T10:00:00Kitchen22.7-1.30
2022-01-01T08:00:00Living Room21.1NULL
2022-01-01T09:00:00Living Room21.41.42
2022-01-01T10:00:00Living Room21.81.87

Compare values at regular intervals

For regularly spaced time series data (like hourly readings), use LAG with an offset parameter to compare values from a specific number of rows back.

The following query compares each temperature reading with the reading from one hour earlier (assuming hourly data):

SELECT
  time,
  room,
  temp,
  LAG(temp, 1) OVER (
    PARTITION BY room
    ORDER BY time
  ) AS temp_1h_ago,
  temp - LAG(temp, 1) OVER (
    PARTITION BY room
    ORDER BY time
  ) AS hourly_change
FROM home
WHERE
  time >= '2022-01-01T08:00:00Z'
  AND time < '2022-01-01T12:00:00Z'
ORDER BY room, time
timeroomtemptemp_1h_agohourly_change
2022-01-01T08:00:00Kitchen21.0NULLNULL
2022-01-01T09:00:00Kitchen23.021.02.0
2022-01-01T10:00:00Kitchen22.723.0-0.3
2022-01-01T11:00:00Kitchen22.422.7-0.3
2022-01-01T08:00:00Living Room21.1NULLNULL
2022-01-01T09:00:00Living Room21.421.10.3
2022-01-01T10:00:00Living Room21.821.40.4
2022-01-01T11:00:00Living Room22.221.80.4

Compare values with exact time offsets

For irregularly spaced time series data or when you need to compare values from an exact time offset (like exactly 1 hour ago, not just the previous row), use a self-join with interval arithmetic.

SELECT
  current.time,
  current.room,
  current.temp AS current_temp,
  previous.temp AS temp_1h_ago,
  current.temp - previous.temp AS hourly_diff
FROM home AS current
LEFT JOIN home AS previous
  ON current.room = previous.room
  AND previous.time = current.time - INTERVAL '1 hour'
WHERE
  current.time >= '2022-01-01T08:00:00Z'
  AND current.time < '2022-01-01T12:00:00Z'
ORDER BY current.room, current.time
timeroomcurrent_temptemp_1h_agohourly_diff
2022-01-01T08:00:00Kitchen21.0NULLNULL
2022-01-01T09:00:00Kitchen23.021.02.0
2022-01-01T10:00:00Kitchen22.723.0-0.3
2022-01-01T11:00:00Kitchen22.422.7-0.3
2022-01-01T08:00:00Living Room21.1NULLNULL
2022-01-01T09:00:00Living Room21.421.10.3
2022-01-01T10:00:00Living Room21.821.40.4
2022-01-01T11:00:00Living Room22.221.80.4

This self-join approach works when:

  • Your data points don’t fall at regular intervals
  • You need to compare against a specific time offset regardless of when the previous data point occurred
  • You want to ensure the comparison is against a value from exactly 1 hour ago (or any other specific interval)

Handle counter metrics and resets

Counter metrics track cumulative values that increase over time, such as total requests, bytes transferred, or errors. Unlike gauge metrics (which can go up or down), counters typically only increase, though they may reset to zero when a service restarts.

Use GREATEST with LAG to handle counter resets by treating negative differences as zero.

InfluxDB 3 SQL and counter metrics

InfluxDB 3 SQL doesn’t provide built-in equivalents to Flux’s increase() or InfluxQL’s NON_NEGATIVE_DIFFERENCE() functions. Use the patterns shown below to achieve similar results.

Calculate non-negative differences (counter rate)

Calculate the increase between consecutive counter readings, treating negative differences (counter resets) as zero.

SELECT
  time,
  host,
  requests,
  LAG(requests) OVER (PARTITION BY host ORDER BY time) AS prev_requests,
  GREATEST(
    requests - LAG(requests) OVER (PARTITION BY host ORDER BY time),
    0
  ) AS requests_increase
FROM metrics
WHERE host = 'server1'
ORDER BY time
timehostrequestsprev_requestsrequests_increase
2024-01-01T00:00:00server11000NULL0
2024-01-01T01:00:00server112501000250
2024-01-01T02:00:00server116001250350
2024-01-01T03:00:00server15016000
2024-01-01T04:00:00server130050250

LAG(requests) retrieves the previous counter value, requests - LAG(requests) calculates the difference, and GREATEST(..., 0) returns 0 for negative differences (counter resets). PARTITION BY host ensures comparisons are only within the same host.

Calculate cumulative counter increase

Calculate the total increase in a counter over time, handling resets. Use a Common Table Expression (CTE) to first calculate the differences, then sum them.

WITH counter_diffs AS (
  SELECT
    time,
    host,
    requests,
    GREATEST(
      requests - LAG(requests) OVER (PARTITION BY host ORDER BY time),
      0
    ) AS requests_increase
  FROM metrics
  WHERE host = 'server1'
)
SELECT
  time,
  host,
  requests,
  SUM(requests_increase) OVER (PARTITION BY host ORDER BY time) AS cumulative_increase
FROM counter_diffs
ORDER BY time
timehostrequestscumulative_increase
2024-01-01T00:00:00server110000
2024-01-01T01:00:00server11250250
2024-01-01T02:00:00server11600600
2024-01-01T03:00:00server150600
2024-01-01T04:00:00server1300850

The CTE computes non-negative differences for each row, then SUM(requests_increase) OVER (...) creates a running total. The cumulative increase continues to grow despite the counter reset at 03:00.

Aggregate counter increases by time interval

Calculate the total increase in a counter for each time interval (for example, hourly totals).

WITH counter_diffs AS (
  SELECT
    DATE_BIN(INTERVAL '1 hour', time) AS time_bucket,
    host,
    requests,
    GREATEST(
      requests - LAG(requests) OVER (PARTITION BY host ORDER BY time),
      0
    ) AS requests_increase
  FROM metrics
)
SELECT
  time_bucket,
  host,
  SUM(requests_increase) AS total_increase
FROM counter_diffs
WHERE requests_increase > 0
GROUP BY time_bucket, host
ORDER BY host, time_bucket
time_buckethosttotal_increase
2024-01-01T01:00:00server1250
2024-01-01T02:00:00server1350
2024-01-01T04:00:00server1250
2024-01-01T01:00:00server2400
2024-01-01T02:00:00server2500
2024-01-01T03:00:00server2300
2024-01-01T04:00:00server2400

The CTE calculates differences for each row. DATE_BIN() assigns each timestamp to a 1-hour interval, SUM(requests_increase) aggregates all increases within each interval, and WHERE requests_increase > 0 filters out zero increases (first row and counter resets).


Was this page helpful?

Thank you for your feedback!


New in InfluxDB 3.7

Key enhancements in InfluxDB 3.7 and the InfluxDB 3 Explorer 1.5.

See the Blog Post

InfluxDB 3.7 is now available for both Core and Enterprise, landing alongside version 1.5 of the InfluxDB 3 Explorer UI. This release focuses on giving developers faster visibility into what their system is doing with one-click monitoring, a streamlined installation pathway, and broader updates that simplify day-to-day operations.

For more information, check out:

InfluxDB Docker latest tag changing to InfluxDB 3 Core

On February 3, 2026, the latest tag for InfluxDB Docker images will point to InfluxDB 3 Core. To avoid unexpected upgrades, use specific version tags in your Docker deployments.

If using Docker to install and run InfluxDB, the latest tag will point to InfluxDB 3 Core. To avoid unexpected upgrades, use specific version tags in your Docker deployments. For example, if using Docker to run InfluxDB v2, replace the latest version tag with a specific version tag in your Docker pull command–for example:

docker pull influxdb:2