Queries
![]()
Queries define the SQL statements you want to execute and monitor.
Purpose
Section titled “Purpose”Queries allow you to:
- Define SQL statements to extract data from your databases
- Use parameters for dynamic query values
- Create multi-step query workflows
- Reuse queries across multiple subscriptions
- Monitor query execution history
Use Cases
Section titled “Use Cases”- Data Validation (Primary): Alert when data violates business rules or integrity constraints
- Data Quality Checks: Detect NULL values, duplicates, orphaned records, invalid states
- Business Rule Enforcement: Trigger alerts when data doesn’t meet expected criteria
- Database Health (Secondary): Monitor database metrics for DBAs (table size, connections, performance)
- Compliance Reporting: Generate audit reports on schedules
Creating a Simple Query
Section titled “Creating a Simple Query”Step 1: Navigate to Queries
Section titled “Step 1: Navigate to Queries”- Log in to Beacon at the React UI (
/login) - Click Queries in the left navigation (
/queries) - Click Create New Query
Step 2: Fill Query Details
Section titled “Step 2: Fill Query Details”| Field | Description | Required | Example |
|---|---|---|---|
| Name | Descriptive query name | Yes | Daily Active Users |
| Description | Purpose of this query | No | Count users active in last 24 hours |
| Store Results | Save execution results | No | ✓ Checked |
Step 3: Add Query Step
Section titled “Step 3: Add Query Step”Every query has at least one step. Click Add Query Step:
| Field | Description | Required | Example |
|---|---|---|---|
| Step Name | Name for this step | Yes | Count Active Users |
| Project | Database to query | Yes | Select your project |
| SQL Query | SQL statement | Yes | See examples below |
| Order | Execution order | Yes | 1 (for first step) |
Simple query example:
SELECT COUNT(*) as active_usersFROM usersWHERE last_login >= NOW() - INTERVAL '24 hours'Step 4: Save Query
Section titled “Step 4: Save Query”Click Save to create the query.
Query Examples
Section titled “Query Examples”Monitor Table Growth
Section titled “Monitor Table Growth”PostgreSQL:
SELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size, pg_total_relation_size(schemaname||'.'||tablename) AS size_bytesFROM pg_tablesWHERE schemaname = 'public'ORDER BY size_bytes DESCLIMIT 10SQL Server:
SELECT t.NAME AS TableName, s.Name AS SchemaName, p.rows AS RowCount, CAST(ROUND((SUM(a.total_pages) * 8) / 1024.00, 2) AS NUMERIC(36, 2)) AS SizeMBFROM sys.tables tINNER JOIN sys.indexes i ON t.OBJECT_ID = i.object_idINNER JOIN sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_idINNER JOIN sys.allocation_units a ON p.partition_id = a.container_idLEFT OUTER JOIN sys.schemas s ON t.schema_id = s.schema_idWHERE t.is_ms_shipped = 0GROUP BY t.Name, s.Name, p.RowsORDER BY SizeMB DESCCheck Database Connections
Section titled “Check Database Connections”PostgreSQL:
SELECT datname, count(*) as connections, max(client_addr) as last_clientFROM pg_stat_activityWHERE datname IS NOT NULLGROUP BY datnameORDER BY connections DESCSQL Server:
SELECT DB_NAME(dbid) as DatabaseName, COUNT(dbid) as NumberOfConnections, loginame as LoginNameFROM sys.sysprocessesWHERE dbid > 0GROUP BY dbid, loginameORDER BY NumberOfConnections DESCMonitor Query Performance
Section titled “Monitor Query Performance”PostgreSQL (slow queries):
SELECT query, calls, mean_exec_time, max_exec_timeFROM pg_stat_statementsWHERE mean_exec_time > 1000 -- Queries slower than 1 secondORDER BY mean_exec_time DESCLIMIT 20Data Validation Examples (Primary Use Case)
Section titled “Data Validation Examples (Primary Use Case)”Check for NULL values in required fields:
SELECT id, email, usernameFROM usersWHERE email IS NULL OR username IS NULL OR created_at IS NULL-- Alert triggers if any rows returnedDetect orphaned records:
SELECT o.id, o.user_id, 'Orphaned order - user not found' as issueFROM orders oLEFT JOIN users u ON o.user_id = u.idWHERE u.id IS NULL-- Alert if orphaned orders existCheck for invalid state combinations:
SELECT id, status, payment_statusFROM ordersWHERE status = 'completed' AND payment_status != 'paid'-- Alert if completed orders are not paidDetect duplicate records:
SELECT email, COUNT(*) as duplicate_countFROM usersGROUP BY emailHAVING COUNT(*) > 1-- Alert if duplicate emails existBusiness rule violations:
SELECT id, total_amount, discount_amountFROM ordersWHERE discount_amount > total_amount-- Alert if discount exceeds total (invalid business rule)Referential integrity checks:
SELECT p.id, p.category_id, 'Invalid category reference' as issueFROM products pWHERE category_id NOT IN (SELECT id FROM categories)-- Alert if products reference non-existent categoriesQuery Parameters
Section titled “Query Parameters”Queries can include dynamic parameters using {{parameterName}} syntax.
Query with parameter:
SELECT COUNT(*) as user_countFROM usersWHERE created_at >= '{{start_date}}' AND created_at < '{{end_date}}'When creating a subscription, you’ll provide values for start_date and end_date.
Multi-Step Queries
Section titled “Multi-Step Queries”Create complex workflows by chaining multiple query steps.
Example: Cross-database data aggregation:
Step 1 - Query PostgreSQL database:
SELECT customer_id, SUM(amount) as totalFROM ordersWHERE created_at >= CURRENT_DATEGROUP BY customer_idStep 2 - Aggregate results:
SELECT COUNT(*) as customers_with_orders, SUM(total) as grand_total, AVG(total) as average_orderFROM @result1 -- References Step 1 resultsCross-database multi-step queries materialize intermediate results into an in-memory SQLite database, then run the final join there.
Managing Queries
Section titled “Managing Queries”View Queries
Section titled “View Queries”The Queries page shows:
- Query name and description
- Associated project
- Number of subscriptions using this query
- Last execution time
- Actions (Edit, Delete, Preview, View History)
Edit Query
Section titled “Edit Query”- Click Edit (pencil icon) on the query row
- Modify query details or SQL
- Test changes with Preview Execution
- Click Save
Delete Query
Section titled “Delete Query”- Click Delete (trash icon) on the query row
- Review impact message (shows affected subscriptions)
- Confirm deletion
Queries are archived (soft delete) and can be restored if needed.
Preview Query Execution
Section titled “Preview Query Execution”Test queries without scheduling:
- Open your query
- Click Preview Query Execution
- Review results in table format
- Check execution time
- Verify data is as expected
Query Best Practices
Section titled “Query Best Practices”Performance
Section titled “Performance”DO:
- ✓ Use indexes on frequently queried columns
- ✓ Limit result sets with
LIMITorTOP - ✓ Filter early with
WHEREclauses - ✓ Use appropriate data types
- ✓ Test performance on production-sized data
DON’T:
- ✗ Use
SELECT *(specify columns) - ✗ Query without indexes
- ✗ Use complex JOINs unnecessarily
- ✗ Return millions of rows
- ✗ Use cursors or loops
Security
Section titled “Security”DO:
- ✓ Use read-only database users
- ✓ Parameterize dynamic values
- ✓ Validate query results make sense
- ✓ Monitor execution history for anomalies
DON’T:
- ✗ Use admin/superuser accounts
- ✗ Include passwords or secrets in queries
- ✗ Allow SQL injection via parameters
- ✗ Query sensitive data unnecessarily
Reliability
Section titled “Reliability”DO:
- ✓ Set appropriate timeout values
- ✓ Handle NULL values in aggregations
- ✓ Use defensive SQL (COALESCE, NULLIF)
- ✓ Test edge cases (empty results, errors)
DON’T:
- ✗ Rely on unstable temp tables
- ✗ Use database-specific syntax if cross-db needed
- ✗ Forget to handle zero-row results
- ✗ Use non-deterministic functions without intent
Query Examples by Use Case
Section titled “Query Examples by Use Case”Application Monitoring
Section titled “Application Monitoring”Error rate (last hour):
SELECT error_type, COUNT(*) as error_count, MAX(created_at) as last_errorFROM error_logsWHERE created_at > NOW() - INTERVAL '1 hour'GROUP BY error_typeORDER BY error_count DESCSlow API endpoints:
SELECT endpoint, AVG(response_time_ms) as avg_response, MAX(response_time_ms) as max_response, COUNT(*) as request_countFROM api_logsWHERE created_at > NOW() - INTERVAL '1 hour'GROUP BY endpointHAVING AVG(response_time_ms) > 1000 -- Slower than 1 secondORDER BY avg_response DESCDatabase Health
Section titled “Database Health”Disk space usage (PostgreSQL):
SELECT pg_database.datname, pg_size_pretty(pg_database_size(pg_database.datname)) AS sizeFROM pg_databaseORDER BY pg_database_size(pg_database.datname) DESCIndex health (PostgreSQL):
SELECT schemaname, tablename, indexname, idx_scan, idx_tup_read, idx_tup_fetchFROM pg_stat_user_indexesWHERE idx_scan = 0 -- Unused indexesORDER BY pg_relation_size(indexrelid) DESCScheduled Reporting Queries
Section titled “Scheduled Reporting Queries”Queries designed for reports delivered as CSV/Excel attachments:
Daily sales report (complete dataset):
SELECT p.product_name, p.category, o.order_date, o.customer_id, c.customer_name, o.quantity, o.unit_price, o.total_amountFROM orders oJOIN products p ON o.product_id = p.idJOIN customers c ON o.customer_id = c.idWHERE DATE(o.created_at) = CURRENT_DATE - INTERVAL '1 day'ORDER BY o.total_amount DESC-- Full results sent as CSV attachment for Excel analysisWeekly user activity report:
SELECT u.username, u.email, COUNT(a.id) as login_count, MAX(a.created_at) as last_login, SUM(a.duration_minutes) as total_minutesFROM users uLEFT JOIN activity_logs a ON u.id = a.user_id AND a.created_at >= CURRENT_DATE - INTERVAL '7 days'GROUP BY u.id, u.username, u.emailORDER BY login_count DESC-- Delivered Monday mornings as CSV for management reviewMonthly financial summary:
SELECT DATE_TRUNC('month', created_at) as month, category, COUNT(*) as transaction_count, SUM(amount) as total_amount, AVG(amount) as average_amount, MIN(amount) as min_amount, MAX(amount) as max_amountFROM transactionsWHERE created_at >= DATE_TRUNC('month', CURRENT_DATE - INTERVAL '1 month') AND created_at < DATE_TRUNC('month', CURRENT_DATE)GROUP BY DATE_TRUNC('month', created_at), categoryORDER BY category-- Delivered first day of month with complete data for analysisTroubleshooting
Section titled “Troubleshooting”Query Syntax Errors
Section titled “Query Syntax Errors”Test in database client first:
# PostgreSQLpsql -h host -U user -d database -c "SELECT COUNT(*) FROM users"
# SQL Serversqlcmd -S server -U user -P password -d database -Q "SELECT COUNT(*) FROM users"
# MySQLmysql -h host -u user -p database -e "SELECT COUNT(*) FROM users"Query Timeout
Section titled “Query Timeout”If queries time out:
- Check query execution plan for performance issues
- Add indexes to improve performance
- Increase timeout in subscription settings
- Simplify query or reduce result set
Permission Errors
Section titled “Permission Errors”Verify user has SELECT permission:
-- PostgreSQLSELECT grantee, table_schema, table_name, privilege_typeFROM information_schema.table_privilegesWHERE grantee = 'your_user';Related Documentation
Section titled “Related Documentation”- Subscriptions - Schedule query execution
- Data Sources - Database connection and project management
- Data Migration - Cross-database ETL using the same query layer