Skill: theboringhumane/data-analysis-synehq (version 1.1.3)

--- name: kole description: Execute queries against your databases using SQL, PostgreSQL commands, or natural language through SyneHQ's Kole platform. Use this skill whenever the user mentions databases, SQL queries, data analysis, PostgreSQL, querying data, database connections, checking database schemas, listing tables, or wants to explore or analyze data in their databases. Also use when user needs to sign up for SyneHQ or get their API credentials. IMPORTANT - Always use this skill when user asks about their data, even if they don't explicitly say "database" or "SQL". tools: No special tools required - uses MCP server compatibility: Requires synehq-kole MCP server to be configured version: 1.1.2 requires_mcp: package: "@synehq/kole-mcp" source: "https://github.com/synehq/kole-mcp" npm: "https://www.npmjs.com/package/@synehq/kole-mcp" install: "npm install -g @synehq/kole-mcp" min_version: "1.0.0" requires_env: - name: SYNEHQ_API_KEY required: true description: "SyneHQ API authentication key. Get from https://data.synehq.com/dashboard → Settings → API Keys" security: "Grants access to query connected databases. Treat as sensitive credential. Do not commit to version control." - name: SYNEHQ_CONNECTION_ID required: false description: "Default database connection ID. Can be overridden per-query. Get from https://data.synehq.com/dashboard → Connections" security: "Optional. If not set, must provide connectionId with each query." install_steps: - "Install MCP server: npm install -g @synehq/kole-mcp" - "Get SyneHQ account: curl https://data.synehq.com/api/auth-info" - "Sign up at: https://data.synehq.com/signup" - "Get API key from: https://data.synehq.com/dashboard → Settings → API Keys" - "Configure connection at: https://data.synehq.com/dashboard → Connections" - "Set environment variables: SYNEHQ_API_KEY and SYNEHQ_CONNECTION_ID (optional)" - "Add MCP server to .mcp.json or claude_desktop_config.json (see documentation below)" security_warnings: - "This MCP server can execute arbitrary SQL queries and DDL statements (CREATE, ALTER, DROP) against connected databases." - "Use least-privilege, read-only database credentials when possible for testing and exploration." - "For production use, ensure database user has only necessary permissions." - "All queries are logged for audit purposes with optional userId tracking." - "Test on non-production databases first to understand query capabilities and impacts." - "API key grants access to all databases connected to your SyneHQ account." - "Review MCP package source code at https://github.com/synehq/kole-mcp before installation." audit_logging: "All queries logged server-side with timestamps, user IDs (if provided), and connection details. Access logs at https://data.synehq.com/dashboard → Audit Logs" --- # kole - Query Your Data with SyneHQ Execute SQL queries, PostgreSQL commands, and natural language questions against your databases through SyneHQ's intelligent data platform. ## ⚠️ IMPORTANT: Prerequisites **This skill requires the SyneHQ Kole MCP server to be installed and configured.** ### Install the MCP Server ```bash # Install via npm npm install -g @synehq/kole-mcp # Or from source git clone https://github.com/synehq/kole-mcp.git cd kole-mcp npm install && npm run build ``` ### Configure the MCP Server Create `.mcp.json` in your project: ```json { "synehq-kole": { "command": "npx", "args": ["-y", "@synehq/kole-mcp@latest"], "env": { "SYNEHQ_API_KEY": "${SYNEHQ_API_KEY}", "SYNEHQ_CONNECTION_ID": "${SYNEHQ_CONNECTION_ID}" } } } ``` Or if you have it installed globally: ```json { "synehq-kole": { "command": "synehq-kole-mcp", "env": { "SYNEHQ_API_KEY": "${SYNEHQ_API_KEY}", "SYNEHQ_CONNECTION_ID": "${SYNEHQ_CONNECTION_ID}" } } } ``` ### Set Environment Variables ```bash export SYNEHQ_API_KEY="your_api_key" export SYNEHQ_CONNECTION_ID="your_connection_id" ``` **Without the MCP server installed, this skill will not work!** The skill provides instructions and patterns, but the actual query execution happens through the MCP server's tools. --- ## What is Kole? Kole is SyneHQ's data query platform that enables you to: - Execute SQL queries against connected databases - Use PostgreSQL-specific commands and DDL operations - Ask questions in natural language and get structured results - Discover database schemas and metadata - Manage multiple database connections - Test connections before querying ## Quick Start ### First Time User? If the user doesn't have a SyneHQ account yet, use `get_auth_info` to get signup instructions: ```javascript get_auth_info() // Returns: signup URL, login URL, dashboard URL, setup instructions ``` ### Check Available Connections Always start by listing available connections: ```javascript get_connections() // Shows all database connections with IDs, names, types, and status ``` ### Test Connection Before running queries, verify the connection works: ```javascript test_connection({ connectionId: "conn_abc123" // optional if SYNEHQ_CONNECTION_ID env var is set }) ``` ## Core Operations ### 1. Execute SQL Queries **Simple SELECT:** ```javascript execute_query({ query: "SELECT * FROM users WHERE created_at > '2024-01-01' LIMIT 10" }) ``` **With options:** ```javascript execute_query({ query: "SELECT * FROM large_table", connectionId: "prod-db", limit: 100, timeout: 60000, // 60 seconds userId: "analyst_1" // for audit tracking }) ``` ### 2. PostgreSQL Mode (psql commands) Enable PostgreSQL-specific commands with `psql: true`: **List all tables:** ```javascript execute_query({ query: "\\dt", psql: true }) ``` **Describe table structure:** ```javascript execute_query({ query: "\\d+ users", psql: true }) ``` **DDL Operations:** ```javascript execute_query({ query: "CREATE TABLE analytics_cache (id SERIAL PRIMARY KEY, query_hash VARCHAR(64), result JSONB)", psql: true }) ``` ### 3. Natural Language Queries Ask questions in plain English: ```javascript execute_query({ query: "Show me the top 10 customers by revenue this month" }) execute_query({ query: "How many active users signed up last week?" }) execute_query({ query: "What are the best-selling products?" }) ``` ### 4. Database Metadata Discovery **List all tables:** ```javascript get_tables({ database: "production", // optional filter schema: "public" // optional filter }) ``` **Get detailed table schema:** ```javascript get_table_schema({ database: "production", schema: "public", table: "users" }) // Returns: columns, types, constraints, indexes ``` ## Common Query Patterns See `references/query-patterns.md` for detailed examples. Here are the most common: ### Data Exploration ```sql -- Quick table overview SELECT * FROM users LIMIT 5; -- Column statistics SELECT COUNT(*) as total_rows, COUNT(DISTINCT email) as unique_emails, MIN(created_at) as earliest_user, MAX(created_at) as latest_user FROM users; ``` ### Business Analytics ```sql -- Monthly revenue SELECT DATE_TRUNC('month', order_date) as month, COUNT(*) as order_count, SUM(total_amount) as revenue FROM orders WHERE order_date >= '2024-01-01' GROUP BY month ORDER BY month DESC; -- Customer lifetime value SELECT user_id, COUNT(*) as order_count, SUM(total_amount) as lifetime_value FROM orders GROUP BY user_id ORDER BY lifetime_value DESC LIMIT 20; ``` ### Data Quality Checks ```sql -- Find NULL values SELECT COUNT(*) as total_rows, COUNT(*) FILTER (WHERE email IS NULL) as null_emails, COUNT(*) FILTER (WHERE phone IS NULL) as null_phones FROM users; -- Find duplicates SELECT email, COUNT(*) as duplicate_count FROM users GROUP BY email HAVING COUNT(*) > 1; ``` ## Workflow Patterns ### Pattern 1: Data Discovery Workflow When user wants to explore their data: 1. **List connections** → `get_connections()` 2. **Test connection** → `test_connection()` 3. **List tables** → `get_tables()` 4. **Inspect schema** → `get_table_schema()` 5. **Query data** → `execute_query()` ### Pattern 2: Analytics Workflow When user wants to analyze data: 1. **Understand structure** → `get_table_schema()` 2. **Sample data** → `SELECT * FROM table LIMIT 10` 3. **Run aggregations** → Complex SELECT with GROUP BY 4. **Iterate based on results** ### Pattern 3: PostgreSQL Admin Workflow When user needs database operations: 1. **Check current state** → `\dt`, `\d+ table_name` (psql mode) 2. **Execute DDL** → CREATE, ALTER, DROP (psql mode) 3. **Verify changes** → Check with `\dt` or queries 4. **Create indexes** → CREATE INDEX (psql mode) ## Best Practices ### 1. Always Start with Discovery ```javascript // Step 1: List connections get_connections() // Step 2: Test the connection test_connection({ connectionId: "chosen_connection" }) // Step 3: Discover tables get_tables() // Step 4: Understand schema get_table_schema({ database: "db", schema: "public", table: "users" }) // Step 5: Query safely with LIMIT execute_query({ query: "SELECT * FROM users LIMIT 10" }) ``` ### 2. Use LIMIT for Exploration Always use LIMIT when exploring large tables: ```sql -- Good: Limited results SELECT * FROM large_table LIMIT 10 -- Bad: Might return millions of rows SELECT * FROM large_table ``` ### 3. Leverage PostgreSQL Features (psql mode) ```sql -- JSON operations SELECT data->>'name' as name FROM json_table -- Array operations SELECT * FROM users WHERE tags @> ARRAY['premium'] -- Window functions SELECT user_id, order_date, total_amount, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY order_date DESC) as order_rank FROM orders ``` ### 4. Handle Errors Gracefully ```javascript // Always test connection first const connection = await test_connection() if (connection.success) { // Then run your query const result = await execute_query({ query: "SELECT * FROM users LIMIT 10" }) } ``` ### 5. Use User IDs for Tracking Track queries with user IDs for audit logs: ```javascript execute_query({ query: "SELECT * FROM sensitive_table", userId: "user_12345" // Shows up in audit logs }) ``` ### 6. Set Appropriate Timeouts For complex queries, increase timeout: ```javascript execute_query({ query: "SELECT * FROM orders JOIN order_items USING(order_id)", timeout: 120000 // 2 minutes }) ``` ## Troubleshooting ### Connection Issues **Symptom:** "Connection failed" or "Invalid credentials" **Solution:** 1. Test connection: `test_connection()` 2. List connections to verify ID: `get_connections()` 3. Check credentials in SyneHQ dashboard 4. Verify database is accessible ### Query Timeouts **Symptom:** Query takes too long and times out **Solution:** ```javascript execute_query({ query: "your_complex_query", limit: 100, // Limit result size timeout: 120000 // Increase timeout to 2 minutes }) ``` ### Large Result Sets **Symptom:** Too much data returned **Solution:** ```javascript // Use LIMIT and OFFSET for pagination execute_query({ query: "SELECT * FROM users ORDER BY id LIMIT 100 OFFSET 0", limit: 100 }) ``` ### No API Key **Symptom:** "SYNEHQ_API_KEY environment variable is required" **Solution:** 1. Get signup info: `get_auth_info()` 2. Sign up at https://data.synehq.com/signup 3. Get API key from Settings → API Keys 4. Set environment variable: `export SYNEHQ_API_KEY="your_key"` ## Environment Variables | Variable | Required | Default | Purpose | |----------|----------|---------|---------| | `SYNEHQ_API_KEY` | ✅ Yes | - | Authentication | | `SYNEHQ_CONNECTION_ID` | ⚠️ Recommended | - | Default database connection | | `SYNEHQ_BASE_URL` | ❌ No | https://cosmos.synehq.com | API endpoint | | `SYNEHQ_DATA_URL` | ❌ No | https://data.synehq.com | Data API endpoint | ## Advanced Features ### Multi-Connection Scenarios Query multiple databases in the same session: ```javascript // Production database execute_query({ query: "SELECT COUNT(*) as prod_users FROM users", connectionId: "prod-db" }) // Staging database execute_query({ query: "SELECT COUNT(*) as staging_users FROM users", connectionId: "staging-db" }) // Analytics database execute_query({ query: "SELECT * FROM daily_metrics WHERE date >= CURRENT_DATE - 30", connectionId: "analytics-db" }) ``` ### Query Performance Analysis Use EXPLAIN to analyze query performance: ```javascript execute_query({ query: "EXPLAIN ANALYZE SELECT * FROM users WHERE email LIKE '%@gmail.com'", psql: true }) ``` ### Database Maintenance Check table sizes and run maintenance: ```javascript // Check table sizes execute_query({ query: ` SELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as size FROM pg_tables WHERE schemaname = 'public' ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC `, psql: true }) // Vacuum table execute_query({ query: "VACUUM ANALYZE users", psql: true }) ``` ## Security Notes - API keys are transmitted via secure headers (never in URLs) - All communication over HTTPS - User IDs tracked for audit logs - Connection credentials managed server-side - Never expose API keys in client code - Use read-only database users when possible ## Resources - **Signup**: https://data.synehq.com/signup - **Login**: https://data.synehq.com/login - **Dashboard**: https://data.synehq.com/dashboard - **Documentation**: https://docs.synehq.com - **Support**: [REDACTED_SECRET] ## Quick Reference ### Get Started ```javascript get_auth_info() // Get signup/login URLs get_connections() // List all connections test_connection() // Test connection ``` ### Query Data ```javascript execute_query({ query: "SELECT * FROM users LIMIT 10" }) execute_query({ query: "\\dt", psql: true }) // PostgreSQL mode execute_query({ query: "Show me top customers" }) // Natural language ``` ### Discover Schema ```javascript get_tables() get_table_schema({ database: "db", schema: "schema", table: "table" }) ``` --- **Built with ❤️ by SyneHQ** | Query your data, your way.

