Writing SQL Queries
1. Writing Basic SELECT Queries
| Component | Description | Example |
|---|---|---|
| SELECT | Specifies the columns to retrieve from a table | SELECT name, age FROM users; |
| FROM | Indicates the table to query data from | FROM employees; |
| WHERE | Filters rows based on a condition | WHERE age > 30; |
| ORDER BY | Sorts the result set by one or more columns | ORDER BY salary DESC; |
| LIMIT | Restricts the number of rows returned | LIMIT 10; |
Example: Basic SELECT Query
SELECT name, email
FROM users
WHERE active = 1
ORDER BY created_at DESC
LIMIT 5;
Note: In Grafana, SQL queries are executed against data sources like MySQL, PostgreSQL, or InfluxDB. Ensure the query returns a valid result set that matches the expected schema.
2. Using WHERE Clauses
| Clause | Description | Example |
|---|---|---|
| WHERE | Filters rows based on a condition | SELECT * FROM logs WHERE level = 'ERROR' |
| AND | Combines multiple conditions, all must be true | SELECT * FROM logs WHERE level = 'ERROR' AND timestamp > '2024-01-01' |
| OR | Combines multiple conditions, at least one must be true | SELECT * FROM logs WHERE level = 'ERROR' OR level = 'WARNING' |
| NOT | Negates a condition | SELECT * FROM logs WHERE NOT level = 'INFO' |
| IN | Matches any value in a list | SELECT * FROM logs WHERE status IN ('200', '201') |
| BETWEEN | Matches values within a range | SELECT * FROM logs WHERE timestamp BETWEEN '2024-01-01' AND '2024-01-31' |
| LIKE | Matches patterns using wildcards | SELECT * FROM logs WHERE message LIKE '%error%' |
| IS NULL | Checks for null values | SELECT * FROM logs WHERE user_id IS NULL |
| IS NOT NULL | Checks for non-null values | SELECT * FROM logs WHERE user_id IS NOT NULL |
Example: Filtering Logs by Error Level and Time Range
SELECT timestamp, level, message
FROM logs
WHERE level = 'ERROR'
AND timestamp BETWEEN '2024-01-01T00:00:00Z' AND '2024-01-31T23:59:59Z'
ORDER BY timestamp DESC;
3. Using JOIN Operations
| JOIN Type | Description | Example |
|---|---|---|
| INNER JOIN | Returns rows that have matching values in both tables | SELECT * FROM table1 t1 INNER JOIN table2 t2 ON t1.id = t2.id; |
| LEFT JOIN | Returns all rows from the left table and matched rows from the right table | SELECT * FROM table1 t1 LEFT JOIN table2 t2 ON t1.id = t2.id; |
| RIGHT JOIN | Returns all rows from the right table and matched rows from the left table | SELECT * FROM table1 t1 RIGHT JOIN table2 t2 ON t1.id = t2.id; |
| FULL OUTER JOIN | Returns all rows from both tables with matching records | SELECT * FROM table1 t1 FULL OUTER JOIN table2 t2 ON t1.id = t2.id; |
Example: Combining Data with JOINs
SELECT u.name, o.order_date, o.total
FROM users u
INNER JOIN orders o ON u.user_id = o.user_id
WHERE o.order_date >= '2023-01-01';
Note: JOINs can significantly impact query performance. Use indexes on joined columns for better performance.
4. Using Time Macros ($__timeFilter, $__timeFrom)
| Macro | Description | Usage |
|---|---|---|
| $__timeFilter | Generates a time filter condition for SQL queries. | WHERE timestamp >= $__timeFrom AND timestamp <= $__timeTo |
| $__timeFrom | Represents the start time of the current query range in milliseconds. | SELECT * FROM logs WHERE timestamp >= $__timeFrom |
| $__timeTo | Represents the end time of the current query range in milliseconds. | SELECT * FROM logs WHERE timestamp <= $__timeTo |
Example: Using Time Macros in a Query
SELECT
timestamp,
message
FROM
logs
WHERE
timestamp >= $__timeFrom
AND timestamp <= $__timeTo
ORDER BY
timestamp DESC;
Note: Time macros are automatically replaced with actual timestamps when the query is executed.
5. Using Interval Macros ($__interval, $__interval_ms)
| Macro | Description | Example Value | Notes |
|---|---|---|---|
| $__interval | Auto-calculated interval for time series queries | 1m |
Used for dynamic grouping and aggregation in queries |
| $__interval_ms | Auto-calculated interval in milliseconds | 60000 |
Useful for precise time-based calculations in queries |
Example: Using $__interval in a SQL Query
SELECT
date_trunc('hour', time) AS hour,
avg(cpu_usage) AS avg_cpu
FROM
system_metrics
WHERE
time >= $__timeFrom() AND time <= $__timeTo()
GROUP BY
hour
ORDER BY
hour
Example: Using $__interval_ms in a SQL Query
SELECT
time_bucket('1m', time) AS bucket,
avg(cpu_usage) AS avg_cpu
FROM
system_metrics
WHERE
time >= $__timeFrom() AND time <= $__timeTo()
GROUP BY
bucket
ORDER BY
bucket
6. Formatting as Time Series
| Option | Values | Description |
|---|---|---|
| time_series | true |
Formats query results as time series data for Grafana visualization |
| time_column | timestamp |
Name of the column containing time values (default: time) |
| value_column | value |
Name of the column containing numeric values (default: value) |
| metric_column | metric |
Name of the column containing metric labels (optional) |
Example: Time Series Query
SELECT
timestamp AS time,
value AS metric_value,
label AS metric_label
FROM metrics_table
WHERE timestamp >= '2024-01-01'
ORDER BY timestamp
Note: Grafana expects time series data in a specific format with timestamps and numeric values for proper visualization.
7. Formatting as Table
| Format | Description | Usage |
|---|---|---|
| table | Displays query results in a standard tabular format | Default format for most queries |
| time_series | Formats results for time-series visualization | Used with time-based data and charting |
| logs | Displays query results as log entries | Best for log data analysis and filtering |
| trace | Formats results for trace visualization | Used with distributed tracing systems like Jaeger or Zipkin |
Example: Using Table Format in SQL Query
SELECT
"timestamp",
"metric_name",
"value"
FROM
"metrics_table"
WHERE
"timestamp" >= now() - 1h
ORDER BY
"timestamp"
Note: The table format is ideal for displaying structured data with multiple columns and rows, making it easy to analyze and filter results.
8. Using Aggregate Functions
| Function | Description | Example |
|---|---|---|
| COUNT() | Counts the number of rows or non-NULL values | SELECT COUNT(*) FROM logs; |
| SUM() | Returns the sum of numeric values | SELECT SUM(bytes) FROM network_logs; |
| AVG() | Calculates the average of numeric values | SELECT AVG(cpu_usage) FROM system_metrics; |
| MIN() | Returns the minimum value in a set | SELECT MIN(temp_celsius) FROM weather_data; |
| MAX() | Returns the maximum value in a set | SELECT MAX(memory_usage_mb) FROM app_stats; |
| GROUP_CONCAT() | Concatenates values from multiple rows into one string | SELECT GROUP_CONCAT(tag SEPARATOR ', ') FROM events; |
Note: Aggregate functions are often used with
GROUP BY to summarize data by categories or time ranges.9. Using Window Functions
| Function | Description | Example |
|---|---|---|
| ROW_NUMBER() | Assigns a unique sequential number to each row | SELECT ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num, name, salary FROM employees; |
| RANK() | Assigns ranks, with gaps for ties | SELECT RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS rank, name, salary FROM employees; |
| DENSE_RANK() | Assigns ranks without gaps for ties | SELECT DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank, name, salary FROM employees; |
| LAG() | Accesses previous row's value in partition | SELECT name, salary, LAG(salary, 1) OVER (ORDER BY hire_date) AS prev_salary FROM employees; |
| LEAD() | Accesses next row's value in partition | SELECT name, salary, LEAD(salary, 1) OVER (ORDER BY hire_date) AS next_salary FROM employees; |
| SUM() OVER() | Calculates cumulative sum | SELECT name, salary, SUM(salary) OVER (ORDER BY hire_date) AS cumulative_salary FROM employees; |
Example: Calculating Running Totals
SELECT
date,
revenue,
SUM(revenue) OVER (ORDER BY date) AS running_total
FROM sales_data;
Example: Ranking by Department
SELECT
employee_id,
department,
salary,
RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dept_rank
FROM employee_salary;
Note: Window functions must be used with
OVER() clause. The PARTITION BY clause is optional and defines groups within the result set.10. Handling NULL Values
| Function | Description | Example |
|---|---|---|
| ISNULL() | Returns 1 if the expression is NULL, otherwise 0 | SELECT ISNULL(column_name) FROM table_name; |
| COALESCE() | Returns the first non-NULL value from a list of expressions | SELECT COALESCE(column1, column2, 'default') FROM table_name; |
| IFNULL() | MySQL-specific function; returns the second argument if the first is NULL | SELECT IFNULL(column_name, 'N/A') FROM table_name; |
| ISNULL() with CASE | Use CASE to handle NULLs in a more complex logic | SELECT CASE WHEN column_name IS NULL THEN 'Missing' ELSE column_name END FROM table_name; |
Note: In Grafana, when querying databases with NULL values, it's important to handle them explicitly in your queries to avoid unexpected results or errors.
11. Optimizing Query Performance
| Technique | Description | Impact |
|---|---|---|
| Use indexes | Ensure columns used in WHERE, JOIN, and ORDER BY clauses are indexed. | Significant performance improvement |
| Limit result sets | Use LIMIT to restrict the number of rows returned by queries. | Reduces memory usage and query time |
| Avoid SELECT * | Specify only required columns instead of retrieving all fields. | Improves speed and reduces bandwidth |
| Filter early | Apply WHERE conditions as early as possible in the query. | Reduces dataset size for further processing |
| Use EXPLAIN | Run EXPLAIN to analyze query execution plan and identify bottlenecks. | Helps understand how queries are executed |
Example: Using LIMIT to optimize results
SELECT * FROM logs
WHERE timestamp >= '2024-01-01'
ORDER BY timestamp DESC
LIMIT 100;
Example: Filtering early to reduce dataset size
SELECT count(*) FROM events
WHERE event_type = 'user_login'
AND timestamp >= '2024-01-01'
AND status = 'success';
Note: Grafana's query editor can show query execution times, helping identify slow queries.
Key Tips for Query Optimization
- Use indexes on frequently filtered columns
- Apply filters at the earliest stage of query execution
- Limit result sets to prevent excessive data transfer
- Avoid selecting unnecessary columns
- Use EXPLAIN to understand query plans
12. Using Query Inspector
Example: Inspecting a SQL Query in Grafana
SELECT
"timestamp",
"metric_name",
"value"
FROM
"metrics_table"
WHERE
"timestamp" >= $__timeFrom()
AND "timestamp" <= $__timeTo()
ORDER BY
"timestamp"
LIMIT 1000;
| Feature | Description | Usage |
|---|---|---|
| Query Inspector | Tool to inspect and debug SQL queries in Grafana | Access via the "Inspect" button in the query editor |
| Query Execution Log | Shows the actual SQL query executed with parameters | View in "Query" tab of the inspector |
| Response Data | Raw data returned by the query | View in "Data" tab of the inspector |
| Query Analysis | Information about query performance and execution plan | View in "Query" tab of the inspector |
Note: The Query Inspector is available in Grafana v9.0 and later.
Warning: Query Inspector may not show all query parameters for complex queries with multiple variables.