docs/sources/datasources/postgres/query-editor/_index.md
The PostgreSQL query editor lets you build and run queries against your data source. For general query editor and data transformation concepts, refer to Query and transform data.
You can open the PostgreSQL query editor from the Explore page or from a dashboard panel—click the ellipsis in the upper right of the panel and select Edit.
{{< admonition type="note" >}} A default database must be configured in the data source settings. If none is set, or it is removed, the data source will not run queries until a database is configured again. {{< /admonition >}}
The PostgreSQL query editor has two modes: Builder and Code.
Builder mode helps you build a query using a visual interface. Code mode allows for advanced querying and offers support for complex SQL query writing.
The following components will help you build a PostgreSQL query:
time. Refer to Time series queries for more information.AND operator to display all true conditions or an OR operator to display any true conditions. Use the second drop-down to choose a filter value. To remove a filter, click the X next to that filter's drop-down. After selecting a date-type column, you can choose Macros from the operators list and select timeFilter to add the $__timeFilter macro to the query with the selected date column.ORDER BY statement.ASC) or descending (DESC) order.To create advanced queries, switch to Code mode by clicking Code in the upper right of the editor window. Code mode supports the auto-completion of tables, columns, SQL keywords, standard SQL functions, Grafana template variables, and Grafana macros. Columns cannot be completed before a table has been specified.
{{< admonition type="note" >}}
If a table or column name is a reserved word or contains mixed case or special characters, use double quotes in SQL. For example, "user" or "Created At".
{{< /admonition >}}
{{< figure src="/static/img/docs/v92/sql_code_editor.png" class="docs-image--no-shadow" >}}
Select Table or Time Series as the format. Click the {} in the bottom right to format the query. Click the downward caret to expand the Code mode editor. CTRL/CMD + Return serves as a keyboard shortcut to execute the query.
{{< admonition type="warning" >}} Changes made to a query in Code mode will not transfer to Builder mode and will be discarded. You will be prompted to copy your code to the clipboard to save any changes. {{< /admonition >}}
You can add macros to your queries to simplify the syntax and enable dynamic elements, such as date range filters.
Grafana expands macros into native PostgreSQL SQL before executing the query. When the TimescaleDB extension is enabled, $__timeGroup and $__timeGroupAlias use time_bucket() for more efficient grouping.
| Macro example | Description |
|---|---|
$__time(dateColumn) | Renames the column to time. Example: dateColumn AS "time". Use for native date/time columns. |
$__timeEpoch(dateColumn) | Converts to UNIX epoch (seconds) and renames the column to time. Example: extract(epoch from dateColumn) as "time". Requires a column argument. |
$__timeFilter(dateColumn) | Replaces the value with a time range filter using the specified column name. Example: dateColumn BETWEEN '2020-07-13T20:19:09.254Z' AND '2020-07-13T21:19:09.254Z'. |
$__timeFrom() | Replaces the value with the start of the currently active time selection (RFC3339Nano). Example: '2020-07-13T20:19:09.254Z'. |
$__timeTo() | Replaces the value with the end of the currently active time selection (RFC3339Nano). Example: '2020-07-13T20:19:09.254Z'. |
$__timeGroup(dateColumn,'5m') | Replaces the value with an expression suitable for use in a GROUP BY clause. Example: floor(extract(epoch from dateColumn)/300)*300. With TimescaleDB: time_bucket('300s', dateColumn). |
$__timeGroup(dateColumn,'5m', 0) | Same as $__timeGroup(dateColumn,'5m'), but includes a fill parameter to ensure missing points in the series are added by Grafana, using 0 as the default value. Fill only applies to time series queries. |
$__timeGroup(dateColumn,'5m', NULL) | Same as $__timeGroup(dateColumn,'5m', 0) but NULL is used as the value for missing points. Fill only applies to time series queries. |
$__timeGroup(dateColumn,'5m', previous) | Same as $__timeGroup(dateColumn,'5m', 0) but uses the previous value in the series as the fill value. If no previous value exists, it uses NULL. Fill only applies to time series queries. |
$__timeGroupAlias(dateColumn,'5m') | Same as $__timeGroup but with an added column alias AS "time". With TimescaleDB, uses time_bucket(). |
| Macro example | Description |
|---|---|
$__unixEpochFilter(dateColumn) | Replaces the value with a time range filter for columns storing UNIX epoch (seconds). Example: dateColumn >= 1494410783 AND dateColumn <= 1494497183. |
$__unixEpochFrom() | Replaces the value with the start of the currently active time selection as a UNIX timestamp (seconds). Example: 1494410783. |
$__unixEpochTo() | Replaces the value with the end of the currently active time selection as a UNIX timestamp (seconds). Example: 1494497183. |
$__unixEpochNanoFilter(dateColumn) | Replaces the value with a time range filter for columns storing UNIX epoch in nanoseconds. Example: dateColumn >= 1494410783152415214 AND dateColumn <= 1494497183142514872. |
$__unixEpochNanoFrom() | Replaces the value with the start of the currently active time selection as a nanosecond timestamp. Example: 1494410783152415214. |
$__unixEpochNanoTo() | Replaces the value with the end of the currently active time selection as a nanosecond timestamp. Example: 1494497183142514872. |
$__unixEpochGroup(dateColumn,'5m', [fillmode]) | Same as $__timeGroup but for columns storing UNIX epoch (seconds). Example: floor((dateColumn)/300)*300. fillMode only applies to time series queries. |
$__unixEpochGroupAlias(dateColumn,'5m', [fillmode]) | Same as $__unixEpochGroup but with an added column alias AS "time". fillMode only applies to time series queries. |
In addition to the macros listed, the following Grafana interval variables are replaced before query execution:
| Variable | Description |
|---|---|
$__interval | Replaced with the calculated interval based on the time range and panel width (for example, 5m). Use in $__timeGroup or directly in custom expressions. |
$__interval_ms | Same as $__interval but in milliseconds (for example, 300000). |
You can set a lower bound for these variables with the Min time interval option in the data source configuration or the panel's query options.
Keep the following behavior in mind when using macros:
-- line comments and /* */ block comments) before expanding macros. Macros placed inside comments aren't expanded.$__timeGroup and $__timeGroupAlias use PostgreSQL's time_bucket() function instead of floor(extract(epoch ...)).$__timeGroup(...) call followed immediately by a comma (,) is automatically treated as $__timeGroupAlias(...), adding the AS "time" alias. This is a legacy convenience—use $__timeGroupAlias explicitly for clarity.If the Format option is set to Table, you can execute virtually any type of SQL query. The Table panel automatically displays the resulting columns and rows from your query.
You can change or customize the name of a Table panel column by using the SQL keyword AS syntax.
SELECT
title as "Title",
"user".login as "Created By",
dashboard.created as "Created On"
FROM dashboard
INNER JOIN "user" on "user".id = dashboard.created_by
WHERE $__timeFilter(dashboard.created)
You can use template variables in queries to create dynamic, reusable dashboards. For example, to filter by a variable named hostname:
SELECT
$__time("time_date_time"),
value_double AS value,
hostname
FROM test_data
WHERE
$__timeFilter("time_date_time")
AND hostname IN($hostname)
ORDER BY time
For more information on creating and using template variables with PostgreSQL, refer to PostgreSQL template variables.
Table format queries support a timeend column in addition to the time column. When both are present, Grafana treats the row as a time range rather than a single point. This is useful for displaying events with duration, such as maintenance windows or deployments.
SELECT
start_time AS "time",
end_time AS "timeend",
description,
status
FROM maintenance_windows
WHERE $__timeFilter(start_time)
Set the Format option to Time series to create and run time series queries.
{{< admonition type="note" >}}
To run a time series query you must include a column named time that returns either a SQL datetime value or a numeric datatype representing the UNIX epoch time in seconds. Additionally, the query results must be sorted by the time column for proper visualization in panels.
{{< /admonition >}}
The examples in this section refer to the data in the following table:
+---------------------+--------------+---------------------+----------+
| time_date_time | value_double | CreatedAt | hostname |
+---------------------+--------------+---------------------+----------+
| 2020-01-02 03:05:00 | 3.0 | 2020-01-02 03:05:00 | 10.0.1.1 |
| 2020-01-02 03:06:00 | 4.0 | 2020-01-02 03:06:00 | 10.0.1.2 |
| 2020-01-02 03:10:00 | 6.0 | 2020-01-02 03:10:00 | 10.0.1.1 |
| 2020-01-02 03:11:00 | 7.0 | 2020-01-02 03:11:00 | 10.0.1.2 |
| 2020-01-02 03:20:00 | 5.0 | 2020-01-02 03:20:00 | 10.0.1.2 |
+---------------------+--------------+---------------------+----------+
Time series query results are returned in wide data frame format. In the data frame query result, any column, except for time or string-type columns, transforms into value fields. String columns become field labels.
The PostgreSQL plugin identifies string-type columns (text, varchar, char, bpchar) as label columns that split the result into multiple series. Numeric and time columns become value fields.
{{< admonition type="note" >}}
For backward compatibility, an exception to this rule applies to queries that return three columns, one of which is a string column named metric. Instead of converting the metric column into field labels, it's used as the field name, while the series name is set to its value. Refer to the following example.
{{< /admonition >}}
When using $__timeGroup or $__unixEpochGroup with a fill parameter (the third argument), Grafana fills gaps in your time series data. This is useful when your data has irregular intervals or missing data points. Fill only works with time series format queries.
The available fill modes are:
| Fill mode | Behavior |
|---|---|
0 (or any number) | Missing points are filled with the specified numeric value. |
NULL | Missing points are filled with NULL, which creates gaps in line charts. |
previous | Missing points use the last known value. If no previous value exists, NULL is used. |
For example, $__timeGroupAlias("CreatedAt",'5m', 0) groups data into 5-minute intervals and fills any gaps with 0.
Grafana applies a server-side row limit to query results to protect against excessive memory usage. If your query returns more rows than the limit, the result is truncated. To reduce the number of rows returned, narrow the dashboard time range, increase the $__timeGroup interval, or add a LIMIT clause to your query.
Example with metric column:
SELECT
$__timeGroupAlias("time_date_time",'5m'),
min("value_double"),
'min' as metric
FROM test_data
WHERE $__timeFilter("time_date_time")
GROUP BY time
ORDER BY time
Data frame result:
+---------------------+-----------------+
| Name: time | Name: min |
| Labels: | Labels: |
| Type: []time.Time | Type: []float64 |
+---------------------+-----------------+
| 2020-01-02 03:05:00 | 3 |
| 2020-01-02 03:10:00 | 6 |
+---------------------+-----------------+
To customize default series name formatting, refer to Standard options definitions.
Following are time series query examples.
Example with no grouping (raw points):
To return raw time and value points without grouping, use $__time to alias your date/time column as time:
SELECT
$__time("time_date_time"),
"value_double" as value
FROM test_data
WHERE $__timeFilter("time_date_time")
ORDER BY time
Example using the fill parameter in the $__timeGroupAlias macro to convert null values to zero:
SELECT
$__timeGroupAlias("CreatedAt",'5m',0),
sum(value) as value,
hostname
FROM test_data
WHERE
$__timeFilter("CreatedAt")
GROUP BY time, hostname
ORDER BY time
Based on the data frame result in the following example, the time series panel will generate two series named value 10.0.1.1 and value 10.0.1.2. To display the series names as 10.0.1.1 and 10.0.1.2, use the Standard options definitions display value ${__field.labels.hostname}.
Data frame result:
+---------------------+---------------------------+---------------------------+
| Name: time | Name: value | Name: value |
| Labels: | Labels: hostname=10.0.1.1 | Labels: hostname=10.0.1.2 |
| Type: []time.Time | Type: []float64 | Type: []float64 |
+---------------------+---------------------------+---------------------------+
| 2020-01-02 03:05:00 | 3 | 4 |
| 2020-01-02 03:10:00 | 6 | 7 |
+---------------------+---------------------------+---------------------------+
Example with multiple columns:
SELECT
$__timeGroupAlias("time_date_time",'5m'),
min("value_double") as "min_value",
max("value_double") as "max_value"
FROM test_data
WHERE $__timeFilter("time_date_time")
GROUP BY time
ORDER BY time
Data frame result:
+---------------------+-----------------+-----------------+
| Name: time | Name: min_value | Name: max_value |
| Labels: | Labels: | Labels: |
| Type: []time.Time | Type: []float64 | Type: []float64 |
+---------------------+-----------------+-----------------+
| 2020-01-02 03:04:00 | 3 | 4 |
| 2020-01-02 03:05:00 | 6 | 7 |
+---------------------+-----------------+-----------------+
Example with UNIX epoch time column:
When your time column stores UNIX epoch (seconds), use $__timeEpoch to alias it as time and $__unixEpochFilter in the WHERE clause:
SELECT
$__timeEpoch(epoch_seconds_column),
value_column as value
FROM my_table
WHERE $__unixEpochFilter(epoch_seconds_column)
ORDER BY time
For grouped time series with epoch columns, use $__unixEpochGroupAlias and $__unixEpochFilter. Refer to the Macros table for details.
You can run EXPLAIN and EXPLAIN ANALYZE queries in Code mode to inspect query execution plans. The results are returned as a table, which is useful for diagnosing slow queries directly within Grafana.
EXPLAIN ANALYZE
SELECT
$__timeGroupAlias("time_date_time", '5m'),
avg("value_double") AS value
FROM test_data
WHERE $__timeFilter("time_date_time")
GROUP BY time
ORDER BY time
Grafana macros are expanded before the query runs, so you can use $__timeFilter and other macros in your EXPLAIN queries to see the actual execution plan for the dashboard's time range. Set the Format to Table to display the plan output.