path: README.md
content: # Kole Skill for Claude Code

Official Claude Code skill for SyneHQ Kole - query your databases with SQL, PostgreSQL, and natural language.

## ⚠️ Important: MCP Server Required

**This skill only works if you have the SyneHQ Kole MCP server installed and running.**

The skill provides query patterns and best practices, but actual database queries are executed through the MCP server's tools. You must install the MCP server first.

### Install MCP Server First

```bash
# Install the MCP server
npm install -g @synehq/kole-mcp

# Configure environment
export SYNEHQ_API_KEY="your_api_key"
export SYNEHQ_CONNECTION_ID="your_connection_id"

# Create .mcp.json in your project
# See main README for configuration details
```

## Skill Installation (After MCP Server Setup)

### Option 1: Copy to Skills Directory (Recommended)

```bash
# Copy the skill to your Claude skills directory
cp -r skill ~/.claude/skills/kole
```

### Option 2: Symlink for Development

```bash
# Symlink for automatic updates during development
ln -s $(pwd)/skill ~/.claude/skills/kole
```

### Option 3: NPM Package (After Publishing)

```bash
# Install the npm package globally
npm install -g @synehq/kole-mcp

# The skill will be available at:
# $(npm root -g)/@synehq/kole-mcp/skill/

# Copy or symlink it
cp -r $(npm root -g)/@synehq/kole-mcp/skill ~/.claude/skills/kole
```

## Verification

After installation, restart Claude Code and verify the skill is loaded:

```bash
# The skill should appear in your available skills
# You can invoke it by asking Claude about databases or SQL
```

## Usage

Once installed, Claude will automatically use this skill when you ask about:

- Database queries
- SQL operations
- PostgreSQL commands
- Data analysis
- Database connections
- Table schemas
- Data exploration

### Example Prompts

```
"Show me all my database connections"
"List the tables in my production database"
"Get the top 10 customers by revenue"
"What's the schema of the users table?"
"Run this SQL: SELECT * FROM orders WHERE created_at > '2024-01-01'"
```

## Skill Structure

```
skill/
├── SKILL.md              # Main skill file
├── README.md             # This file
├── references/
│   └── query-patterns.md # SQL query pattern reference
├── examples/
│   └── (future examples)
└── scripts/
    └── (future helper scripts)
```

## Prerequisites

This skill requires the SyneHQ Kole MCP server to be configured. See the main README for setup instructions.

## Development

To modify the skill:

1. Edit `SKILL.md` (main skill instructions)
2. Add reference materials to `references/`
3. Add examples to `examples/`
4. Add helper scripts to `scripts/`
5. Test with Claude Code
6. Commit changes

## Frontmatter Fields

The `SKILL.md` file uses YAML frontmatter:

- `name`: Skill identifier (used for invocation)
- `description`: When to trigger, what it does (IMPORTANT for Claude's decision to use the skill)
- `tools`: Required tools (none for this skill - uses MCP)
- `compatibility`: Prerequisites
- `version`: Skill version (matches package version)

## License

MIT - see LICENSE file in root directory

## Support

- **GitHub**: https://github.com/synehq/kole-mcp
- **Email**: [REDACTED_SECRET]
- **Docs**: https://docs.synehq.com


path: references/query-patterns.md
content: # Query Patterns Reference

Comprehensive collection of SQL query patterns for common use cases with SyneHQ Kole.

## Table of Contents

- [Data Exploration](#data-exploration)
- [Business Analytics](#business-analytics)
- [Customer Analysis](#customer-analysis)
- [Time-Series Analysis](#time-series-analysis)
- [Data Quality](#data-quality)
- [Performance Optimization](#performance-optimization)
- [PostgreSQL Specific](#postgresql-specific)

## Data Exploration

### Quick Table Samples

```sql
-- Get first 10 rows
SELECT * FROM users LIMIT 10;

-- Get random sample
SELECT * FROM users ORDER BY RANDOM() LIMIT 100;

-- Get recent records
SELECT * FROM orders 
ORDER BY created_at DESC 
LIMIT 20;
```

### Column Statistics

```sql
-- Basic statistics
SELECT 
  COUNT(*) as total_rows,
  COUNT(DISTINCT email) as unique_emails,
  COUNT(*) FILTER (WHERE email IS NULL) as null_emails,
  MIN(created_at) as earliest_record,
  MAX(created_at) as latest_record
FROM users;

-- Numeric column stats
SELECT 
  COUNT(*) as count,
  AVG(total_amount) as avg_amount,
  MIN(total_amount) as min_amount,
  MAX(total_amount) as max_amount,
  STDDEV(total_amount) as stddev_amount,
  PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY total_amount) as median_amount
FROM orders;
```

### Value Distribution

```sql
-- Count by category
SELECT 
  status,
  COUNT(*) as count,
  ROUND(100.0 * COUNT(*) / SUM(COUNT(*)) OVER (), 2) as percentage
FROM orders
GROUP BY status
ORDER BY count DESC;

-- Top values
SELECT 
  country,
  COUNT(*) as user_count
FROM users
GROUP BY country
ORDER BY user_count DESC
LIMIT 10;
```

## Business Analytics

### Revenue Analysis

```sql
-- Daily revenue
SELECT 
  DATE(order_date) as date,
  COUNT(*) as order_count,
  SUM(total_amount) as daily_revenue,
  AVG(total_amount) as avg_order_value
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY DATE(order_date)
ORDER BY date DESC;

-- Monthly revenue with growth
SELECT 
  DATE_TRUNC('month', order_date) as month,
  SUM(total_amount) as revenue,
  LAG(SUM(total_amount)) OVER (ORDER BY DATE_TRUNC('month', order_date)) as prev_month_revenue,
  ROUND(
    ((SUM(total_amount) - LAG(SUM(total_amount)) OVER (ORDER BY DATE_TRUNC('month', order_date))) 
    / LAG(SUM(total_amount)) OVER (ORDER BY DATE_TRUNC('month', order_date)) * 100)::numeric, 
    2
  ) as growth_pct
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '12 months'
GROUP BY DATE_TRUNC('month', order_date)
ORDER BY month DESC;
```

### Product Analysis

```sql
-- Best selling products
SELECT 
  p.product_name,
  COUNT(oi.id) as units_sold,
  SUM(oi.quantity * oi.unit_price) as total_revenue,
  AVG(oi.quantity) as avg_quantity_per_order
FROM order_items oi
JOIN products p ON oi.product_id = p.id
WHERE oi.created_at >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY p.id, p.product_name
ORDER BY total_revenue DESC
LIMIT 20;

-- Product performance by category
SELECT 
  p.category,
  COUNT(DISTINCT oi.order_id) as order_count,
  COUNT(oi.id) as item_count,
  SUM(oi.quantity * oi.unit_price) as revenue
FROM order_items oi
JOIN products p ON oi.product_id = p.id
GROUP BY p.category
ORDER BY revenue DESC;
```

## Customer Analysis

### Customer Lifetime Value

```sql
-- CLV by customer
SELECT 
  u.id,
  u.email,
  u.name,
  COUNT(o.id) as total_orders,
  SUM(o.total_amount) as lifetime_value,
  AVG(o.total_amount) as avg_order_value,
  MIN(o.order_date) as first_order_date,
  MAX(o.order_date) as last_order_date,
  EXTRACT(DAY FROM NOW() - MAX(o.order_date)) as days_since_last_order
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.email, u.name
HAVING COUNT(o.id) > 0
ORDER BY lifetime_value DESC
LIMIT 50;
```

### Customer Segmentation

```sql
-- RFM segmentation
WITH rfm_calc AS (
  SELECT 
    user_id,
    EXTRACT(DAY FROM NOW() - MAX(order_date)) as recency_days,
    COUNT(*) as frequency,
    SUM(total_amount) as monetary
  FROM orders
  GROUP BY user_id
),
rfm_scores AS (
  SELECT 
    user_id,
    recency_days,
    frequency,
    monetary,
    NTILE(5) OVER (ORDER BY recency_days ASC) as r_score,
    NTILE(5) OVER (ORDER BY frequency DESC) as f_score,
    NTILE(5) OVER (ORDER BY monetary DESC) as m_score
  FROM rfm_calc
)
SELECT 
  user_id,
  recency_days,
  frequency,
  monetary,
  r_score || f_score || m_score as rfm_score,
  CASE 
    WHEN r_score >= 4 AND f_score >= 4 THEN 'Champions'
    WHEN r_score >= 3 AND f_score >= 3 THEN 'Loyal Customers'
    WHEN r_score >= 4 AND f_score <= 2 THEN 'Promising'
    WHEN r_score <= 2 AND f_score >= 3 THEN 'At Risk'
    WHEN r_score <= 2 AND f_score <= 2 THEN 'Lost'
    ELSE 'Others'
  END as customer_segment
FROM rfm_scores
ORDER BY monetary DESC;
```

### New vs Returning Customers

```sql
-- Customer type analysis
WITH first_orders AS (
  SELECT 
    user_id,
    MIN(order_date) as first_order_date
  FROM orders
  GROUP BY user_id
)
SELECT 
  DATE(o.order_date) as date,
  COUNT(CASE WHEN o.order_date::date = fo.first_order_date::date THEN 1 END) as new_customers,
  COUNT(CASE WHEN o.order_date::date > fo.first_order_date::date THEN 1 END) as returning_customers,
  SUM(CASE WHEN o.order_date::date = fo.first_order_date::date THEN o.total_amount ELSE 0 END) as new_customer_revenue,
  SUM(CASE WHEN o.order_date::date > fo.first_order_date::date THEN o.total_amount ELSE 0 END) as returning_customer_revenue
FROM orders o
JOIN first_orders fo ON o.user_id = fo.user_id
WHERE o.order_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY DATE(o.order_date)
ORDER BY date DESC;
```

## Time-Series Analysis

### Cohort Analysis

```sql
-- Monthly cohort retention
WITH cohorts AS (
  SELECT 
    user_id,
    DATE_TRUNC('month', MIN(order_date)) as cohort_month
  FROM orders
  GROUP BY user_id
),
monthly_activity AS (
  SELECT 
    c.cohort_month,
    DATE_TRUNC('month', o.order_date) as activity_month,
    COUNT(DISTINCT o.user_id) as active_users
  FROM cohorts c
  JOIN orders o ON c.user_id = o.user_id
  WHERE c.cohort_month >= '2024-01-01'
  GROUP BY c.cohort_month, DATE_TRUNC('month', o.order_date)
)
SELECT 
  cohort_month,
  activity_month,
  active_users,
  EXTRACT(MONTH FROM AGE(activity_month, cohort_month)) as months_since_first_order
FROM monthly_activity
ORDER BY cohort_month, activity_month;
```

### Moving Averages

```sql
-- 7-day moving average of revenue
SELECT 
  DATE(order_date) as date,
  SUM(total_amount) as daily_revenue,
  AVG(SUM(total_amount)) OVER (
    ORDER BY DATE(order_date)
    ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
  ) as seven_day_avg
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '60 days'
GROUP BY DATE(order_date)
ORDER BY date DESC;
```

### Trends and Growth

```sql
-- Year-over-year growth
SELECT 
  DATE_TRUNC('month', order_date) as month,
  SUM(total_amount) as revenue,
  LAG(SUM(total_amount), 12) OVER (ORDER BY DATE_TRUNC('month', order_date)) as same_month_last_year,
  ROUND(
    ((SUM(total_amount) - LAG(SUM(total_amount), 12) OVER (ORDER BY DATE_TRUNC('month', order_date))) 
    / LAG(SUM(total_amount), 12) OVER (ORDER BY DATE_TRUNC('month', order_date)) * 100)::numeric,
    2
  ) as yoy_growth_pct
FROM orders
GROUP BY DATE_TRUNC('month', order_date)
ORDER BY month DESC;
```

## Data Quality

### Find Missing Data

```sql
-- NULL values analysis
SELECT 
  COUNT(*) as total_rows,
  COUNT(*) FILTER (WHERE email IS NULL) as null_emails,
  COUNT(*) FILTER (WHERE phone IS NULL) as null_phones,
  COUNT(*) FILTER (WHERE address IS NULL) as null_addresses,
  COUNT(*) FILTER (WHERE city IS NULL) as null_cities,
  ROUND(100.0 * COUNT(*) FILTER (WHERE email IS NULL) / COUNT(*), 2) as null_email_pct,
  ROUND(100.0 * COUNT(*) FILTER (WHERE phone IS NULL) / COUNT(*), 2) as null_phone_pct
FROM users;
```

### Find Duplicates

```sql
-- Duplicate emails
SELECT 
  email,
  COUNT(*) as duplicate_count,
  ARRAY_AGG(id) as user_ids
FROM users
GROUP BY email
HAVING COUNT(*) > 1
ORDER BY duplicate_count DESC;

-- Duplicate orders (potential data issue)
SELECT 
  user_id,
  order_date,
  total_amount,
  COUNT(*) as duplicate_count
FROM orders
GROUP BY user_id, order_date, total_amount
HAVING COUNT(*) > 1;
```

### Data Freshness

```sql
-- Check when tables were last updated
SELECT 
  'users' as table_name,
  MAX(created_at) as latest_record,
  COUNT(*) as total_records,
  EXTRACT(DAY FROM NOW() - MAX(created_at)) as days_since_last_update
FROM users
UNION ALL
SELECT 
  'orders' as table_name,
  MAX(order_date) as latest_record,
  COUNT(*) as total_records,
  EXTRACT(DAY FROM NOW() - MAX(order_date)) as days_since_last_update
FROM orders
UNION ALL
SELECT 
  'products' as table_name,
  MAX(updated_at) as latest_record,
  COUNT(*) as total_records,
  EXTRACT(DAY FROM NOW() - MAX(updated_at)) as days_since_last_update
FROM products;
```

## Performance Optimization

### Query Analysis

```sql
-- Use EXPLAIN ANALYZE to see query plan
EXPLAIN ANALYZE
SELECT * FROM users 
WHERE email LIKE '%@gmail.com'
LIMIT 100;
```

### Index Usage

```sql
-- Check if indexes are being used
SELECT 
  schemaname,
  tablename,
  indexname,
  idx_scan as index_scans,
  idx_tup_read as tuples_read,
  idx_tup_fetch as tuples_fetched,
  pg_size_pretty(pg_relation_size(indexrelid)) as index_size
FROM pg_stat_user_indexes
ORDER BY idx_scan DESC;

-- Find unused indexes
SELECT 
  schemaname,
  tablename,
  indexname,
  idx_scan,
  pg_size_pretty(pg_relation_size(indexrelid)) as index_size
FROM pg_stat_user_indexes
WHERE idx_scan = 0
ORDER BY pg_relation_size(indexrelid) DESC;
```

### Table Sizes

```sql
-- Check table sizes
SELECT 
  schemaname,
  tablename,
  pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as total_size,
  pg_size_pretty(pg_relation_size(schemaname||'.'||tablename)) as table_size,
  pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename) - pg_relation_size(schemaname||'.'||tablename)) as index_size,
  pg_total_relation_size(schemaname||'.'||tablename) as size_bytes
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
```

## PostgreSQL Specific

### JSON Operations

```sql
-- Extract JSON fields
SELECT 
  id,
  data->>'name' as name,
  data->>'email' as email,
  data->'address'->>'city' as city
FROM json_table;

-- Query JSON arrays
SELECT 
  id,
  jsonb_array_elements_text(tags) as tag
FROM products
WHERE tags @> '["featured"]'::jsonb;
```

### Array Operations

```sql
-- Array contains
SELECT * FROM users 
WHERE tags @> ARRAY['premium', 'verified'];

-- Array overlap
SELECT * FROM users
WHERE tags && ARRAY['premium', 'vip'];

-- Unnest array
SELECT 
  user_id,
  unnest(tags) as tag
FROM users;
```

### Full-Text Search

```sql
-- Basic full-text search
SELECT 
  id,
  title,
  content,
  ts_rank(to_tsvector('english', content), query) as rank
FROM articles,
     to_tsquery('english', 'postgresql & database') query
WHERE to_tsvector('english', content) @@ query
ORDER BY rank DESC;
```

### Window Functions

```sql
-- Ranking within groups
SELECT 
  user_id,
  order_date,
  total_amount,
  ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY order_date DESC) as order_rank,
  RANK() OVER (PARTITION BY user_id ORDER BY total_amount DESC) as amount_rank,
  SUM(total_amount) OVER (PARTITION BY user_id) as user_total
FROM orders;

-- Running totals
SELECT 
  order_date,
  total_amount,
  SUM(total_amount) OVER (ORDER BY order_date) as running_total
FROM orders
ORDER BY order_date;
```

---

For more query examples, see the examples directory or visit https://docs.synehq.com


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)