# introduction.mdx
## Welcome to WhoDB

A lightweight, modern database management tool for exploring, querying, and visualizing your data

# Welcome to WhoDB

WhoDB is a powerful, intuitive database management tool designed to simplify how you interact with your databases. Whether you're a developer, data analyst, or database administrator, WhoDB provides a streamlined interface for exploring schemas, managing data, and visualizing relationships.

![WhoDB Login Page](/images/01-login-page.png)

## What is WhoDB?

WhoDB is an open-source database management solution that combines the power of traditional database clients with modern visualization and query capabilities. It provides a unified interface for working with multiple database types while maintaining a focus on simplicity and user experience.

Connect to PostgreSQL, MySQL, SQLite, MongoDB, Redis, and more with a single interface

Explore your database structure with interactive graph visualizations showing table relationships

Write and execute SQL queries with syntax highlighting, history tracking, and result visualization

View, add, edit, and delete records with an intuitive spreadsheet-like interface

## Key Features

WhoDB supports multiple database engines out of the box. Connect to your databases using standard connection strings or configure advanced options like SSL certificates, SSH tunnels, and connection pooling.

Navigate your database structure with ease. The Schema Explorer shows all your databases, schemas, tables, views, and stored procedures in an organized tree view. Click any table to instantly view its structure and data.

View and edit data in a familiar spreadsheet-like interface. Sort columns, filter rows, search across all fields, and perform bulk operations with just a few clicks.

Write SQL queries in a powerful editor with syntax highlighting, auto-completion, and multi-cell support. Execute queries, view results, and save frequently used queries for later use.

Visualize your database schema as an interactive graph. See table relationships, foreign keys, and data flow at a glance. Pan, zoom, and click nodes to explore your database structure visually.

Generate realistic test data for development and testing. Configure the number of rows, choose between append or overwrite modes, and let WhoDB populate your tables with sample data.

Export your data in multiple formats including CSV, Excel, JSON, and SQL. Choose specific columns, apply filters, and select custom delimiters for maximum flexibility.

## Why Choose WhoDB?

WhoDB's clean interface eliminates clutter and focuses on what matters most: your data. No complex menus or hidden features—everything is accessible and easy to find.

Built with performance in mind, WhoDB loads quickly and handles large datasets efficiently. Query results appear instantly, and the interface remains responsive even with complex operations.

Designed for developers by developers. WhoDB includes features like query history, keyboard shortcuts, syntax highlighting, and multi-cell query execution that streamline your workflow.

WhoDB is fully open source, allowing you to audit the code, contribute improvements, and customize it to your needs. Join our community on GitHub to get involved.

## Use Cases

WhoDB is perfect for a wide range of database management tasks

**Development & Testing**
- Quickly inspect database state during development
- Generate test data for your applications
- Debug SQL queries and view results instantly
- Explore schema changes and relationships

**Data Analysis**
- Export data for further analysis in Excel or other tools
- Run ad-hoc queries to answer business questions
- Filter and sort large datasets to find specific records
- Visualize table relationships to understand data flow

**Database Administration**
- Monitor table structures and indexes
- Manage user data and permissions
- Backup and restore data using export/import
- Maintain data quality with filtering and validation

**Learning & Education**
- Explore database structures to learn SQL
- Visualize relationships between tables
- Practice queries in a safe environment
- Understand database design patterns

## Getting Started

Learn how to install WhoDB on your system

Connect to your first database

Get up and running in minutes

WhoDB is under active development. New features and improvements are added regularly. Check our GitHub repository for the latest updates and release notes.

## Community & Support

Join our growing community of users and contributors:

- **GitHub**: Report issues, request features, and contribute code
- **Documentation**: Comprehensive guides and API references
- **Community Forum**: Ask questions and share tips with other users

Ready to start exploring your databases? Continue to the **Installation** page to get WhoDB running on your system.

---


# why-whodb.mdx
## Why Choose WhoDB?

Discover why WhoDB is the best lightweight database management tool for developers, data analysts, and teams. Fast, intuitive, and powerful.

# Why Choose WhoDB?

**WhoDB is the modern database management tool that developers actually want to use.** Unlike bloated legacy tools, WhoDB delivers a lightning-fast, intuitive experience that makes working with databases enjoyable instead of frustrating.

## The Problem with Traditional Database Tools

Traditional database management tools like phpMyAdmin, pgAdmin, and others were built for a different era. They're:

- **Slow and Resource-Intensive**: Heavy desktop applications that consume gigabytes of RAM
- **Cluttered Interfaces**: Dozens of menus, toolbars, and options that hide what you actually need
- **Difficult to Learn**: Steep learning curves that waste hours of productive time
- **Poor Visualization**: Limited or non-existent schema visualization capabilities
- **Inconsistent Experience**: Different tools for different databases, each with unique quirks

The average developer wastes **2-3 hours per week** wrestling with database tools instead of building features.

## The WhoDB Difference

WhoDB was built from the ground up to solve these problems with a focus on **speed, simplicity, and developer experience**.

### 1. Lightning Fast Performance

Load in under 1 second - no more waiting for heavy applications to launch

Query results appear instantly, even with large datasets

Uses 90% less memory than traditional database tools

No installation required - access from any device with a browser

### 2. Beautifully Simple Interface

WhoDB's clean, modern interface eliminates clutter and puts your data front and center.

![WhoDB Interface](/images/09-data-view-users-table.png)

New users become productive in minutes, not hours. No training required.

Everything you need is exactly where you expect it. Sidebar navigation, context menus, and keyboard shortcuts make navigating databases effortless.

- **Clear hierarchy**: Database → Schema → Table
- **Quick search**: Find any table or column instantly
- **Smart breadcrumbs**: Always know where you are

Work with database data the same way you work with spreadsheets. Click to sort, right-click for actions, search across all columns.

- **Familiar interface**: If you can use Excel, you can use WhoDB
- **Inline editing**: Click cells to edit directly
- **Bulk operations**: Select multiple rows for batch actions

Write SQL with modern features like syntax highlighting, auto-completion, and query history.

- **Syntax highlighting**: Color-coded SQL for easy reading
- **Auto-completion**: Intelligent suggestions as you type
- **Query history**: Reuse and clone previous queries
- **Multi-cell support**: Organize queries into logical sections

### 3. Visual Schema Explorer

![Graph Visualization](/images/24-graph-view-schema-topology.png)

Understanding database relationships has never been easier. WhoDB's interactive graph view shows your entire schema at a glance.

See all tables and relationships in a visual diagram - no need to read through pages of schema definitions

Click any table to see its columns, indexes, and foreign keys. Follow relationships to explore data flow

Identify hub tables, isolated tables, and complex relationship patterns instantly

Export schema diagrams for team documentation and onboarding

### 4. Multi-Database Support

**One tool for all your databases.** Why juggle multiple database tools when WhoDB supports them all?

Full support for PostgreSQL 10+

Complete MySQL 5.7+ compatibility

Local and embedded databases

NoSQL document databases

Key-value data stores

Additional databases in development

### 5. Developer-Friendly Features

WhoDB includes features that developers actually use every day:

Generate realistic test data in seconds. Perfect for development and testing environments.

- Configure row counts (1 to 10,000+)
- Smart data type handling
- Append or overwrite modes
- Realistic fake data for all column types

Export data in the format you need:

- **CSV**: For Excel and spreadsheet tools
- **Excel (.xlsx)**: Native Excel format with formatting
- **JSON**: For APIs and applications
- **SQL**: INSERT statements for backups and migrations

Configure delimiters, select specific columns, and apply filters before export.

Build complex WHERE conditions with a visual interface:

- Multiple conditions with AND/OR logic
- All standard operators (`=`, `!=`, `>`, `<`, `>=`, `<=`)
- Date range filtering
- Text pattern matching
- NULL value handling

Every query is automatically saved. Clone and modify previous queries instead of rewriting them from scratch.

- **Searchable history**: Find queries by text or date
- **One-click cloning**: Reuse successful queries
- **Performance tracking**: See execution times
- **Export history**: Save frequently-used queries

## Who Uses WhoDB?

### Developers

 **"Finally, a database tool that doesn't get in my way."**

Developers love WhoDB's speed and keyboard-driven workflow. Connect to any database, run queries, inspect data, and move on - all in seconds.

**Perfect for:**
- Local development workflows
- Debugging production issues
- API development and testing
- Schema exploration for new projects

### Data Analysts

 **"I can explore data and export results faster than ever before."**

Analysts appreciate WhoDB's filtering, sorting, and export capabilities. Build complex queries, filter results, and export to Excel - all without leaving the browser.

**Perfect for:**
- Ad-hoc data analysis
- Report generation
- Data quality checks
- Business intelligence queries

### Database Administrators

 **"Managing multiple databases has never been easier."**

DBAs use WhoDB to monitor schemas, manage data, and perform administrative tasks across multiple database servers from a single interface.

**Perfect for:**
- Schema management
- Data maintenance
- Performance monitoring
- Multi-database administration

### QA Engineers

 **"Mock data generation saves me hours every sprint."**

QA teams use WhoDB to generate test data, verify database states, and debug test failures quickly.

**Perfect for:**
- Test data generation
- Database state verification
- Test debugging
- Test automation support

## Comparison with Alternatives

See how WhoDB compares to phpMyAdmin

See how WhoDB compares to pgAdmin

See how WhoDB compares to DBeaver

See how WhoDB compares to TablePlus

## Real-World Benefits

**Spend less time fighting tools, more time building features.**

WhoDB's speed and intuitive interface save developers an average of 2-3 hours per week compared to traditional database tools.

**Visual schema exploration prevents relationship mistakes.**

Understanding database relationships visually reduces data integrity errors by catching relationship issues before they become problems.

**Get more done with fewer context switches.**

One unified tool for all databases means less context switching and faster workflow.

**New team members become productive immediately.**

WhoDB's intuitive interface requires no training. New developers can start exploring databases on day one.

## Open Source & Community

WhoDB is **fully open source** under a permissive license, giving you:

Audit the code and ensure it meets your security requirements

Customize and extend WhoDB for your specific needs

Self-host without dependencies on external services

Join thousands of developers using WhoDB worldwide

## Enterprise Edition

For teams that need advanced features, **WhoDB Enterprise Edition** offers:

- **Additional database support**: Oracle, SQL Server, Cassandra, and more
- **Team collaboration**: Share queries and workflows
- **SSO integration**: LDAP, SAML, and OAuth support
- **Audit logging**: Track all database access and changes
- **Priority support**: Direct access to our engineering team
- **Custom integrations**: Built specifically for your workflow

Learn more about WhoDB Enterprise Edition →

## Getting Started

Ready to experience the fastest, most intuitive database tool you've ever used?

Get started in under 5 minutes

See WhoDB in action

Complete documentation

## Frequently Asked Questions

Yes! WhoDB Community Edition is completely free and open source. You can use it for personal projects, commercial projects, and everything in between. No hidden costs, no usage limits.

Enterprise Edition is available for teams that need advanced features like additional database support, SSO, and priority support.

We've created detailed comparison guides:

- [WhoDB vs phpMyAdmin](/resources/comparisons/vs-phpmyadmin)
- [WhoDB vs pgAdmin](/resources/comparisons/vs-pgadmin)
- [WhoDB vs DBeaver](/resources/comparisons/vs-dbeaver)
- [WhoDB vs TablePlus](/resources/comparisons/vs-tableplus)

The short answer: WhoDB is faster, simpler, and more modern while supporting multiple database types.

Absolutely! WhoDB is production-ready and used by thousands of developers worldwide. We recommend using read-only database accounts for production access and enabling SSL/TLS for security.

For production use with sensitive data, consider WhoDB Enterprise Edition for additional security features like audit logging and SSO.

No. WhoDB does not store database credentials by default. Each session is temporary and credentials are cleared when you close the browser.

For convenience, you can optionally configure connection profiles (stored locally in your browser) or use environment variables for automated connections.

Yes! WhoDB is designed for self-hosting. You can run it on your own servers, in Docker containers, on Kubernetes, or even on your local machine. You have complete control over where your data goes.

Check our [Installation Guide](/installation) for deployment options.

WhoDB Community Edition supports:
- PostgreSQL 10+
- MySQL 5.7+ / MariaDB
- SQLite 3
- MongoDB 4.0+
- Redis 5.0+

WhoDB Enterprise Edition includes additional databases like Oracle, SQL Server, Cassandra, and more. See the [full list](/resources/supported-databases).

Migration is simple because WhoDB works with your existing databases - no data migration required! Just:

1. Install WhoDB
2. Connect to your existing databases
3. Start using WhoDB alongside your current tool
4. Switch completely when you're comfortable

We have migration guides for users coming from [phpMyAdmin](/guides/migrating-from-phpmyadmin) and [pgAdmin](/guides/migrating-from-pgadmin).

## What Developers Say

"WhoDB has become my go-to tool for database work. It's fast, it's beautiful, and it just works. I recommend it to every developer on my team."

**— Sarah Chen, Senior Backend Developer**

"The graph visualization alone makes WhoDB worth it. Understanding complex schemas used to take hours. Now it takes minutes."

**— Michael Rodriguez, Database Architect**

"Finally, a database tool that doesn't make me want to pull my hair out. WhoDB is what database management should have been all along."

**— Alex Thompson, Full-Stack Developer**

## Ready to Try WhoDB?

Join thousands of developers who have already made the switch to faster, simpler database management.

Install WhoDB in under 5 minutes

Star us on GitHub ⭐

**No credit card required. No account needed. Start using WhoDB immediately.**

---


# installation.mdx
## Installation

Get WhoDB installed and running on your system

# Installation Guide

WhoDB can be installed and run in multiple ways depending on your environment and preferences. Choose the method that best suits your needs.

## Installation Methods

Quick start with Docker for any platform

Download pre-compiled binaries for your OS

Compile WhoDB from source code

Native desktop application (Community & Enterprise)

## Docker Installation (Recommended)

Docker is the fastest and easiest way to get started with WhoDB

### Prerequisites

- Docker installed on your system ([Get Docker](https://docs.docker.com/get-docker/))
- Access to a database you want to connect to

### Quick Start with Docker

```
docker pull clidey/whodb:latest
```

```
docker run -d \
  --name whodb \
  -p 8080:8080 \
  clidey/whodb:latest
```

Open your browser and navigate to `http://localhost:8080`

WhoDB will be accessible on port 8080. You can change the port by modifying the `-p` flag (e.g., `-p 3000:8080` for port 3000)

### Docker Compose

For a more robust setup, use Docker Compose:

```
version: '3.8'
services:
  whodb:
    image: clidey/whodb:latest
    ports:
      - "8080:8080"
    environment:
      - WHODB_PORT=8080
    restart: unless-stopped
    volumes:
      - whodb-data:/data
volumes:
  whodb-data:
```

Save this as `docker-compose.yml` and run:

```
docker-compose up -d
```

## Binary Installation

Pre-compiled binaries are platform-specific. Make sure to download the correct version for your operating system.

### Download Pre-Compiled Binaries

```
# Download the latest Linux binary
curl -L https://github.com/clidey/whodb/releases/latest/download/whodb-linux-amd64 -o whodb

# Make it executable
chmod +x whodb

# Run WhoDB
./whodb
```

```
# Download the latest macOS binary
curl -L https://github.com/clidey/whodb/releases/latest/download/whodb-darwin-amd64 -o whodb

# Make it executable
chmod +x whodb

# Run WhoDB
./whodb
```

1. Download `whodb-windows-amd64.exe` from the [releases page](https://github.com/clidey/whodb/releases/latest)
2. Double-click the executable to run WhoDB
3. Access via browser at `http://localhost:8080`

## Build from Source

For developers who want to customize WhoDB or contribute to the project:

### Prerequisites

- Go 1.21 or higher
- Git

- Node.js 18 or higher
- pnpm package manager

### Build Steps

```
git clone https://github.com/clidey/whodb.git
cd whodb
```

```
cd frontend
pnpm install
pnpm run build
cd ..
```

```
cd core
go build -o whodb .
```

```
./whodb
```

The built binary will embed the frontend assets, creating a single executable file

### Development Mode

For active development with hot-reload:

``` Backend
cd core
go run .
```
``` Frontend
cd frontend
pnpm start
```

Run both commands in separate terminals. The frontend will be available at `http://localhost:5173` and will proxy API requests to the backend.

## Configuration Options

WhoDB can be configured using environment variables or command-line flags:

### Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `WHODB_PORT` | Port to run WhoDB on | `8080` |
| `WHODB_HOST` | Host address to bind to | `0.0.0.0` |
| `WHODB_LOG_LEVEL` | Logging level (debug, info, warn, error) | `info` |

### Example with Custom Port

```
WHODB_PORT=3000 ./whodb
```

Or with Docker:

```
docker run -d \
  -p 3000:3000 \
  -e WHODB_PORT=3000 \
  clidey/whodb:latest
```

## Verifying Installation

After installation, verify WhoDB is running correctly

Open your browser and navigate to `http://localhost:8080` (or your configured port)

You should see the WhoDB login page with database connection options

Try connecting to a test database to ensure everything works properly

![WhoDB Login Page](/images/01-login-page.png)

## Troubleshooting

If port 8080 is already in use, either:
- Stop the service using that port
- Use a different port with `-p 3000:8080` (Docker) or `WHODB_PORT=3000` (binary)

Make sure the binary is executable:
```
chmod +x whodb
```

Check the logs to see what went wrong:
```
docker logs whodb
```

Ensure:
- The database is accessible from WhoDB's network
- Firewall rules allow the connection
- Credentials are correct
- For Docker: use host.docker.internal instead of localhost for databases on the host

## Network Considerations

By default, WhoDB binds to `0.0.0.0`, making it accessible from any network interface. For production deployments, consider:

- Running behind a reverse proxy (Nginx, Caddy)
- Enabling HTTPS/TLS
- Implementing authentication and authorization
- Restricting network access with firewall rules
- Using a VPN or SSH tunnel for remote access

## Next Steps

Connect to your first database

Learn the basics in minutes

Now that WhoDB is installed, proceed to the **First Login** guide to connect to your database and start exploring your data

---


# first-login.mdx
## First Login

Connect to your first database with WhoDB

# First Login

Let's get you connected to your first database. WhoDB supports multiple database types and connection methods, making it easy to start exploring your data.

## The Login Page

When you first access WhoDB, you'll see the login page where you can configure your database connection.

![WhoDB Login Page](/images/01-login-page.png)

The login page is your gateway to all supported databases. Each connection is temporary and not stored by default for security reasons.

## Supported Databases

WhoDB supports a wide range of database systems:

Full support for PostgreSQL 10+

MySQL 5.7+ and MariaDB

Local SQLite databases

MongoDB 4.0+

Redis 5.0+

Additional databases in Enterprise Edition

## Selecting Your Database Type

The first field allows you to select your database type. Click the dropdown to see all available options.

![Database Type Dropdown](/images/02-login-database-type-dropdown.png)

Select the database you want to connect to. The form fields will adapt based on your selection.

Requires: Host, Port, Username, Password, Database Name

Requires: Host, Port, Username, Password, Database Name

Requires: File Path

Requires: Connection String or Host, Port, Database

Requires: Host, Port, Password (optional)

## Connection Examples

Let's look at connection examples for the most common databases:

### PostgreSQL Connection

![PostgreSQL Selected](/images/52-login-mysql-selected.png)

#### Standard Connection
```
Host: localhost
Port: 5432
Username: postgres
Password: your_password
Database: myapp_production
```

#### Connection String
```
postgresql://username:password@localhost:5432/database_name
```

For PostgreSQL, you can use either individual fields or a connection string

---

### MySQL Connection

![MySQL Selected](/images/52-login-mysql-selected.png)

```
Host: localhost
Port: 3306
Username: root
Password: your_password
Database: my_database
```

MySQL connections also support SSL/TLS. Enable this in Advanced Options if your server requires it.

---

### SQLite Connection

```
File Path: /path/to/your/database.db
```

For SQLite, ensure WhoDB has read/write permissions to the database file

---

### MongoDB Connection

![MongoDB Selected](/images/53-login-mongodb-selected.png)

#### Connection String
```
mongodb://username:password@localhost:27017/database_name
```

Or use individual fields:
```
Host: localhost
Port: 27017
Username: admin
Password: your_password
Database: myapp
```

---

### Redis Connection

![Redis Selected](/images/54-login-redis-selected.png)

```
Host: localhost
Port: 6379
Password: (optional)
Database: 0
```

Redis database number defaults to 0. Change this if you're using a different database index.

## Advanced Options

Click "Advanced Options" to access additional connection settings:

![Advanced Options](/images/04-login-advanced-options.png)

Enable secure connections with SSL certificates:
- **SSL Mode**: Disable, Require, Verify-CA, or Verify-Full
- **SSL Certificate**: Path to SSL certificate file
- **SSL Key**: Path to SSL private key
- **SSL Root Certificate**: Path to root certificate

Connect through an SSH tunnel for added security:
- **SSH Host**: SSH server address
- **SSH Port**: SSH port (usually 22)
- **SSH Username**: SSH username
- **SSH Private Key**: Path to private key file

Optimize performance with connection pooling:
- **Max Connections**: Maximum number of connections
- **Max Idle**: Maximum idle connections
- **Connection Lifetime**: Maximum connection lifetime

Set timeouts for long-running queries:
- **Query Timeout**: Maximum query execution time in seconds

## Connecting to Your Database

Enter all required information for your database connection. Make sure the credentials are correct and the database is accessible.

![Login Form Filled](/images/03-login-form-filled.png)

Click the "Connect" button to establish the connection. WhoDB will validate your credentials and attempt to connect to the database.

Upon successful connection, you'll be redirected to the main WhoDB interface showing your database schema and tables.

Successful connection! You should now see the WhoDB main interface with your database loaded.

## Troubleshooting Connection Issues

**Problem**: Cannot connect to the database server

**Solutions**:
- Verify the host and port are correct
- Ensure the database server is running
- Check firewall rules allow the connection
- For Docker: use `host.docker.internal` instead of `localhost` for host databases

**Problem**: Username or password is incorrect

**Solutions**:
- Double-check your credentials
- Ensure the user has the necessary permissions
- Verify the database name is correct
- Check if the user is allowed to connect from WhoDB's IP address

**Problem**: The specified database doesn't exist

**Solutions**:
- Verify the database name is spelled correctly (case-sensitive in some systems)
- Ensure the database has been created
- Check if you have permission to access that database

**Problem**: SSL/TLS connection fails

**Solutions**:
- Verify SSL certificates are valid and not expired
- Ensure certificate paths are correct
- Try different SSL modes (require vs verify-full)
- Check if the server requires SSL connections

**Problem**: Connection times out

**Solutions**:
- Check network connectivity
- Verify firewall rules
- Ensure the database server is not overloaded
- Increase the connection timeout in advanced options

## Security Best Practices

Follow these security guidelines when connecting to databases:

Always use strong, unique passwords for database connections. Never use default passwords in production.

For production databases, always enable SSL/TLS to encrypt data in transit.

Create dedicated database users with minimal required permissions for WhoDB access.

When only viewing data, use read-only database accounts to prevent accidental modifications.

Restrict database access to specific IP addresses or use VPNs for remote connections.

## Connection Examples by Environment

```
# PostgreSQL Local
Host: localhost
Port: 5432
Username: dev_user
Password: dev_password
Database: myapp_dev
```

For local development, you can use simpler credentials and skip SSL

```
# Connecting to PostgreSQL in Docker Compose
Host: postgres
Port: 5432
Username: postgres
Password: password
Database: app_db
```

Use the service name from docker-compose.yml as the host when WhoDB runs in the same Docker network

```
# Production PostgreSQL with SSL
Host: db.example.com
Port: 5432
Username: whodb_readonly
Password: strong_random_password
Database: production_db

Advanced Options:
SSL Mode: verify-full
SSL Certificate: /certs/client.crt
SSL Key: /certs/client.key
SSL Root Certificate: /certs/ca.crt
```

Always use SSL and read-only accounts for production databases

```
# AWS RDS PostgreSQL
Host: mydb.abc123.us-east-1.rds.amazonaws.com
Port: 5432
Username: admin
Password: your_password
Database: myapp

# Google Cloud SQL
Host: 10.1.2.3
Port: 5432
Username: postgres
Password: your_password
Database: mydb

# Azure Database
Host: myserver.postgres.database.azure.com
Port: 5432
Username: admin@myserver
Password: your_password
Database: mydb
```

Cloud databases often require SSL connections. Enable SSL mode in Advanced Options.

## Next Steps

Learn the WhoDB interface basics

Explore your database structure

Start viewing and managing data

Write and execute queries

Now that you're connected, head to the **Quick Start Guide** to learn how to navigate WhoDB and start working with your data

---


# quick-start.mdx
## Quick Start Guide

Get up and running with WhoDB in minutes

# Quick Start Guide

This guide will walk you through the basics of using WhoDB to explore your database, view data, run queries, and export results.

Follow along with this guide after successfully connecting to your database

## Interface Overview

After logging in, you'll see the main WhoDB interface:

![Storage Unit List](/images/05-storage-unit-list.png)

The interface consists of several key areas:

Navigate between databases, schemas, and tables

View and interact with your data

Access different views and features

Context-specific actions and tools

## Your First Five Minutes

The sidebar shows your database structure with expandable sections for schemas and tables.

![Sidebar Navigation](/images/06-storage-unit-list-with-sidebar.png)

Click on any schema to expand it and see its tables, views, and other objects.

Click on a table name to view its data in the main area.

![Data View](/images/09-data-view-users-table.png)

The data grid shows all rows and columns with sorting, filtering, and search capabilities.

Click the "Explore" tab to view table metadata, columns, indexes, and constraints.

![Explore Table](/images/07-explore-users-table.png)

Switch to the "Scratchpad" view to write and execute custom SQL queries.

![Scratchpad](/images/27-scratchpad-main-view.png)

Open the "Graph" view to see your database schema as an interactive diagram.

![Graph View](/images/24-graph-view-schema-topology.png)

## Key Features to Try

### 1. Viewing and Filtering Data

Click any column header to sort the data in ascending or descending order.

![Sorted Column](/images/38-data-view-sorted-column.png)

Hold Shift and click multiple columns to sort by multiple fields

Use the search box to find specific values across all visible columns.

![Search Highlight](/images/19-data-view-search-highlight.png)

Search is real-time and highlights matching results as you type.

Click the filter button to add WHERE conditions for complex filtering.

![Where Conditions](/images/16-data-view-where-conditions-popover.png)

You can combine multiple conditions with AND/OR logic:
- Equals (=)
- Not Equals (!=)
- Greater Than (>)
- Less Than (\<)
- Greater Than or Equal (>=)
- Less Than or Equal (\<=)

Control how many rows appear per page using the page size dropdown.

![Page Size](/images/15-data-view-page-size-dropdown.png)

Navigate between pages using the pagination controls at the bottom.

### 2. Managing Data

Click the "Add Row" button to insert new records.

![Add Row Dialog](/images/11-data-view-add-row-dialog.png)

Fill in the fields and click "Add" to insert the record.

![Add Row Filled](/images/12-data-view-add-row-filled.png)

Required fields are marked with an asterisk (*). Auto-increment fields can be left empty.

Right-click any row to open the context menu, then select "Edit Row".

![Context Menu](/images/13-data-view-context-menu.png)

Modify the values in the edit dialog:

![Edit Row Dialog](/images/14-data-view-edit-row-dialog.png)

Double-click a row to quickly open the edit dialog

Right-click a row and select "Delete Row" from the context menu.

![Delete Option](/images/43-context-menu-delete-option.png)

Confirm the deletion to permanently remove the record.

Deletions are permanent and cannot be undone. Make sure you have backups before deleting data.

### 3. Running Queries

The Scratchpad is your SQL playground:

![Scratchpad Code Editor](/images/28-scratchpad-code-editor.png)

Type your SQL query in the code editor. You get syntax highlighting and auto-completion.

```
SELECT id, username, email
FROM users
WHERE created_at > '2024-01-01'
ORDER BY id DESC
LIMIT 10;
```

Click the Run button or press Cmd/Ctrl+Enter to execute.

Results appear below the editor in a data grid.

![Query Results](/images/29-scratchpad-query-results.png)

All queries are automatically saved to history for easy access later.

![Query History](/images/33-scratchpad-query-history.png)

Scratchpad supports multiple cells, allowing you to organize and execute different queries independently

### 4. Visualizing Schema

The Graph view provides a visual representation of your database:

![Graph View](/images/25-graph-view-with-controls.png)

- **Pan**: Click and drag the canvas
- **Zoom**: Use mouse wheel or zoom controls
- **Fit View**: Click the fit button to center all nodes

Click on any table node to view its columns and relationships.

![Node Details](/images/26-graph-view-node-details.png)

Relationships are shown as connecting lines with cardinality indicators.

- **One-to-Many**: Solid line with single/multiple indicators
- **Many-to-One**: Reverse of one-to-many
- **Many-to-Many**: Through junction tables
- **One-to-One**: Solid line with single indicators on both ends

### 5. Exporting Data

Export your data in multiple formats:

![Export Dialog](/images/20-data-view-export-dialog.png)

- **Export All**: Export all rows in the table
- **Export Selected**: Export only selected rows
- **Export Filtered**: Export rows matching current filters

Choose your preferred format:

Comma-separated values for Excel and data tools

Native Excel format (.xlsx)

JSON format for APIs and applications

SQL INSERT statements for backups

Set delimiter, encoding, and other format-specific options:

![Export Format Options](/images/21-data-view-export-format-dropdown.png)

Click "Export" to download your file

## Common Workflows

Log in with your credentials

Expand schemas in the sidebar to find interesting tables

Click a table to see its contents

Add WHERE conditions to focus on specific data

Save filtered data for further analysis

Navigate to the Scratchpad view

Type your SQL query with auto-completion help

Run the query and view results

Adjust the query based on results

Query is automatically saved to history

Find and open the table you want to modify

Use WHERE conditions to find specific rows

Add, edit, or delete rows as needed

Refresh to see updated data

Export a backup before major changes

Switch to the Graph visualization

Click nodes to see table connections

Look for table clusters and relationship chains

Use Explore view to see indexes and keys

Export schema diagram for documentation

## Keyboard Shortcuts

Master these shortcuts to work faster in WhoDB

### Global Shortcuts

| Shortcut | Action |
|----------|--------|
| `Cmd/Ctrl + K` | Quick navigation |
| `Cmd/Ctrl + /` | Toggle sidebar |
| `Cmd/Ctrl + F` | Search in current view |
| `Esc` | Close dialogs/modals |

### Scratchpad Shortcuts

| Shortcut | Action |
|----------|--------|
| `Cmd/Ctrl + Enter` | Run current query |
| `Cmd/Ctrl + S` | Save query |
| `Cmd/Ctrl + N` | New cell |
| `Cmd/Ctrl + D` | Duplicate cell |
| `Cmd/Ctrl + Shift + F` | Format query |

### Data Grid Shortcuts

| Shortcut | Action |
|----------|--------|
| `Cmd/Ctrl + C` | Copy selected cell |
| `Cmd/Ctrl + A` | Select all rows |
| `Tab` | Move to next cell |
| `Shift + Tab` | Move to previous cell |
| `Enter` | Edit selected row |

## Best Practices

- Use `LIMIT` for large result sets
- Add indexes for frequently filtered columns
- Avoid `SELECT *` in production queries
- Use EXPLAIN to analyze slow queries

- Always backup before bulk operations
- Use read-only accounts when possible
- Test queries on development databases first
- Enable SQL review before executing

- Use descriptive names for saved queries
- Create folders for different projects
- Document complex queries with comments
- Tag queries by purpose or category

- Share connection details securely
- Document schema changes
- Use consistent naming conventions
- Keep query history for auditing

## Next Steps

Now that you understand the basics, explore specific features in depth:

Query databases using natural language

Master data viewing, filtering, and editing

Learn advanced query techniques

Understand your database structure

You're now ready to use WhoDB effectively! Explore the detailed documentation for advanced features and techniques.

---


# features/database-connectivity.mdx
## Database Connectivity

Connect to multiple database types with WhoDB, including PostgreSQL, MySQL, MongoDB, and Redis

# Database Connectivity

WhoDB provides seamless connectivity to multiple database systems with a unified interface. Whether you're working with traditional relational databases or modern NoSQL solutions, WhoDB makes it easy to connect and manage your data.

## Supported Databases

WhoDB supports the most popular databases used in production environments:

Full support for PostgreSQL 10+, the leading open-source relational database

MySQL 5.7+, MariaDB, and other MySQL-compatible systems

MongoDB 4.0+, the flexible document-oriented NoSQL database

Redis 5.0+, the in-memory data store for caching and sessions

SQLite for local development and embedded databases

Additional databases in enterprise and community versions

## PostgreSQL Connection

PostgreSQL is one of the most widely-used relational databases. WhoDB provides comprehensive support for PostgreSQL instances with multiple connection options.

![PostgreSQL Login](/images/01-login-page.png)

### Standard PostgreSQL Connection

Open the database type dropdown and select PostgreSQL from the list of available databases.

![Database Type Dropdown](/images/02-login-database-type-dropdown.png)

Enter your PostgreSQL connection information in the form fields.

![Login Form](/images/03-login-form-filled.png)

``` Connection Fields
Host: localhost
Port: 5432
Username: postgres
Password: your_password
Database: production_db
```
``` Connection String Format
postgresql://username:password@localhost:5432/database_name
```

For production systems, enable SSL/TLS and other security features.

![Advanced Options](/images/04-login-advanced-options.png)

- **SSL Mode**: Disable, Require, Verify-CA, or Verify-Full
- **SSL Certificate**: Path to client certificate
- **SSL Key**: Path to private key
- **SSL Root Certificate**: Path to root CA

Securely connect through an SSH tunnel:
- **SSH Host**: SSH server address
- **SSH Port**: Usually 22
- **SSH Username**: Your SSH username
- **SSH Private Key**: Path to private key

PostgreSQL defaults to port 5432. Ensure your firewall allows connections on this port or the custom port you've configured.

### PostgreSQL-Specific Features

PostgreSQL connections in WhoDB unlock advanced capabilities:

- **Full ACID Support**: Transactional queries with complete rollback capabilities
- **Schema Support**: Work with multiple schemas within a database
- **Advanced Types**: Support for arrays, JSON, UUID, and custom types
- **Extensions**: Query systems with PostGIS, hstore, and other extensions
- **Row-Level Security**: View tables with RLS policies applied

Use prepared statements for sensitive queries to prevent SQL injection and improve performance.

## MySQL Connection

MySQL is the world's most popular open-source relational database. WhoDB provides full support for MySQL and MariaDB.

![MySQL Selected](/images/52-login-mysql-selected.png)

### Setting Up MySQL Connection

Choose MySQL from the database type dropdown.

``` MySQL Connection
Host: localhost
Port: 3306
Username: root
Password: your_password
Database: myapp_db
```
``` MariaDB Connection
Host: mariadb.example.com
Port: 3306
Username: app_user
Password: secure_password
Database: application
```

For production systems, enable SSL/TLS encryption in Advanced Options.

MySQL by default does not require SSL. Enable it for any production connections handling sensitive data.

### MySQL Connection Best Practices

Ensure proper character encoding for international characters:
```
Use utf8mb4 for full UTF-8 support
```

- Set appropriate connection pool size
- Enable query timeout to prevent runaway queries
- Use connection limits based on your needs

WhoDB fully supports MariaDB 10.5+. Use standard MySQL connection settings; WhoDB automatically detects the database type.

## MongoDB Connection

MongoDB brings flexibility with its document-oriented NoSQL approach. WhoDB provides intuitive access to MongoDB collections.

![MongoDB Selected](/images/53-login-mongodb-selected.png)

### Connecting to MongoDB

Select MongoDB from the database type dropdown.

``` Connection String
mongodb://username:password@localhost:27017/database_name
```
``` Individual Fields
Host: localhost
Port: 27017
Username: admin
Password: your_password
Database: myapp
```
``` MongoDB Atlas (Cloud)
mongodb+srv://username:password@cluster.mongodb.net/database_name
```

Specify which database within the MongoDB instance you want to explore.

MongoDB connection strings can include authentication and replica set information. WhoDB handles complex connection strings automatically.

### MongoDB-Specific Features

- **Document Viewing**: Browse collections with automatic formatting
- **Schema Inspection**: View document structure and field types
- **Index Management**: See indexes and their performance impact
- **Authentication**: Support for standard and SCRAM authentication

MongoDB collections in WhoDB are displayed as tables, making them familiar to users of relational databases.

## Redis Connection

Redis is an in-memory data store ideal for caching, sessions, and real-time applications. WhoDB provides a unique view into Redis data structures.

![Redis Selected](/images/54-login-redis-selected.png)

### Connecting to Redis

Choose Redis from the database dropdown.

``` Standard Redis
Host: localhost
Port: 6379
Password: (leave empty if none)
Database: 0
```
``` Redis with Password
Host: redis.example.com
Port: 6379
Password: your_redis_password
Database: 0
```
``` Redis Cluster
Host: node1.cluster.local
Port: 6379
Username: default
Password: cluster_password
```

Redis supports multiple databases (0-15 by default). Choose which database you want to explore.

### Redis-Specific Considerations

Redis supports up to 16 databases by default (0-15). Each database is isolated and contains its own keys. Choose the correct database for your use case.

Redis keys can have TTL (Time To Live). WhoDB shows expiration information when available, but cannot modify TTLs through the UI.

Browse and filter Redis keys using patterns. WhoDB helps you navigate large Redis instances efficiently.

Redis stores data in memory. Exercise caution when making modifications, as changes are permanent and immediate.

## Connection Options and Security

### All Database Types

Enable SSL/TLS for encrypted connections:
- **Disable**: Unencrypted connections (development only)
- **Require**: Encrypted connection required
- **Verify-CA**: Verify server certificate against root CA
- **Verify-Full**: Full certificate validation (recommended for production)

Connect securely through an SSH tunnel:
```
SSH Host: bastion.example.com
SSH Port: 22
SSH Username: ssh_user
SSH Private Key: /path/to/id_rsa
```
Useful for accessing databases behind firewalls or in private networks.

Optimize performance with connection pooling:
- **Max Connections**: Maximum concurrent connections (default varies by database)
- **Max Idle Time**: How long idle connections remain open
- **Connection Lifetime**: Maximum time before connection is recycled

Prevent runaway queries from consuming resources:
```
Query Timeout: 30 seconds (configurable)
```
Queries exceeding this timeout will be cancelled automatically.

## Security Best Practices

Always use strong, unique passwords for database users. Never use default passwords like "password" or "admin" in production.

Weak credentials are a leading cause of database breaches.

Use SSL/TLS for all connections to production databases to encrypt data in transit.

Don't use administrative accounts for routine work. Create dedicated database users with minimal required permissions.

``` PostgreSQL Read-Only User
CREATE USER whodb_readonly WITH PASSWORD 'strong_password';
GRANT CONNECT ON DATABASE mydb TO whodb_readonly;
GRANT USAGE ON SCHEMA public TO whodb_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO whodb_readonly;
```
``` MySQL Read-Only User
CREATE USER 'whodb_ro'@'localhost' IDENTIFIED BY 'strong_password';
GRANT SELECT ON myapp.* TO 'whodb_ro'@'localhost';
FLUSH PRIVILEGES;
```

Restrict database access to known IP addresses or use VPNs for remote connections. Consider using firewalls and security groups.

Keep track of who is accessing your databases and when. Review logs regularly for suspicious activity.

## Connection Troubleshooting

**Symptom**: "Connection refused" or "Unable to connect"

**Solutions**:
1. Verify the host and port are correct
2. Ensure the database service is running
3. Check firewall rules allow the connection
4. For Docker containers: use `host.docker.internal` instead of `localhost`
5. Try connecting with the database's native client tool first (psql, mysql, mongo, etc.)

**Symptom**: "Invalid username or password" or "Access denied"

**Solutions**:
1. Double-check credentials (case-sensitive in many systems)
2. Verify the database user exists and is active
3. Ensure the user has permission to connect from your IP address
4. Check if the user has the correct role/permissions
5. Reset the password using the database administration tools

**Symptom**: "Database does not exist" or "Database not accessible"

**Solutions**:
1. Verify the database name (some systems are case-sensitive)
2. Ensure the database has been created
3. Check if the user has permission to access that database
4. For MySQL: database and schema are the same concept
5. For PostgreSQL: verify the database exists with `\l` command

**Symptom**: "SSL certificate error" or "Self-signed certificate error"

**Solutions**:
1. Verify SSL certificates are valid and not expired
2. Ensure certificate paths are correctly specified
3. Try different SSL modes (require → verify-ca → verify-full)
4. For self-signed certificates, use "Require" mode without verification
5. Check if the server certificate matches the hostname

**Symptom**: "Connection timed out" or "Request timeout"

**Solutions**:
1. Verify network connectivity to the database host
2. Check firewall rules and security groups
3. Increase the connection timeout in Advanced Options
4. Ensure the database server is not overloaded
5. Try connecting from a different network location
6. Check for proxy or VPN issues

**Symptom**: "SSH connection failed" or tunnel-related errors

**Solutions**:
1. Verify SSH credentials and key file permissions
2. Ensure SSH server is accessible and running
3. Check SSH port (usually 22) is accessible
4. Verify the private key file has correct permissions (600)
5. Try SSH connection manually before configuring in WhoDB

## Environment-Specific Configurations

### Local Development

``` PostgreSQL Local
Host: localhost
Port: 5432
Username: dev_user
Password: dev_password
Database: myapp_dev
```
``` MySQL Local
Host: 127.0.0.1
Port: 3306
Username: root
Password: root
Database: dev_db
```

For local development, skip SSL configuration to simplify setup. Focus on security in production.

### Docker Compose

``` PostgreSQL in Docker
Host: postgres (service name from docker-compose.yml)
Port: 5432
Username: postgres
Password: password
Database: app_db
```
``` MongoDB in Docker
Host: mongodb
Port: 27017
Database: myapp
```

When WhoDB runs in the same Docker network as your database, use the service name as the host instead of localhost.

### Production Environments

``` AWS RDS PostgreSQL
Host: mydb.abc123.us-east-1.rds.amazonaws.com
Port: 5432
Username: whodb_user (read-only account)
Password: strong_random_password
Database: production_db

Advanced Options:
SSL Mode: verify-full
```
``` Google Cloud SQL
Host: 10.1.2.3 (private IP)
Port: 5432
Username: whodb_readonly
Password: strong_password
Database: mydb

Advanced Options:
SSL Mode: require
```
``` Azure Database for PostgreSQL
Host: myserver.postgres.database.azure.com
Port: 5432
Username: admin@myserver
Password: strong_password
Database: mydb

Advanced Options:
SSL Mode: verify-full
```

Always use SSL/TLS and read-only accounts in production. Enable advanced security features and monitor all connections.

## Next Steps

Query your database using natural language

Learn the WhoDB interface basics

Explore your database structure

Start viewing and managing data

You're now ready to connect to your database and start exploring. Use the Schema Explorer to understand your data structure, or jump straight to viewing data.

---


# features/schema-explorer.mdx
## Schema Explorer

Explore your database schema, view table metadata, columns, indexes, and foreign key relationships

# Schema Explorer

The Schema Explorer is your window into the structure of your database. It provides detailed information about tables, columns, indexes, constraints, and relationships, helping you understand and navigate your data architecture.

## Overview of Schema Explorer

Once connected to your database, the Schema Explorer displays your complete database structure with an intuitive interface for browsing tables and their metadata.

![Explore View](/images/07-explore-users-table.png)

The Schema Explorer automatically loads when you connect to a database. Use it to navigate your schema before performing queries or data operations.

## Viewing Table Metadata

When you select a table in the Schema Explorer, comprehensive metadata about that table is displayed, including column definitions, constraints, and indexes.

![Table Metadata](/images/08-explore-table-metadata.png)

### Understanding Table Information

Basic table details including:
- **Table Name**: The name of the table in your database
- **Row Count**: Approximate number of rows (updated periodically)
- **Storage Size**: Total storage used by the table
- **Created**: When the table was created
- **Modified**: Last modification time
- **Type**: Base table, view, temporary table, etc.

For MySQL databases:
- **Engine**: MyISAM, InnoDB, or other storage engine
- **Charset**: Default character encoding for the table
- **Collation**: How string comparisons are performed

Performance-related information:
- **Avg Row Size**: Average size of individual rows
- **Data Length**: Space used by table data
- **Index Length**: Space used by indexes
- **Free Space**: Unused space in the table

## Exploring Columns

Each table contains columns with specific data types and constraints. The Schema Explorer provides detailed information about each column.

Click on any table in the left sidebar to view its columns.

The columns section displays:
- **Column Name**: Name of the column
- **Data Type**: PostgreSQL/MySQL data type (VARCHAR, INT, TIMESTAMP, JSON, etc.)
- **Nullable**: Whether NULL values are allowed
- **Default**: Default value when inserting rows without specifying this column
- **Constraints**: Unique, check, or other constraints

``` Standard Columns
Column Name: email
Type: VARCHAR(255)
Nullable: No
Collation: utf8mb4_unicode_ci
Default: None
```
``` Timestamp Columns
Column Name: created_at
Type: TIMESTAMP
Nullable: No
Default: CURRENT_TIMESTAMP
On Update: CURRENT_TIMESTAMP
```
``` JSON Columns
Column Name: metadata
Type: JSON
Nullable: Yes
Default: None
Comment: Stores additional user data
```

### Column Data Types

WhoDB recognizes and displays all standard database data types:

VARCHAR, CHAR, TEXT, STRING

INT, BIGINT, FLOAT, DECIMAL, NUMERIC

DATE, TIME, TIMESTAMP, DATETIME, INTERVAL

BOOLEAN, BIT, TINYINT(1)

JSON, UUID, ARRAY, ENUM, BLOB

PostGIS types, MongoDB documents

Hover over column names to see additional details like comments and constraints.

## Primary Keys and Constraints

Understanding primary keys and other constraints is critical for database design. The Schema Explorer highlights these essential elements.

![Primary Key Column](/images/98-explore-primary-key-column.png)

### Primary Key Identification

Primary keys uniquely identify each row in a table:
- **Single Column PK**: Most common type (e.g., `id` INT PRIMARY KEY)
- **Composite PK**: Multiple columns form the primary key
- **Auto-Increment**: ID automatically generates with each new row
- **Symbol**: Indicated by a key icon in the Schema Explorer

Every table should have a primary key. Tables without primary keys can be challenging to update or delete specific rows.

Ensure column values are unique across all rows:
- **Single Column**: Unique constraint on one column (e.g., email)
- **Composite Unique**: Combination of columns must be unique
- **Null Handling**: Most databases allow multiple NULL values in unique columns

Enforce business logic at the database level:
```
-- Example: age must be between 0 and 150
ALTER TABLE users ADD CONSTRAINT age_check CHECK (age >= 0 AND age <= 150);
```

Require that column always has a value:
- Marked as "NOT NULL" or "Nullable: No" in the Schema Explorer
- Prevents incomplete data entries

Pay special attention to nullable columns. Queries must handle NULL values appropriately.

## Foreign Keys and Relationships

Foreign keys establish relationships between tables, maintaining data integrity and enabling joins. The Schema Explorer visualizes these relationships clearly.

![Foreign Key Columns](/images/99-explore-foreign-key-columns.png)

### Understanding Foreign Keys

Foreign key columns are marked with a relationship icon in the Schema Explorer. They reference primary keys in other tables.

For each foreign key, understand:
- **Referenced Table**: Which table this column points to
- **Referenced Column**: Which column in the other table
- **Cardinality**: One-to-many, one-to-one, or many-to-many
- **Cascade Rules**: What happens when referenced rows are deleted/updated

``` One-to-Many Relationship
Table: orders
Column: customer_id
↓ References
Table: customers
Column: id

Each customer can have many orders
```
``` One-to-One Relationship
Table: users
Column: profile_id
↓ References
Table: profiles
Column: id

Each user has exactly one profile
```

### Foreign Key Options

- **RESTRICT**: Prevents deletion if foreign key references exist
- **CASCADE**: Automatically deletes referencing rows
- **SET NULL**: Sets foreign key to NULL when referenced row is deleted
- **SET DEFAULT**: Sets to default value when referenced row is deleted

- **RESTRICT**: Prevents updating if foreign key references exist
- **CASCADE**: Automatically updates foreign key values
- **SET NULL**: Sets foreign key to NULL when referenced row is updated
- **SET DEFAULT**: Sets to default value when referenced row is updated

For databases supporting deferred constraint checking:
- **Immediate**: Check constraint immediately after each statement
- **Deferred**: Check constraint at transaction end
- Useful for loading data in specific orders

Foreign key constraints ensure referential integrity—you cannot have an order for a non-existent customer.

## Indexes and Performance

Indexes dramatically improve query performance by allowing the database to find data without scanning entire tables. The Schema Explorer displays all indexes on each table.

![Table with Indexes](/images/100-explore-table-with-indexes.png)

### Understanding Indexes

Each table displays its indexes with key information:
- **Index Name**: Name of the index
- **Type**: B-tree, Hash, GiST, GIN, or BRIN (database-dependent)
- **Columns**: Which columns the index covers
- **Unique**: Whether the index enforces uniqueness
- **Size**: Storage space used by the index

Automatically created, ensures uniqueness, used for fast row lookup

Similar to PK, enforces uniqueness across column(s)

Speed up filtering, joins, and sorting operations

Indexes on multiple columns for complex queries

``` Good Index Examples
-- Single column frequently used in WHERE clause
CREATE INDEX idx_email ON users(email);

-- Support for JOIN operations
CREATE INDEX idx_user_id ON orders(user_id);

-- Composite index for common queries
CREATE INDEX idx_user_date ON orders(user_id, order_date);

-- Partial index for specific data
CREATE INDEX idx_active_users ON users(id) WHERE status = 'active';
```

### Index Types by Database

- **B-tree**: General purpose, default for most queries
- **Hash**: Fast equality comparisons
- **GiST**: Geographic and geometric data
- **GIN**: Full-text search and array queries
- **BRIN**: Very large tables, time-series data

- **B-tree**: Standard index type
- **Hash**: Memory storage engine only
- **Full-text**: Text search indexes
- **Spatial**: Geographic data

Indexes improve SELECT performance but slow down INSERT, UPDATE, DELETE operations. Monitor the impact of new indexes on write performance.

Too many indexes can harm performance. Index only columns that are frequently used in WHERE clauses, JOINs, or ORDER BY.

## Navigating Complex Schemas

Databases with many tables can be overwhelming. WhoDB provides tools for effective navigation.

### Schema Overview

Use the search functionality to quickly find tables by name. Especially useful in large schemas with hundreds of tables.

Filter tables by type:
- **Base Tables**: Regular tables containing data
- **Views**: Virtual tables defined by queries
- **System Tables**: Database system tables
- **Temporary Tables**: Temporary tables for the session

For databases supporting multiple schemas (PostgreSQL, MySQL):
- View tables organized by schema
- Switch between schemas easily
- Understanding schema separation helps organize complex databases

### Relationship Visualization

Use the Graph View (available from the Data View) to visualize table relationships and see your schema structure at a glance. This helps understand complex relationships at a high level.

## Column Details Deep Dive

### String Columns

``` VARCHAR (Variable Character)
Storage: Only characters you provide
Typical Uses: Email, names, addresses
Example: VARCHAR(255)
```
``` CHAR (Fixed Character)
Storage: Always uses full length
Typical Uses: Codes, country codes
Example: CHAR(2)
```
``` TEXT (Large Text)
Storage: Unlimited size
Typical Uses: Descriptions, content
Example: TEXT
```

### Numeric Columns

``` Small Numbers
Type: SMALLINT
Range: -32,768 to 32,767
Use: Enum-like values, small counts
```
``` Regular Numbers
Type: INT or INTEGER
Range: -2 billion to 2 billion
Use: IDs, counts, quantities
```
``` Large Numbers
Type: BIGINT
Range: -9 quintillion to 9 quintillion
Use: Millisecond timestamps, large IDs
```
``` Decimals
Type: DECIMAL(10,2)
Range: Precise decimal values
Use: Money, prices, measurements
```

### Temporal Columns

- **DATE**: Only year, month, day (e.g., 2024-01-15)
- **TIME**: Only hour, minute, second (e.g., 14:30:00)
- **DATETIME**: Both date and time, no timezone
- **TIMESTAMP**: Date and time with timezone awareness

Common patterns for tracking:
```
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
```

## Best Practices for Schema Design

Every table needs a primary key. Use auto-incrementing IDs or UUIDs.

Tables without primary keys cannot be properly updated or deleted by specific row.

Choose the right data type for each column:
- Small numbers: SMALLINT (vs INT or BIGINT)
- Emails: VARCHAR(255)
- Prices: DECIMAL(10,2) (not FLOAT)
- Status: ENUM or specific values

Index columns used frequently in WHERE, JOIN, or ORDER BY clauses. Avoid indexing rarely-queried columns.

Add CHECK constraints for business logic:
```
ALTER TABLE users ADD CONSTRAINT valid_age CHECK (age >= 0 AND age <= 150);
```

Column names should clearly indicate their purpose:
- `email` instead of `e`
- `created_at` instead of `date`
- `is_active` instead of `status`

## Advanced Schema Information

### Views and Virtual Tables

Some tables may actually be views—queries that appear as tables. Views are useful for:
- Simplifying complex queries
- Providing different perspectives on data
- Security (limiting what users can see)

Views are read-only in most databases and cannot be directly modified.

### Materialized Views

Some databases (PostgreSQL, etc.) support materialized views that store query results:
- Faster queries than regular views
- Must be refreshed to see new data
- Use for complex, expensive queries

## Next Steps

Start viewing data in your tables

Visualize your schema relationships

Master the navigation features

Write queries in the Scratchpad

Now that you understand your schema structure, you're ready to explore the actual data. Head to the Data Viewing guide to learn how to browse and manage your table contents.

---


# features/sidebar-navigation.mdx
## Sidebar Navigation

Master WhoDB's sidebar features including database and schema selection, and efficient table navigation

# Sidebar Navigation

The sidebar is the command center of WhoDB, providing quick access to your databases, schemas, and tables. Understanding how to effectively use the sidebar will significantly improve your productivity when working with multiple databases and large schemas.

## The WhoDB Sidebar Overview

The sidebar appears on the left side of the WhoDB interface, organizing your database structure in a hierarchical, easy-to-navigate format.

![Sidebar with Storage Units](/images/06-storage-unit-list-with-sidebar.png)

The sidebar adapts based on your database type. Different databases (PostgreSQL, MySQL, MongoDB, Redis) have different organizational structures reflected in the sidebar.

## Database Selector

The database selector at the top of the sidebar shows your currently-connected database and provides options for managing connections.

![Database Selector](/images/34-sidebar-database-selector.png)

### Changing Databases

Click on the database name at the top of the sidebar to access database management options.

The selector displays:
- **Database Type**: PostgreSQL, MySQL, MongoDB, or Redis
- **Database Name**: The currently connected database
- **Connection Status**: Visual indicator showing you're connected
- **Server Information**: Host and port (if available)

Close your current database connection

Add a new database connection

Quickly reconnect to recently used databases

View details about the current connection

### Connection Indicator

The status indicator shows:
- **Green**: Successfully connected and ready
- **Yellow**: Connection unstable or in progress
- **Red**: Connection failed or disconnected

Use the connection indicator to quickly verify your database connection status before running queries.

## Schema Selector

For databases that support multiple schemas (PostgreSQL, MySQL), the schema selector allows you to switch between schemas without reconnecting.

![Schema Selector](/images/35-sidebar-schema-selector.png)

### Understanding Schemas

Schemas are containers for database objects (tables, views, indexes). They help organize large databases:

- **PostgreSQL**: Native schema support - can have public, private, or application schemas
- **MySQL**: Limited schema support - schemas are similar to databases
- **SQLite**: No schema support - all tables are in one namespace
- **MongoDB**: Uses databases instead of schemas

``` E-Commerce Database Schema Structure
public (default schema)
├── users
├── products
├── orders
└── order_items

auth_schema
├── users
├── permissions
└── roles

reporting_schema
├── sales_summary
├── user_analytics
└── product_trends
```

Click the schema dropdown to switch between available schemas. All tables in the sidebar immediately update to show the selected schema's contents.

When switching schemas, your current view updates to show tables from the new schema. Any ongoing data editing operations are cancelled.

## Table Navigation Menu

The main section of the sidebar displays all tables in the current schema, organized for easy navigation.

![Navigation Menu](/images/36-sidebar-navigation-menu.png)

### Table Organization

Tables are displayed in a hierarchical list format:
- **Table Name**: Full name of the table
- **Table Type**: Icon indicating the table type (regular, view, temporary)
- **Row Count**: Number of rows (if available)
- **Last Modified**: When the table was last changed

Tables sorted A-Z for easy search

Recently modified tables at the top

Tables by size, largest first

Tables by size, smallest first

Use the search box to quickly find tables:
```
Search features:
- Type table name or partial name
- Real-time filtering as you type
- Case-insensitive search
- Highlight matching tables
```

### Interacting with Tables

Click a table once to view its schema information and metadata in the main panel.

Double-click a table to immediately view its data in the Data View.

Right-click on a table to access:
- **View Data**: Open the table in Data View
- **View Schema**: Display table structure
- **Copy Name**: Copy table name to clipboard
- **Export**: Export table data
- **Generate Mock Data**: Create test data
- **Properties**: View detailed table information

Hover over a table to see a tooltip with:
- Complete table name
- Row count
- Storage size
- Last modified date

## Efficient Navigation Workflows

### Finding Tables in Large Schemas

For databases with hundreds of tables, use these strategies:

Type part of the table name in the search box. Search is case-insensitive and matches partial names.

``` Search Examples
Type: "user"
Matches: users, user_roles, user_permissions

Type: "order"
Matches: orders, order_items, order_status

Type: "_prod"
Matches: All tables ending with _prod
```

Some sidebars allow filtering by table type:
- **Tables**: Regular data tables
- **Views**: Virtual tables defined by queries
- **System**: Database system tables

Mark frequently-used tables as favorites for quick access:
- Click the star icon next to table name
- Favorites appear at the top of the list
- Makes navigation faster in complex schemas

### Multi-Table Workflows

1. Click first table to view its schema
2. Scroll down to see all columns
3. Open second table in new tab to compare
4. Use browser tabs to switch between tables

1. View a table's schema
2. Identify foreign key columns
3. Click the foreign key to jump to related table
4. Browser back button returns to previous table
5. Understand data relationships quickly

1. Expand table list to see all tables
2. Note naming patterns (user_*, order_*, product_*)
3. Identify related tables by prefix
4. Understand database organization
5. Plan your queries based on structure

## Table Sections in the Sidebar

### Collapsible Groups

Some databases organize tables into collapsible groups:

Tables grouped by schema:
```
public
├── users
├── products
└── orders

auth
├── roles
└── permissions

reporting
├── monthly_sales
└── user_analytics
```
Click schema name to expand/collapse all tables in that schema.

Some configurations group tables by type:
```
Tables
├── users
├── orders
└── products

Views
├── active_users
└── order_summary

System Tables
├── pg_catalog
└── information_schema
```

Enterprise versions may support custom grouping by application or module.

## Sidebar Actions and Features

### Table Operations

Open table in Data View to see and edit rows

Display column definitions and constraints

Copy table name to clipboard

Export table data in various formats

Create mock/test data for the table

View detailed table metadata and statistics

### Quick Actions

``` Navigation Shortcuts
Ctrl/Cmd + F: Focus on search box
Enter: Open first search result
Arrow Up/Down: Navigate search results
Escape: Close search or context menu
```

Some configurations allow dragging tables to:
- Reorder favorites
- Group related tables
- Create custom collections

Sidebar items show different options based on:
- Database type (PostgreSQL vs MySQL vs MongoDB)
- User permissions (read-only vs read-write)
- Connection status (connected vs disconnected)

## Sidebar Customization

### Appearance Settings

- Resize by dragging the divider between sidebar and main panel
- Double-click to auto-fit content
- Sidebar remembers your preferred width

Some versions support different density settings:
- **Compact**: More tables visible, less spacing
- **Normal**: Standard comfortable spacing
- **Spacious**: Large spacing, fewer visible tables

The sidebar adapts to your chosen theme:
- Light mode: Light background, dark text
- Dark mode: Dark background, light text
- High contrast: Enhanced readability

### Preferences

Control whether schemas/groups auto-expand when clicked:
- Always expand: Recommended for frequent navigation
- Manual only: Requires explicit clicking

Toggle whether row counts display for each table:
- Enable: Helpful for understanding data volume
- Disable: Faster performance with very large schemas

Display when each table was last modified:
- Useful for understanding recent changes
- Helps identify active tables

## Advanced Navigation Patterns

### Search-First Workflow

For power users with large schemas:

The search box is always accessible at the top of the sidebar.

Begin typing immediately - search is real-time:
```
Example: type "cust" → matches "customers", "customer_orders"
```

Arrow keys or mouse to select the table you want.

Press Enter or click to open the selected table.

### Relationship Exploration

Select a main table (e.g., orders)

See all columns and foreign keys

Jump to referenced table (e.g., customers)

Continue exploring through relationships:
```
orders → customers → addresses
```

Use back/forward buttons to revisit tables

## Troubleshooting Sidebar Issues

**Solution**:
1. Check database connection (look for connection indicator)
2. Verify schema is selected (especially for PostgreSQL)
3. Refresh the page (Ctrl+R or Cmd+R)
4. Check for permission issues (read-only account)

**Solution**:
1. Verify table names - search is exact substring match
2. Check for schema filter - may be hiding tables
3. Try different search terms or partial names
4. Scroll manually through list to verify table exists

**Solution**:
1. Try resizing the sidebar by dragging divider
2. Close and reopen the connection
3. Refresh your browser
4. Check browser console for JavaScript errors

## Best Practices

Invest time in learning the search functionality. For large schemas, search-first is the fastest approach.

Most tables can be found in 1-2 seconds with effective searching.

Use meaningful table names and schemas:
- Prefix related tables: user_*, order_*, product_*
- Use schemas for logical grouping
- Avoid abbreviations that aren't obvious

Mark only tables you use regularly. Too many favorites defeats their purpose.

Having 20+ favorites makes navigation slower, not faster.

Keep an eye on growing tables:
- Unexpectedly large tables may indicate bugs
- Growing log tables may need archival

## Next Steps

Deep dive into table structure and relationships

Manage and organize your storage

Browse and edit table data

Visualize your entire schema structure

The sidebar is your primary navigation hub. Master it, and you'll work much faster through WhoDB's powerful features.

---


# features/storage-units.mdx
## Storage Units

Understand and manage storage units in WhoDB for organizing and accessing your database tables

# Storage Units

Storage Units are WhoDB's way of organizing and presenting database objects. Whether you're working with a single database, multiple schemas, or various database types, Storage Units help you navigate and manage your data efficiently.

## What Are Storage Units?

Storage Units are logical containers that group related database objects. The exact structure depends on your database type and configuration.

![Storage Unit List](/images/05-storage-unit-list.png)

A Storage Unit typically corresponds to a schema in PostgreSQL, a database in MySQL, or a database in MongoDB. For Redis, each database number is a separate Storage Unit.

### Storage Unit Types by Database

Schemas within a database are Storage Units. Useful for multi-tenant or modular applications.

Each database is a Storage Unit. MySQL doesn't have true schemas like PostgreSQL.

Each database is a Storage Unit. Collections within are similar to tables.

Each database index (0-15) is a Storage Unit, containing Redis keys.

The entire SQLite file is one Storage Unit with no sub-divisions.

Enterprise versions may support additional database structures.

## Storage Unit List View

The Storage Unit list provides an overview of all available Storage Units in your current connection.

![Storage Unit List with Sidebar](/images/06-storage-unit-list-with-sidebar.png)

### Viewing Storage Units

From the main WhoDB interface, look for the Storage Units view. This is typically accessible from:
- A "Storage Units" tab or menu item
- The main dashboard when no table is selected
- A dedicated view in the sidebar

Each Storage Unit displays:
- **Name**: The identifier (schema name, database name, or index)
- **Type**: What kind of storage unit (Schema, Database, Collection, etc.)
- **Table Count**: Number of tables/collections it contains
- **Size**: Total storage space used
- **Created**: When it was created
- **Last Modified**: Last time it was modified

``` PostgreSQL Storage Units (Schemas)
public              │ 24 tables  │ 85 MB
auth                │ 5 tables   │ 3 MB
reporting           │ 12 tables  │ 120 MB
temporal_data       │ 8 tables   │ 450 MB
```
``` MySQL Storage Units (Databases)
production_db       │ 42 tables  │ 2.5 GB
staging_db          │ 42 tables  │ 2.3 GB
testing_db          │ 42 tables  │ 1.8 GB
analytics_db        │ 15 tables  │ 5 GB
```

## Managing Storage Units

### Selecting a Storage Unit

Click on any Storage Unit from the list to select it.

The sidebar updates to show all tables in the selected Storage Unit.

Use the sidebar navigation (see Sidebar Navigation guide) to browse and work with tables.

### Storage Unit Information

Hovering over or clicking a Storage Unit shows:
- **Full Name**: Complete identifier
- **Object Count**: Total number of tables/collections
- **Data Size**: Storage used by data
- **Index Size**: Storage used by indexes
- **Total Size**: Combined data and index storage
- **Created Date**: Creation timestamp
- **Last Modified**: Last change timestamp
- **Owner/Creator**: Database user who created it

Access detailed properties by:
1. Right-clicking the Storage Unit
2. Selecting "Properties" or "Details"
3. Viewing configuration information

``` PostgreSQL Schema Properties
Name: public
Owner: postgres
Description: Standard schema
Collation: en_US.UTF-8
Tables: 24
Views: 5
Indexes: 63
Functions: 12
```

List can be sorted by:
- **Name**: Alphabetically A-Z
- **Size**: Largest or smallest first
- **Created**: Newest or oldest first
- **Modified**: Recently changed first
- **Table Count**: Most or least tables

## Viewing Tables in Storage Units

### Table List

When a Storage Unit is selected, all its tables appear in the sidebar with options for viewing and managing them.

The sidebar displays all tables in the selected Storage Unit with:
- Table name
- Row count (if available)
- Last modified date
- Table type (regular, view, temporary)

View table schema and metadata

Open table in Data View to see rows

Access context menu with all options

Find tables by name quickly

Once a table is selected:
- View all columns with their data types
- See primary keys and foreign keys
- Review indexes and constraints
- Understand relationships with other tables

### Table Types Within Storage Units

Standard tables containing data:
- Store actual data rows
- Have primary keys and relationships
- Can be indexed and queried
- Most common table type

``` Example Regular Tables
users              │ 15,234 rows
products           │ 3,456 rows
orders             │ 89,123 rows
order_items        │ 342,567 rows
```

Virtual tables defined by queries:
- Don't store data directly
- Represent a query result
- Useful for simplifying complex queries
- May be read-only or updateable

```
CREATE VIEW active_users AS
  SELECT * FROM users
  WHERE status = 'active'
    AND deleted_at IS NULL;
```

Tables that exist only for the session:
- Created with CREATE TEMPORARY TABLE
- Automatically deleted when session ends
- Private to your connection
- Useful for complex data processing

Temporary tables are not shown in all views. They disappear when you disconnect.

Internal database system tables:
- PostgreSQL: pg_* tables, information_schema
- MySQL: information_schema, mysql database
- Usually read-only
- Contain database metadata

Avoid modifying system tables. WhoDB typically hides these by default to prevent accidental changes.

## Storage Capacity and Growth

### Understanding Storage Usage

Each Storage Unit shows its total size:
- Data size: Actual row data
- Index size: Storage for indexes
- Total size: Combined usage

The table list shows individual table sizes. Identify which tables consume the most space:

``` Storage Analysis Example
logs table              │ 45 GB  │ Growing
archive_2023 table      │ 32 GB  │ Static
events table            │ 28 GB  │ Growing
user_sessions table     │ 12 GB  │ Stable
```

Growing tables like "logs" may need archival strategies or cleanup jobs.

Understanding storage usage helps with:
- Capacity planning
- Budget forecasting
- Performance optimization (compress old data)
- Backup strategy (larger databases need more storage)

### Identifying Growth Patterns

Tables that grow quickly over time may need:
- Partitioning (split into smaller pieces)
- Archival (move old data to separate storage)
- Purging (delete unnecessary old records)
- Compression (reduce data size)

Rapidly growing tables can cause performance degradation. Address growth before it becomes a problem.

Tables that don't change much:
- Good candidates for aggressive indexing
- Unlikely to benefit from partitioning
- Can use materialized views for complex queries

Tables organized by time period:
```
transactions_2023_q1    │ 15 GB
transactions_2023_q2    │ 14 GB
transactions_2023_q3    │ 16 GB
transactions_2023_q4    │ 17 GB
```
Good for time-based archival and cleanup strategies.

## Navigation Workflows

### Switching Between Storage Units

The Storage Unit name typically appears in the header or sidebar.

Click on another Storage Unit to switch to it.

The table list immediately updates to show tables from the new Storage Unit.

For quick comparisons, use browser tabs:
- Open two WhoDB windows
- Keep different Storage Units in each
- Compare side-by-side

### Exploring Complex Schemas

For databases with many Storage Units:

If a search function is available:
1. Type the schema/database name
2. Find it quickly in large lists
3. Double-click to switch to it

Mentally organize units by purpose:
- `*_prod` units: Production data
- `*_staging` units: Pre-production testing
- `*_dev` units: Development/testing
- `analytics_*` units: Reporting and analysis

Mark frequently-used Storage Units as favorites for quick access.

## Database-Specific Storage Unit Behavior

### PostgreSQL Schemas

Default schema where tables are created if not specified

Application-specific schemas for organization

pg_catalog and other system schemas (hidden by default)

PostgreSQL searches schemas in path order

``` PostgreSQL Schema Operations
-- Create new schema
CREATE SCHEMA IF NOT EXISTS app_schema;

-- Set default schema for session
SET search_path TO app_schema, public;

-- View all schemas
SELECT * FROM information_schema.schemata;

-- Move table between schemas
ALTER TABLE old_schema.table_name SET SCHEMA new_schema;
```

### MySQL Databases

In MySQL, "database" and "schema" are synonymous. What PostgreSQL calls schemas, MySQL calls databases.

MySQL has several system databases:
- `information_schema`: Metadata about all databases
- `mysql`: User accounts and permissions
- `performance_schema`: Performance metrics
- `sys`: Performance monitoring views

```
-- Create new database
CREATE DATABASE app_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- Switch database
USE app_db;

-- View all databases
SHOW DATABASES;

-- Delete database
DROP DATABASE app_db;
```

### MongoDB Databases

In MongoDB, each Storage Unit represents a database containing collections (similar to tables):

``` MongoDB Structure
myapp (Database/Storage Unit)
├── users (Collection)
├── products (Collection)
├── orders (Collection)
└── order_items (Collection)

analytics (Database/Storage Unit)
├── user_events (Collection)
├── page_views (Collection)
└── error_logs (Collection)
```

MongoDB databases and collections are lazily created—they don't exist until data is written to them.

### Redis Databases

Redis supports 16 databases (0-15) by default, each isolated and independent:

``` Redis Database Structure
Database 0 (Default)    │ Cache data
Database 1              │ Session data
Database 2              │ Queue data
Database 3-15           │ Additional storage
```

```
-- Select database
SELECT 1

-- View keys in database
KEYS *

-- Get database info
INFO

-- Flush database (delete all keys)
FLUSHDB
```

Switching Redis databases in WhoDB changes your entire view. Be cautious not to accidentally operate on the wrong database.

## Best Practices

Before working extensively with WhoDB, understand your database's Storage Unit organization:
- How many storage units exist
- What each unit contains
- Naming conventions used
- Which units are production vs. development

When designing your database:
- Group related tables in the same Storage Unit
- Use clear, descriptive names (production_db vs. prod)
- Separate concerns (auth from business logic)
- Document your structure

Regularly check Storage Unit sizes:
- Identify rapidly growing units
- Plan for capacity needs
- Address performance issues early
- Consider archival strategies

If possible, restrict access to sensitive Storage Units:
- Use read-only accounts for analytics units
- Limit direct access to production units
- Archive or delete old data as appropriate

## Troubleshooting Storage Unit Issues

**Problem**: Expected Storage Unit (schema/database) doesn't show

**Solutions**:
1. Verify the Storage Unit exists (use database admin tools)
2. Check that your user account has access to it
3. Refresh the page to reload the list
4. Verify the connection is to the correct server
5. Check for permission/role restrictions

**Problem**: Clicking a Storage Unit doesn't switch to it

**Solutions**:
1. Ensure you're connected to the database
2. Verify the Storage Unit is accessible (not deleted)
3. Check browser console for errors
4. Try refreshing the page
5. Reconnect to the database

**Problem**: Table counts or storage sizes show as "unknown" or "—"

**Solutions**:
1. WhoDB may still be calculating statistics
2. Your user account may lack permission to view statistics
3. The database may be very large (calculation takes time)
4. Refresh the page to retry calculation
5. Try with a more privileged account

**Problem**: Storage size seems inaccurate

**Explanations**:
- Size estimates can be approximate in some databases
- Deleted data may still occupy space until vacuum/optimize runs
- Indexes take additional space not reflected in table size
- Some databases report sizes differently
- Run database maintenance to get accurate sizes

## Performance Considerations

### Impact of Large Storage Units

Storage Unit size can affect WhoDB performance:
- Listing many tables takes longer
- Loading Storage Unit information takes time
- Very large units (thousands of tables) may be slow

### Optimization Strategies

Instead of one huge Storage Unit, split into logical units:
- Faster loading and navigation
- Better organization
- Clearer permissions and access control

Move historical data to separate Storage Units:
- Keeps active Storage Units performant
- Makes backup/archival easier
- Improves query performance on active data

For very large tables, partition by date or range:
- Improves query performance
- Makes maintenance easier
- Enables selective backups

## Next Steps

Master efficient table navigation in the sidebar

Explore table structure and relationships

Browse and work with table data

Configure and manage database connections

Understanding Storage Units is fundamental to efficient WhoDB navigation. Spend time exploring your database's structure to become a WhoDB power user.

---


# ai/introduction.mdx
## AI Chat Assistant - Introduction

Transform how you interact with databases using WhoDB's natural language AI assistant

# AI Chat Assistant - Introduction

WhoDB's AI Chat Assistant revolutionizes database interaction by letting you use natural language instead of SQL. Ask questions in plain English, and the assistant generates accurate queries, executes them, and presents results in an intuitive format.

The AI Chat Assistant is like having an expert database administrator and SQL developer available 24/7

## What is the AI Chat Assistant?

The AI Chat Assistant bridges the gap between human language and database queries. Instead of remembering SQL syntax, table names, or join conditions, you simply describe what you want to know.

![Chat Initial Page](/images/101-chat-initial-page.png)

### Key Capabilities

Ask questions in plain English and get structured results

The assistant writes optimized SQL based on your database schema

Works with PostgreSQL, MySQL, MongoDB, SQLite, Redis, and more

Choose from OpenAI, Anthropic, Ollama, or custom providers

Maintains conversation history for follow-up questions

Requires explicit confirmation for updates, deletes, and inserts

## How It Works

The AI Chat Assistant follows a simple workflow:

Type your question in natural language:
- "Show me all users"
- "What's the total revenue this month?"
- "Find products that are out of stock"

The assistant analyzes your question and your database schema to create the appropriate SQL query.

The assistant has full awareness of your tables, columns, relationships, and database type

WhoDB runs the generated SQL against your database with proper security and timeout controls.

Results appear in an interactive table with sorting, filtering, and export capabilities.

## Who Should Use the AI Assistant?

### Complete Beginners

Never written SQL before? The AI assistant is perfect for you:
- No SQL knowledge required
- Learn by seeing how questions translate to queries
- Explore databases without fear of making mistakes
- Get immediate results without syntax errors

### Data Analysts

Speed up your analysis workflow:
- Generate complex aggregations in seconds
- Explore data relationships naturally
- Create ad-hoc reports without writing SQL
- Focus on insights, not query syntax

### Developers

Accelerate development tasks:
- Quickly understand unfamiliar database schemas
- Test queries before implementing in code
- Debug data issues in development and production
- Prototype features without writing boilerplate SQL

### Database Administrators

Simplify routine tasks:
- Run diagnostics with natural language commands
- Generate reports for stakeholders
- Validate data integrity
- Perform maintenance queries efficiently

## Example Interactions

### Simple Data Retrieval

**You**: "Show me all users"

**Assistant**: "Here are all the users in the database."

Result: Interactive table with all user records

---

**You**: "Find users created in the last week"

**Assistant**: [Generates and executes appropriate date filter query]

Result: Filtered user list with recent signups

### Complex Analysis

**You**: "Count users by email domain"

**Assistant**: "Here's the user count by email domain."

Result: Aggregated data showing domain distribution

---

**You**: "Show average order value by month for the last year"

**Assistant**: [Generates query with date grouping and aggregation]

Result: Monthly revenue analysis table

### Data Modification

**You**: "Delete user with id 5"

**Assistant**: "Are you sure you want to delete this user? This action cannot be undone. Please confirm to proceed."

**You**: "Yes, delete it"

**Assistant**: [Executes DELETE and confirms]

Result: "Action Executed - 1 row affected"

## Supported AI Providers

WhoDB supports multiple AI providers, giving you flexibility based on your needs:

**Best for**: Most users, general-purpose queries

**Models Available**:
- GPT-4: Most capable, best for complex queries
- GPT-3.5 Turbo: Fast, cost-effective for simple queries

**Requirements**: OpenAI API key

**Pros**:
- Highly accurate SQL generation
- Fast response times
- Excellent natural language understanding

**Cons**:
- Requires paid API key
- Data sent to external servers

**Best for**: Complex reasoning, large context windows

**Models Available**:
- Claude 3.5 Sonnet: Balanced performance and speed
- Claude 3 Opus: Maximum capability

**Requirements**: Anthropic API key

**Pros**:
- Excellent handling of complex queries
- Very large context windows
- Strong reasoning capabilities

**Cons**:
- Requires paid API key
- Data sent to external servers

**Best for**: Privacy-focused deployments, no API costs

**Models Available**:
- Llama 3.1: Open-source, highly capable
- Mistral: Fast, efficient
- CodeLlama: Optimized for code generation
- Many others available

**Requirements**: Ollama installed locally

**Pros**:
- Complete data privacy (runs locally)
- No API costs
- No internet required
- Full control over model selection

**Cons**:
- Requires local installation
- Slower than cloud providers
- Requires sufficient local hardware

**Best for**: Enterprise deployments, specific requirements

Configure custom AI endpoints for:
- Self-hosted models
- Enterprise AI platforms
- Specialized database assistants

Contact your administrator for configuration details.

## Privacy and Security

### Data Privacy Considerations

**What's Sent**: Your database schema and query text
**Not Sent**: Actual database data or query results
**Consideration**: Review your organization's policies on external AI services

**What's Sent**: Nothing—all processing is local
**Privacy**: Complete data isolation
**Consideration**: Ideal for sensitive or regulated environments

For highly sensitive data, use local models (Ollama) or consult your security team before enabling external AI providers

### Security Features

- **Confirmation Required**: All data modifications require explicit confirmation
- **Read-Only Mode**: Can be configured with read-only database users
- **Query Timeouts**: Automatic timeout prevents runaway queries
- **Audit Trail**: All queries are logged for compliance
- **Permission Aware**: Respects your database user permissions

## Getting Started

Ready to use the AI Chat Assistant? Follow these steps:

Decide between external providers (OpenAI, Anthropic) or local models (Ollama).

- **Use External Providers** if: You want the best accuracy and speed
- **Use Ollama** if: Privacy is critical or you want zero API costs

Configure your chosen AI provider with necessary credentials.

[Learn how to set up providers →](/ai/setup-providers)

Navigate to the Chat page and begin exploring your database.

[See querying guide →](/ai/querying-data)

## AI Assistant Features Overview

### Core Features

- **Natural Language Understanding**: Ask questions conversationally
- **Schema Awareness**: Knows your tables, columns, and relationships
- **SQL Generation**: Creates optimized, database-specific queries
- **Result Display**: Interactive tables with full functionality
- **Error Handling**: Clear explanations when queries fail

### Advanced Features

- **Conversation Context**: Follow-up questions build on previous queries
- **Multi-Turn Dialogues**: Complex analysis through iterative refinement
- **SQL Code View**: Toggle between results and generated SQL
- **Scratchpad Integration**: Move queries to Scratchpad for refinement
- **Query History**: Navigate through previous questions with keyboard shortcuts

### Data Operations

- **SELECT Queries**: Retrieve and analyze data
- **Aggregations**: COUNT, SUM, AVG, MIN, MAX automatically applied
- **Filtering**: WHERE conditions from natural language
- **Sorting**: ORDER BY inferred from context
- **Joins**: Multi-table queries generated correctly
- **INSERT/UPDATE/DELETE**: Safe data modifications with confirmation

## Use Case Examples

Quickly understand unfamiliar databases

Generate reports without writing SQL

See how natural language translates to SQL

Find data quality issues efficiently

## Comparison: Traditional SQL vs. AI Assistant

| Task | Traditional Approach | With AI Assistant |
|------|---------------------|-------------------|
| Find all users | Write `SELECT * FROM users` | Ask "Show me all users" |
| Complex aggregation | Write GROUP BY with multiple joins | Ask "Count orders by product category" |
| Filter by date | Remember date syntax for your database | Ask "Show orders from last month" |
| Debug query | Read error message, fix syntax | Rephrase question naturally |
| Learn schema | Query information_schema tables | Ask "What tables exist?" |
| Data modification | Write UPDATE/DELETE carefully | Describe change, confirm when prompted |

The AI assistant eliminates syntax memorization and reduces the time from question to insight.

## Limitations

While powerful, the AI assistant has some limitations:

Extremely complex multi-step operations may require:
- Breaking into multiple queries
- Manual refinement in Scratchpad
- Traditional SQL for edge cases

The assistant respects your database user permissions:
- Cannot perform operations beyond granted privileges
- No elevation of user permissions
- Same access as manual SQL queries

Query generation depends on:
- AI provider speed
- Network latency (for external providers)
- Database query execution time
- Schema complexity

While highly accurate, the assistant may occasionally:
- Misinterpret ambiguous questions
- Generate suboptimal queries for very specific needs
- Require clarification for context-dependent requests

Always review generated SQL for critical operations.

## Best Practices

Start specific, then generalize. Instead of "show me the data," ask "show me all users created this month"

Key practices for effective AI assistant use:

1. **Be Specific**: Clear questions get better results
2. **Provide Context**: Mention table names when ambiguous
3. **Verify SQL**: Review generated queries for important operations
4. **Use Confirmation**: Take advantage of modification confirmations
5. **Leverage Context**: Build on previous questions in conversation
6. **Move to Scratchpad**: Save useful queries for reuse

[Learn more best practices →](/best-practices/ai-usage)

## Next Steps

Configure OpenAI, Anthropic, or Ollama for your needs

Learn how to ask questions and retrieve data

Step-by-step tutorial for beginners

Safely update, insert, and delete records

The AI Chat Assistant makes database interaction accessible to everyone, from complete beginners to experienced professionals

---


# ai/setup-providers.mdx
## Setting Up AI Providers

Configure OpenAI, Anthropic, Ollama, or custom AI providers for WhoDB's chat assistant

# Setting Up AI Providers

Before you can use WhoDB's AI Chat Assistant, you need to configure at least one AI provider. This guide walks you through setting up each supported provider and choosing the right model for your needs.

## Accessing the AI Configuration

Navigate to the Chat page in WhoDB to access AI provider settings:

![Chat Initial Page](/images/101-chat-initial-page.png)

The provider configuration is located at the top of the Chat interface with two dropdowns:
1. **AI Provider**: Select your provider (OpenAI, Anthropic, Ollama, etc.)
2. **AI Model**: Choose the specific model to use

## Choosing Your AI Provider

Different providers offer different trade-offs between accuracy, speed, cost, and privacy:

**Best for**: Most users, general-purpose queries
- Industry-leading accuracy
- Fast response times
- Pay-per-use pricing

**Best for**: Complex reasoning, large contexts
- Excellent with sophisticated queries
- Very large context windows
- Strong safety features

**Best for**: Privacy, zero API costs
- Complete data privacy
- No internet required
- Free to use

**Best for**: Enterprise deployments
- Self-hosted models
- Organization-specific configurations
- Complete control

## Setting Up OpenAI

OpenAI provides GPT models that offer excellent SQL generation capabilities with fast response times.

### Prerequisites

Before configuring OpenAI:
1. Create an OpenAI account at https://platform.openai.com/
2. Add payment method to your account
3. Generate an API key from https://platform.openai.com/api-keys

Keep your OpenAI API key secure. Never share it or commit it to version control

### Configuration Steps

On the Chat page, click the AI Provider dropdown.

![AI Provider Dropdown](/images/102-chat-ai-provider-dropdown.png)

Click **"Add a provider"** at the bottom of the dropdown menu.

A sheet will slide in from the right side of the screen.

In the Model Type dropdown, select **OpenAI**.

The dropdown shows icons for each provider to help identify them quickly.

Paste your OpenAI API key in the Token field.

``` Example API Key Format
sk-proj-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

API keys starting with `sk-proj-` are project-specific keys recommended by OpenAI

Click the **Submit** button to test the connection and save your configuration.

WhoDB will:
- Verify the API key is valid
- Fetch available models
- Save the configuration for future use

Once configured, the AI Model dropdown will populate with available models:
- **GPT-4**: Best accuracy, slower, higher cost
- **GPT-3.5 Turbo**: Fast, cost-effective, good accuracy

![AI Model Dropdown](/images/103-chat-ai-model-dropdown.png)

### Available OpenAI Models

| Model | Best Use Case | Speed | Cost per 1M Tokens (Input) | Context Window |
|-------|--------------|-------|---------------------------|----------------|
| GPT-4 | Complex queries, highest accuracy | Slower | ~$10 | 8K tokens |
| GPT-4 Turbo | Balanced performance | Medium | ~$1 | 128K tokens |
| GPT-3.5 Turbo | Simple queries, fast results | Fast | ~$0.50 | 16K tokens |

Start with GPT-3.5 Turbo for most queries. Upgrade to GPT-4 if you need better accuracy for complex scenarios

### Cost Considerations

OpenAI charges based on tokens used (roughly 4 characters per token):

**Typical Costs Per Query**:
- Simple query (e.g., "show me all users"): $0.001 - $0.002
- Complex query with large schema: $0.005 - $0.01
- Conversation with follow-ups: $0.02 - $0.05

**Estimate Monthly Costs**:
- Light use (10 queries/day): $3 - $5/month
- Moderate use (50 queries/day): $15 - $25/month
- Heavy use (200 queries/day): $60 - $100/month

Costs vary based on schema size, conversation length, and model choice. Monitor usage in your OpenAI dashboard

## Setting Up Anthropic (Claude)

Anthropic's Claude models excel at complex reasoning and handle large database schemas exceptionally well.

### Prerequisites

1. Create an Anthropic account at https://console.anthropic.com/
2. Add payment method
3. Generate an API key from the console

### Configuration Steps

Click the AI Provider dropdown and select **"Add a provider"**.

Choose **Anthropic** from the Model Type dropdown.

Paste your Anthropic API key in the Token field.

``` Example API Key Format
sk-ant-api03-xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

Click Submit to verify and save the configuration.

Available Claude models will appear in the Model dropdown:
- **Claude 3.5 Sonnet**: Balanced speed and capability
- **Claude 3 Opus**: Maximum reasoning capability
- **Claude 3 Haiku**: Fastest, most cost-effective

### Available Claude Models

| Model | Best Use Case | Speed | Cost per 1M Tokens (Input) | Context Window |
|-------|--------------|-------|---------------------------|----------------|
| Claude 3 Opus | Most complex queries | Slower | ~$15 | 200K tokens |
| Claude 3.5 Sonnet | Best balance | Medium | ~$3 | 200K tokens |
| Claude 3 Haiku | Speed and cost efficiency | Fast | ~$0.25 | 200K tokens |

Claude 3.5 Sonnet provides the best balance of speed, accuracy, and cost for most database queries

### When to Use Claude

Claude excels in these scenarios:
- **Large Schemas**: Handles databases with hundreds of tables
- **Complex Joins**: Better at understanding multi-table relationships
- **Contextual Queries**: Superior at maintaining long conversation contexts
- **Ambiguous Requests**: Better at asking clarifying questions

## Setting Up Ollama (Local Models)

Ollama allows you to run AI models locally on your machine, providing complete privacy with zero API costs.

### Prerequisites

Download and install Ollama from https://ollama.com

**Supported Platforms**:
- macOS (Apple Silicon and Intel)
- Linux (x86_64, ARM64)
- Windows (via WSL2)

Open a terminal and download your preferred model:

``` Popular Models
# Recommended: Llama 3.1 (8B)
ollama run llama3.1

# Alternative: CodeLlama (optimized for code)
ollama run codellama

# Alternative: Mistral (fast and efficient)
ollama run mistral

# Alternative: Llama 3.1 (70B - requires significant RAM)
ollama run llama3.1:70b
```

The first `ollama run` command downloads the model (several GB). Subsequent runs use the cached model

Check that Ollama is accessible:

```
curl http://localhost:11434/api/tags
```

You should see a JSON response with available models.

### Configuring Ollama in WhoDB

Navigate to the Chat page. If Ollama is running locally, it should appear automatically in the AI Provider dropdown.

Choose **Ollama** from the provider dropdown.

![AI Provider Dropdown](/images/102-chat-ai-provider-dropdown.png)

No API key required—Ollama connects automatically to localhost:11434

Select your downloaded model from the AI Model dropdown.

Available models are those you've downloaded via `ollama run`.

### Recommended Ollama Models for SQL

| Model | Size | RAM Required | Best For | Download Command |
|-------|------|-------------|----------|------------------|
| Llama 3.1 (8B) | 4.7 GB | 8 GB | General use, good balance | `ollama run llama3.1` |
| CodeLlama (7B) | 3.8 GB | 8 GB | Code/SQL generation | `ollama run codellama` |
| Mistral (7B) | 4.1 GB | 8 GB | Fast responses | `ollama run mistral` |
| Llama 3.1 (70B) | 40 GB | 64 GB | Maximum accuracy | `ollama run llama3.1:70b` |

Start with Llama 3.1 (8B) for the best balance of performance and resource usage

### Ollama Performance Optimization

**Minimum**:
- CPU: 4 cores
- RAM: 8 GB
- Disk: 10 GB free

**Recommended**:
- CPU: 8+ cores
- RAM: 16 GB+
- GPU: NVIDIA GPU with 8GB+ VRAM (optional but faster)
- Disk: 50 GB free for multiple models

Ollama automatically uses GPU if available:

**NVIDIA GPU**:
- Requires CUDA drivers
- Dramatically faster inference
- Supports larger models with less RAM

**Apple Silicon (M1/M2/M3)**:
- Native Metal acceleration
- Excellent performance
- No additional configuration needed

Check GPU usage:
```
ollama ps
```

Adjust model size based on your hardware:

**If queries are slow**:
- Use smaller models (7B instead of 70B)
- Close other applications
- Enable GPU acceleration
- Consider using cloud providers for complex queries

**If accuracy is poor**:
- Upgrade to larger models (70B)
- Provide more specific queries
- Use cloud providers (OpenAI/Anthropic) for critical tasks

### Ollama Privacy Benefits

Your database schema never leaves your machine

Works in air-gapped or offline environments

No API charges regardless of usage

Choose models, control updates, customize behavior

Ollama is ideal for regulated industries, sensitive data, or organizations requiring complete data sovereignty

## Advanced Configuration

### Multiple Providers

You can configure multiple AI providers and switch between them:

Add OpenAI, Anthropic, and Ollama providers using the "Add a provider" option.

Use the AI Provider dropdown to switch between configured providers at any time.

**Strategy**:
- Use Ollama for exploration and learning
- Use GPT-3.5 for quick production queries
- Use GPT-4 or Claude for complex analytics

This optimizes both cost and performance.

### Removing Providers

On the Chat page, click the **Delete Provider** button.

![Delete Provider Button](/images/115-chat-delete-provider-button.png)

A confirmation dialog appears. Click **Delete** to remove the current provider configuration.

This removes the API key and disconnects the provider. You'll need to reconfigure to use it again

## Provider Comparison

Choose the right provider for your needs:

| Feature | OpenAI | Anthropic | Ollama |
|---------|--------|-----------|--------|
| **Setup Complexity** | Easy | Easy | Moderate |
| **Cost** | Pay per use | Pay per use | Free |
| **Privacy** | External | External | Complete |
| **Speed** | Fast | Medium | Varies |
| **Accuracy** | Excellent | Excellent | Good |
| **Internet Required** | Yes | Yes | No |
| **Best For** | General use | Complex queries | Privacy/Cost |

## Troubleshooting

**Symptom**: "Invalid API key" error when adding OpenAI provider

**Solutions**:
1. Verify key is copied correctly (no extra spaces)
2. Check key hasn't been revoked in OpenAI dashboard
3. Ensure billing is set up on your OpenAI account
4. Try generating a new API key
5. Verify you're using a valid key format (starts with `sk-`)

**Symptom**: Cannot connect to Anthropic after entering API key

**Solutions**:
1. Verify API key from https://console.anthropic.com/
2. Check your account has available credits
3. Ensure no network/firewall blocking claude.ai
4. Try a new API key
5. Check Anthropic service status

**Symptom**: Ollama doesn't appear in provider dropdown

**Solutions**:
1. Verify Ollama is running: `curl http://localhost:11434/api/tags`
2. Restart Ollama service
3. Check Ollama is accessible on port 11434
4. Ensure no firewall blocking localhost connections
5. Verify at least one model is downloaded

Test Ollama:
```
ollama list  # Shows downloaded models
ollama ps    # Shows running models
```

**Symptom**: Model dropdown is empty after adding provider

**Solutions**:
1. Wait a few seconds for models to load
2. Refresh the page
3. Verify API key has proper permissions
4. Check provider dashboard for account status
5. Try removing and re-adding the provider

For Ollama:
```
ollama list  # Verify models are downloaded
```

**Symptom**: Ollama queries take very long to respond

**Solutions**:
1. Close resource-intensive applications
2. Use smaller models (7B instead of 70B)
3. Verify GPU acceleration is working: `ollama ps`
4. Increase system RAM allocation
5. Consider cloud providers for time-sensitive queries

Check resource usage:
```
# Monitor Ollama resource usage
ollama ps
top -p $(pgrep ollama)
```

**Symptom**: "Rate limit exceeded" from OpenAI or Anthropic

**Solutions**:
1. Wait before retrying (limits reset after time window)
2. Upgrade your API plan for higher limits
3. Reduce query frequency
4. Check usage in provider dashboard
5. Consider switching to Ollama for unlimited queries

## Security Best Practices

- Never share API keys publicly
- Don't commit keys to git repositories
- Use environment variables in production
- Rotate keys regularly (every 90 days)
- Monitor usage for unauthorized access

**Highly Sensitive Data**:
- Use Ollama exclusively
- Never send to external providers

**Moderately Sensitive Data**:
- Review provider terms of service
- Verify data handling policies
- Consider data residency requirements

**Public or Non-Sensitive Data**:
- Any provider acceptable

**OpenAI**: https://platform.openai.com/usage
**Anthropic**: https://console.anthropic.com/settings/usage

Set up billing alerts to prevent unexpected charges.

When creating API keys:
- Limit permissions to only what's needed
- Create separate keys for development/production
- Set spending limits where available
- Enable key restrictions (IP allowlists, etc.)

## Next Steps

Learn how to ask questions and retrieve data using your configured provider

Step-by-step tutorial to get started with the AI assistant

Master multi-turn conversations and context management

Learn optimal patterns for using the AI assistant

With your AI provider configured, you're ready to start querying your database using natural language

---


# ai/querying-data.mdx
## Querying Data with AI Chat

Master the art of retrieving and analyzing database data using natural language queries

# Querying Data with AI Chat

WhoDB's AI Chat Assistant transforms database querying from a technical task into a natural conversation. Ask questions in plain English and get instant results with interactive tables, generated SQL, and intelligent error handling.

The AI assistant understands your database schema and generates optimized queries specific to your database type

## Introduction to AI-Powered Querying

Traditional database querying requires knowledge of SQL syntax, table structures, and join relationships. The AI Chat Assistant eliminates these barriers by translating natural language into accurate SQL queries.

![Example Prompts](/images/104-chat-example-prompts.png)

### What Makes AI Querying Different?

Ask questions conversationally without memorizing syntax

The assistant knows your tables, columns, and relationships

See data immediately without writing a single line of SQL

View generated SQL to understand how queries work

## Getting Started with Simple Queries

### Asking Your First Question

The simplest way to start is by asking basic questions about your data:

Click the Chat icon in the sidebar to access the AI assistant.

In the input field at the bottom, type a natural language question:

``` Example Questions
Show me all users
List all products
Display recent orders
```

Send your message and wait for the AI to respond.

The assistant will display the results in an interactive table.

![SQL Query Results](/images/106-chat-sql-query-results.png)

### Simple Text Responses

For general questions or informational queries, the assistant provides text responses:

![Simple Text Response](/images/105-chat-simple-text-response.png)

**Example interactions:**
- "Hello" - Gets a greeting and overview of capabilities
- "What tables are available?" - Lists your database tables
- "Explain what the users table contains" - Provides schema information

Text responses are perfect for understanding your database structure before diving into queries

## Retrieving Data with SELECT Queries

The AI assistant excels at generating SELECT queries that retrieve data from your tables.

### Basic Data Retrieval

Ask for data in natural language:

``` Natural Language
Show me all users
```

The assistant generates and executes:

``` Generated SQL
SELECT * FROM users ORDER BY id
```

**Result**: Interactive table with all user records

### Retrieving Specific Columns

Be more specific about what you want to see:

``` Natural Language
Show me usernames and emails from the users table
```

**Generated SQL**:
```
SELECT username, email FROM users
```

Specify column names in your questions for more focused results

### Limiting Results

Control how much data you retrieve:

``` Natural Language Examples
Show me the first 10 users
Get 5 recent orders
Display 20 products
```

**Generated SQL**:
```
SELECT * FROM users ORDER BY id LIMIT 10
```

### Filtering Data

Add conditions to narrow down results:

``` Natural Language Examples
Show me users created after 2024-01-01
Find products that cost more than $100
Get orders with status 'completed'
Show me users with admin in their username
```

**Example Generated SQL**:
```
SELECT * FROM users
WHERE created_at > '2024-01-01'
ORDER BY created_at DESC
```

### Sorting Results

Request specific ordering:

``` Natural Language Examples
Show me users sorted by creation date
List products ordered by price descending
Display orders sorted by most recent first
```

**Generated SQL**:
```
SELECT * FROM users
ORDER BY created_at DESC
```

## Viewing SQL Code

One of the most powerful features is the ability to toggle between table results and the generated SQL code.

### SQL Code Toggle

When the assistant returns query results:

Above the results table, find the "View SQL" or code icon button.

Click the toggle to switch from table view to SQL code view.

![SQL Code View](/images/107-chat-sql-code-view.png)

Examine the generated SQL to understand how your question was translated.

Click the toggle again to return to the table view.

### Why View SQL Code?

See how natural language maps to SQL syntax. This is invaluable for learning or improving your SQL skills.

``` Example Learning
Question: "Count users by email domain"

Generated SQL:
SELECT
  SUBSTRING(email FROM POSITION('@' IN email) + 1) as domain,
  COUNT(*) as user_count
FROM users
GROUP BY domain
ORDER BY user_count DESC
```

You learn about string functions, aggregation, and grouping all at once.

For important queries, review the SQL to ensure it matches your intent:

- Check JOIN conditions
- Verify WHERE clauses
- Confirm GROUP BY logic
- Validate ORDER BY direction

Copy generated SQL to:
- Use in your application code
- Save in the Scratchpad for refinement
- Share with teammates
- Document your analysis process

If results aren't what you expected, viewing the SQL helps identify:
- Wrong table references
- Incorrect column names
- Missing filters
- Unexpected joins

## Complex Queries

The AI assistant handles sophisticated queries including aggregations, joins, and multi-step logic.

### Aggregation Queries

Count, sum, average, and other statistical operations:

``` Natural Language Examples
Count users by email domain
What's the average order value?
Show me total revenue by product category
Count orders grouped by status
Find the minimum and maximum prices
```

**Example Generated SQL**:
```
SELECT
  SUBSTRING(email FROM POSITION('@' IN email) + 1) as domain,
  COUNT(*) as user_count
FROM users
GROUP BY domain
ORDER BY user_count DESC
```

![Aggregation Query](/images/109-chat-aggregation-query.png)

### Available Aggregation Functions

Count records or non-null values
- "How many users do we have?"
- "Count orders by status"

Calculate totals
- "What's the total revenue?"
- "Sum order amounts by month"

Calculate averages
- "Average order value"
- "Mean product price by category"

Find extremes
- "Highest and lowest prices"
- "Most recent order date"

### Multi-Table Queries (Joins)

Ask questions that span multiple tables:

``` Natural Language Examples
Show me users and their order counts
List products with their category names
Display orders with customer information
Find users who have placed orders
```

**Example Generated SQL**:
```
SELECT
  u.id,
  u.username,
  COUNT(o.id) as order_count,
  SUM(o.total) as total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.username
ORDER BY total_spent DESC
```

The AI assistant automatically determines the correct JOIN type and conditions based on your schema

### Date-Based Queries

Filter and group by time periods:

``` Natural Language Examples
Show me orders from last month
Count signups by day for the past week
Display users created in 2024
Find orders placed this year
Show revenue grouped by month
```

**Example Generated SQL**:
```
SELECT
  DATE_TRUNC('month', created_at) as month,
  COUNT(*) as order_count,
  SUM(total) as revenue
FROM orders
WHERE created_at >= '2024-01-01'
GROUP BY DATE_TRUNC('month', created_at)
ORDER BY month DESC
```

### Pattern Matching

Search for records with specific patterns:

``` Natural Language Examples
Find users with gmail email addresses
Show products containing 'laptop' in the name
Get orders where customer name starts with 'A'
Find entries with 'test' in any field
```

**Generated SQL**:
```
SELECT * FROM users
WHERE email LIKE '%@gmail.com'
```

### Combining Multiple Conditions

Ask complex questions with multiple criteria:

``` Natural Language Examples
Show me active users who joined after 2024-01-01 and have made at least 5 orders
Find expensive products (over $100) that are in stock and on sale
Display recent orders that are completed and have a total over $50
```

**Example Generated SQL**:
```
SELECT u.*
FROM users u
WHERE u.status = 'active'
  AND u.created_at > '2024-01-01'
  AND (
    SELECT COUNT(*)
    FROM orders o
    WHERE o.user_id = u.id
  ) >= 5
```

## Understanding Query Results

When the AI assistant returns data, you get an interactive table with powerful features.

### Interactive Results Table

![SQL Query Results](/images/106-chat-sql-query-results.png)

**Table Features**:
- **Column Headers**: Show field names from your query
- **Sortable Columns**: Click headers to sort (if supported)
- **Scrollable**: Navigate large result sets
- **Readable Formatting**: Data types displayed appropriately
- **Row Numbers**: Track position in results

### Result Metadata

The assistant provides context about your results:

``` Example Result Context
"I'll retrieve all users from the database for you."

3 rows returned
Columns: id, username, email, password, created_at
```

### Null Values

NULL values in your data are clearly indicated:
- Empty cells for NULL
- Special indicators (varies by theme)
- Distinguishable from empty strings

### Large Result Sets

For queries returning many rows:
- Results may be paginated
- Scroll to view all data
- Consider adding LIMIT in your question

Very large result sets may take longer to load. Ask the assistant to limit results if you only need a sample

## Handling Errors

The AI assistant provides clear, helpful error messages when queries fail.

![Error Message](/images/108-chat-error-message.png)

### Common Error Types

**Error Example**:
```
ERROR: relation "test_schema.nonexistent_table" does not exist
```

**What It Means**: The table or column name doesn't exist in your schema.

**Solutions**:
- Check your table/column names
- Rephrase your question more clearly
- Ask "What tables are available?"
- Verify spelling and schema name

**Error Example**:
```
ERROR: permission denied for table users
```

**What It Means**: Your database user lacks permissions for this operation.

**Solutions**:
- Contact your database administrator
- Use a different database connection with appropriate permissions
- Ask for read-only alternatives

**Error Example**:
```
ERROR: syntax error at or near "FROM"
```

**What It Means**: The generated SQL has invalid syntax (rare but possible).

**Solutions**:
- Rephrase your question more clearly
- Break complex questions into simpler steps
- Provide more specific details about what you want

**Error Example**:
```
ERROR: column "price" is of type numeric but expression is of type text
```

**What It Means**: Data type incompatibility in the query.

**Solutions**:
- Clarify data types in your question
- Ask about the schema first
- Rephrase with correct type expectations

**Error Example**:
```
ERROR: query execution timeout
```

**What It Means**: The query took too long to execute.

**Solutions**:
- Add more specific filters to reduce data volume
- Ask for a limited result set
- Break down into smaller queries
- Contact administrator about database performance

### Recovering from Errors

The error message contains clues about what went wrong.

Try asking the same thing in a different way:

``` Original
Show me data from nonexistent_table

Rephrased
What tables are available?
```

Break complex queries into steps:

``` Complex (may fail)
Show me average order value by month for premium users who joined in 2024

Simpler (more reliable)
1. Show me premium users
2. [After reviewing] Show me order values for these users
3. [After reviewing] Calculate average by month
```

The assistant can explain what went wrong:

```
Why did my last query fail?
Can you explain the error message?
```

## Multi-Turn Conversations

One of the AI assistant's most powerful features is maintaining conversation context for follow-up questions.

![Multiple Messages](/images/112-chat-multiple-messages.png)

### Building on Previous Queries

The assistant remembers your conversation:

``` Conversation Flow
You: What is in the users table?
AI: The users table contains user account information including
    usernames, emails, and creation timestamps.

You: Show me when users were created
AI: [Executes query grouping users by creation date]
```

The second question builds on the context of the first without needing to specify "users table" again.

### Refining Results

Iteratively refine your queries:

``` Iterative Refinement
You: Show me all users
AI: [Returns 1000 users]

You: Just the first 10
AI: [Returns 10 users]

You: Sort by most recent
AI: [Returns 10 users sorted by created_at DESC]

You: Only active users
AI: [Returns 10 active users sorted by created_at DESC]
```

Each question builds on the previous context.

### Contextual Follow-Ups

Ask follow-up questions naturally:

``` Contextual Questions
You: Show me all orders
AI: [Returns orders]

You: How many are there?
AI: [Returns count of orders]

You: What about completed ones?
AI: [Returns count of completed orders]

You: Show me those
AI: [Returns completed orders]
```

Use pronouns like "them," "those," "these" naturally—the assistant understands what you're referring to

### Chat History Navigation

Navigate through your conversation history using keyboard shortcuts:

In the input field, press the Up arrow key.

Your previous message appears in the input field.

Keep pressing Up to go further back in your history.

Press Down to move forward through your history.

Modify the loaded message and press Enter to resend.

**Keyboard Shortcuts**:
- `Up Arrow`: Previous message
- `Down Arrow`: Next message
- `Ctrl/Cmd + Enter`: Send message

## Example Workflows

### Workflow 1: Data Exploration

Explore an unfamiliar database systematically:

```
What tables are available?
```

```
Describe the users table
```

```
Show me 5 sample users
```

```
How are users related to orders?
```

```
Count users by status
```

### Workflow 2: Ad-Hoc Reporting

Generate a quick business report:

```
How many orders were placed this month?
```

```
What's the total revenue?
```

```
Show me revenue by product category
```

```
Which products generated the most revenue?
```

```
How many customers made purchases this month?
```

### Workflow 3: Data Quality Check

Validate data integrity:

```
Show me users with missing email addresses
```

```
Find duplicate email addresses in the users table
```

```
Show me products with negative prices
```

```
Find orders without corresponding users
```

```
Show me invalid email formats in the users table
```

### Workflow 4: Learning SQL

Use the AI as a SQL tutor:

```
Show me all users
```
View the generated SQL (SELECT * FROM users)

```
Show me active users
```
View SQL to learn WHERE clauses

```
Count users by status
```
View SQL to understand GROUP BY

```
Show me users and their order counts
```
View SQL to learn JOIN syntax

Use the "Move to Scratchpad" feature to experiment with modifications

## Moving Queries to Scratchpad

When you generate a useful query, save it for future use:

![Move to Scratchpad Dialog](/images/113-chat-move-to-scratchpad-dialog.png)

Locate a query result with SQL you want to save.

Click the "Move to Scratchpad" button (usually near the SQL view toggle).

**New Page**: Creates a new Scratchpad page with this query
**Existing Page**: Adds to an existing Scratchpad page

Provide a page name if creating new.

Click Confirm to move the query.

Navigate to Scratchpad to find your saved query, where you can:
- Edit and refine it
- Add comments
- Execute multiple times
- Share with teammates

Queries moved to Scratchpad retain their original SQL exactly as generated by the AI

## Best Practices

Begin with clear, specific questions:

**Good**: "Show me users created after January 1, 2024"
**Avoid**: "Show me some users"

If you have multiple tables with similar columns:

**Good**: "Count active records in the users table"
**Avoid**: "Count active records" (which table?)

Don't try to speak SQL syntax:

**Good**: "Show me users who joined in the last week"
**Avoid**: "SELECT * FROM users WHERE created_at > NOW() - INTERVAL '7 days'"

Before acting on results for critical decisions:

1. Toggle to SQL view
2. Verify the query logic
3. Check JOINs and WHERE conditions
4. Confirm aggregation functions

For sophisticated analysis:

1. Start with basic query
2. Verify results
3. Add filters or aggregations
4. Verify again
5. Continue building

Take advantage of conversation history:

- Reference previous queries
- Use follow-up questions
- Build on earlier results

Complex queries on large tables may take time:

- Add LIMIT for initial exploration
- Use specific filters to reduce data volume
- Break very complex operations into steps

## Keyboard Shortcuts

Efficient keyboard navigation speeds up your workflow:

| Shortcut | Action |
|----------|--------|
| `Up Arrow` | Previous message in history |
| `Down Arrow` | Next message in history |
| `Enter` | Send message |
| `Ctrl/Cmd + Enter` | Send message (alternative) |
| `Escape` | Clear input field |
| `Ctrl/Cmd + K` | Focus on input field |

## Troubleshooting

**Symptom**: The generated query doesn't match what you wanted.

**Solutions**:
1. Rephrase using different words
2. Be more specific about table and column names
3. Break complex questions into simpler parts
4. Ask the assistant to explain what it understood
5. Provide an example of what you want to see

Try: "Show me users who signed up recently" instead of "Get the new people"

**Symptom**: Query executes but returns unexpected data.

**Solutions**:
1. View the generated SQL to understand the logic
2. Check if filters are applied correctly
3. Verify you're querying the right table
4. Ask "What columns are in the [table] table?"
5. Add more specific conditions to your question

Try: "Show me active users with more than 5 orders" to clarify conditions

**Symptom**: The assistant or query execution is very slow.

**Solutions**:
1. Add LIMIT to your question: "Show me the first 100 users"
2. Add specific filters to reduce data volume
3. Break large operations into smaller queries
4. Check with your administrator about database performance
5. Use more specific time ranges for date filters

Try: "Show me users from the last month limit 50" instead of "Show me all users"

**Symptom**: You want to re-run a query but can't find it.

**Solutions**:
1. Use Up arrow to navigate through message history
2. Scroll up in the chat to find the previous response
3. Check Scratchpad if you moved the query there
4. Ask the assistant to repeat: "Can you show me that last query again?"

Next time: Move important queries to Scratchpad for permanent storage

**Symptom**: You expected a table but got a text explanation.

**Solutions**:
1. Make your question more action-oriented
2. Use verbs like "show," "list," "display," "find"
3. Specify what data you want to see
4. Avoid purely informational questions when you want data

Try: "Show me all users" instead of "Tell me about users"

**Symptom**: The result table doesn't include columns you need.

**Solutions**:
1. Explicitly mention the columns you want
2. Review the generated SQL to see what was selected
3. Ask a follow-up: "Show me the same data but include [column name]"
4. Be specific: "Show me id, username, and email from users"

Try: "Show me user id, username, email, and status" for complete results

## Limitations

Understanding limitations helps set appropriate expectations:

While the AI handles sophisticated queries well, some extremely complex operations may require:
- Manual SQL in Scratchpad
- Breaking into multiple simpler queries
- Custom query optimization

**Example Complex Cases**:
- Recursive CTEs with deep nesting
- Dynamic pivot operations
- Complex window functions with multiple partitions

The AI adapts to your database type but:
- Some advanced, database-specific functions may need clarification
- Proprietary extensions might not be fully understood
- Non-standard syntax may require manual refinement

**Solution**: Move to Scratchpad for advanced database-specific features

The AI generates correct queries but may not always produce the most optimized version for:
- Very large tables (millions of rows)
- Complex join scenarios
- Specific index usage

**Solution**: Review and optimize generated SQL in Scratchpad for performance-critical queries

If your question could be interpreted multiple ways:
- The AI will make its best guess
- May ask clarifying questions
- Might not choose the interpretation you intended

**Solution**: Be as specific as possible, especially with column names and conditions

## Privacy and Security

For sensitive data, consider using local AI models (Ollama) instead of cloud providers

### What Gets Sent to AI Providers

**Sent to AI**:
- Your question text
- Database schema (table and column names)
- Database type (PostgreSQL, MySQL, etc.)

**Never Sent**:
- Actual data from your tables
- Query results
- Connection credentials
- Passwords or sensitive configuration

### Best Practices for Sensitive Data

Configure Ollama for complete data privacy—nothing leaves your machine.

Check your company's policies on using external AI services before enabling cloud providers.

If table/column names contain sensitive information, use Ollama or rename schema elements.

Configure your database connection with read-only permissions for exploratory querying.

## Next Steps

Learn how to safely update, insert, and delete data using the AI assistant

Master advanced conversation features and context management

Move generated queries to Scratchpad for refinement and reuse

Discover optimal patterns for AI-assisted database work

You now have the skills to query databases naturally using AI, view and understand generated SQL, and handle complex data retrieval scenarios

---


# ai/modifying-data.mdx
## Modifying Data with AI Chat Assistant

Learn how to safely insert, update, and delete data using WhoDB's AI Chat Assistant with built-in confirmation safeguards

# Modifying Data with AI Chat Assistant

WhoDB's AI Chat Assistant enables you to modify database records using natural language while maintaining strict safety controls. Every data modification operation requires explicit confirmation before execution, preventing accidental changes to your database.

Data modification operations are permanent and affect your actual database. Always review confirmation prompts carefully before proceeding.

## Understanding AI-Powered Data Modification

The AI Chat Assistant translates your natural language instructions into SQL data modification statements (INSERT, UPDATE, DELETE). Unlike read-only queries, modification operations follow a confirmation workflow to ensure safety.

### Key Safety Features

All INSERT, UPDATE, and DELETE operations require manual confirmation before execution

See exactly what SQL will execute before confirming

Clear confirmation when operations complete successfully

Detailed error messages if modifications fail

### How the Confirmation Workflow Works

Ask the AI assistant to insert, update, or delete data using natural language.

The assistant analyzes your request and generates the appropriate SQL statement.

A dialog displays the SQL that will execute and asks for explicit confirmation.

![Action Confirmation Dialog](/images/110-chat-action-confirmation.png)

Review the SQL statement carefully. Confirm to proceed or cancel to abort.

After confirmation, the SQL executes against your database.

An "Action Executed" message appears confirming the operation completed.

![Action Executed Feedback](/images/111-chat-action-executed.png)

The confirmation workflow ensures you always know exactly what changes will be made before they occur.

## INSERT Operations - Adding Records

Use natural language to add new records to your tables. The AI assistant generates appropriate INSERT statements based on your table schema.

### Basic Insert Examples

**Simple Single Record**

``` User Request
Add a new user named John Doe with email john@example.com
```

The AI generates:
``` Generated SQL
INSERT INTO users (name, email) VALUES ('John Doe', 'john@example.com')
```

**With Multiple Columns**

``` User Request
Create a new product named Laptop with price 999.99, category Electronics, and stock 50
```

The AI generates:
``` Generated SQL
INSERT INTO products (name, price, category, stock)
VALUES ('Laptop', 999.99, 'Electronics', 50)
```

**With Specific Schema**

``` User Request
Add a user to the test_schema with username test_user, email test@example.com, and password testpass123
```

The AI generates:
``` Generated SQL
INSERT INTO test_schema.users (username, email, password)
VALUES ('test_user', 'test@example.com', 'testpass123')
```

### INSERT Confirmation Process

Type your insert request in the chat interface.

The assistant creates an INSERT statement with values extracted from your request.

A confirmation dialog shows:
- The INSERT statement
- Which table will be affected
- What values will be inserted
- A clear action button to proceed

Click the confirmation button to execute the INSERT.

Look for the "Action Executed" message confirming the record was added.

### Best Practices for INSERT Operations

When inserting records, mention all columns that don't have default values or aren't auto-generated.

**Good**: "Add a user with name, email, and status"
**Avoid**: "Add a user" (missing required fields)

In databases with multiple schemas or similar table names, specify the full table path.

**Good**: "Add to test_schema.users"
**Avoid**: "Add to users" (ambiguous in multi-schema databases)

Provide values in the expected format for the column type.

**Good**: "Add order with date 2025-01-15"
**Avoid**: "Add order with date next Monday" (ambiguous)

Always check the generated INSERT statement in the confirmation dialog to ensure:
- Correct table is targeted
- All required columns are included
- Values are in the right order
- Data types match expectations

### Common INSERT Scenarios

**Adding User Records**
```
Add a new user John Smith with email john.smith@company.com and role admin
```

**Creating Orders**
```
Insert an order for customer_id 123 with total 259.99 and status pending
```

**Logging Events**
```
Add a log entry with message "System started" and level INFO
```

**Populating Test Data**
```
Create a test product with name Sample, sku TEST-001, and price 19.99
```

## UPDATE Operations - Modifying Records

Update existing records by describing what should change and which records to affect.

### Basic Update Examples

**Single Column Update**

``` User Request
Update the email for user with id 5 to newemail@example.com
```

The AI generates:
``` Generated SQL
UPDATE users SET email = 'newemail@example.com' WHERE id = 5
```

**Multiple Column Update**

``` User Request
Update user test_user to set email to newemail@example.com and status to active
```

The AI generates:
``` Generated SQL
UPDATE test_schema.users
SET email = 'newemail@example.com', status = 'active'
WHERE username = 'test_user'
```

**Conditional Updates**

``` User Request
Set all products in Electronics category to have discount 10
```

The AI generates:
``` Generated SQL
UPDATE products SET discount = 10 WHERE category = 'Electronics'
```

**Date-Based Updates**

``` User Request
Mark all orders from last month as archived
```

The AI generates:
``` Generated SQL
UPDATE orders SET status = 'archived'
WHERE created_at >= '2024-12-01' AND created_at < '2025-01-01'
```

### UPDATE Confirmation Process

Describe what you want to change and which records should be affected.

The assistant creates an UPDATE statement with SET clauses and WHERE conditions.

The confirmation dialog shows:
- The complete UPDATE statement
- Which table will be modified
- What columns will change
- Which records will be affected (WHERE clause)

Pay special attention to the WHERE clause to ensure you're updating the correct records.

Missing WHERE clauses will update ALL records in the table. Always verify the WHERE condition.

Click the confirmation button to execute the UPDATE.

Verify the "Action Executed" message and review affected row count.

### Best Practices for UPDATE Operations

Be explicit about which records to update to avoid accidentally modifying all records.

**Good**: "Update user with id 5"
**Dangerous**: "Update all users" (only if you mean all records)

Reference records by primary keys or unique columns when possible.

**Good**: "Update user with id 123"
**Risky**: "Update user named John" (may match multiple records)

Before confirming, mentally estimate how many rows should be affected.

If updating one user, expect 1 row affected. If the confirmation suggests more, cancel and refine your query.

For complex updates, first run a SELECT with the same WHERE clause to see which records will be affected.

**Step 1**: "Show me all inactive users"
**Step 2** (after reviewing): "Set all inactive users to status deleted"

Generally avoid updating primary key columns, as this can break relationships.

**Safe**: "Update user email"
**Risky**: "Update user id" (can break foreign key references)

### Common UPDATE Scenarios

**Changing User Information**
```
Update user with email old@company.com to new@company.com
```

**Adjusting Prices**
```
Increase all product prices in category Electronics by 10 percent
```

**Status Changes**
```
Set order status to shipped for order_id 1001
```

**Bulk Corrections**
```
Update all records where city is null to set city as Unknown
```

**Time-Based Updates**
```
Mark all pending orders older than 7 days as cancelled
```

## DELETE Operations - Removing Records

Delete operations require the most caution as they permanently remove data. The AI assistant provides explicit confirmation prompts for all DELETE requests.

### Basic Delete Examples

**Single Record Deletion**

``` User Request
Delete user with id 5
```

The AI responds with a confirmation request:
``` AI Response
Are you sure you want to delete this user? This action cannot be undone. Please confirm to proceed.
```

After you confirm with "Yes" or "Yes, delete it":
``` Generated SQL
DELETE FROM users WHERE id = 5
```

**Conditional Deletion**

``` User Request
Delete all inactive users
```

The AI requests confirmation:
``` AI Response
Are you sure you want to delete these users? This action cannot be undone. Please confirm to proceed.
```

After confirmation:
``` Generated SQL
DELETE FROM users WHERE status = 'inactive'
```

**Time-Based Deletion**

``` User Request
Remove all log entries older than 90 days
```

After confirmation:
``` Generated SQL
DELETE FROM logs WHERE created_at < NOW() - INTERVAL '90 days'
```

### DELETE Confirmation Process

Ask the AI to delete specific records.

The assistant responds with a warning message:
- Explains what will be deleted
- States the action cannot be undone
- Asks for explicit confirmation

Respond with a clear confirmation like:
- "Yes, delete it"
- "Confirm"
- "Yes, proceed"

The AI requires explicit confirmation language, not just "yes". This prevents accidental deletions.

After confirmation, the assistant creates the DELETE statement.

The confirmation dialog shows:
- The DELETE statement
- Which table will be affected
- The WHERE clause indicating which records will be removed

Click the confirmation button to execute the DELETE.

Check the "Action Executed" message and affected row count.

DELETE operations are permanent and cannot be undone through WhoDB. Only database backups can restore deleted data.

### Best Practices for DELETE Operations

Never delete without specifying which records to remove.

**Good**: "Delete user with id 5"
**Extremely Dangerous**: "Delete all users" (removes all records)

The AI will warn you, but always be explicit about what to delete.

Run a SELECT query first to verify which records will be deleted.

**Step 1**: "Show me all users with status inactive"
**Step 2** (after reviewing): "Delete all users with status inactive"

This two-step approach lets you verify the target records before deletion.

Know whether your DELETE will cascade to related tables.

If your schema has CASCADE DELETE rules, deleting one record might remove related records in other tables. Verify your schema's foreign key constraints before deleting.

Verify no other tables reference the records you're deleting.

If foreign key constraints prevent deletion, you'll receive an error. You may need to delete referencing records first.

For recoverable deletions, use UPDATE to set a deleted flag instead of DELETE.

**Hard Delete**: "Delete user with id 5"
**Soft Delete**: "Update user with id 5 to set deleted true"

For large-scale deletions, ensure recent backups exist.

Before deleting thousands of records, verify your database backup is current and tested.

### Common DELETE Scenarios

**Removing Single Records**
```
Delete the user with email spam@example.com
```

**Cleaning Old Data**
```
Remove all sessions that expired more than 30 days ago
```

**Removing Test Data**
```
Delete all records from test_users table
```

**Conditional Cleanup**
```
Remove all products with stock 0 and discontinued true
```

**Related Record Cleanup**
```
Delete all comments for post_id 123
```

## Understanding Confirmation Prompts

The confirmation dialog is your final checkpoint before data modification. Understanding what to look for helps ensure safe operations.

### What the Confirmation Dialog Shows

Clear indication of whether this is an INSERT, UPDATE, or DELETE.

The exact SQL that will execute, including:
- Target table name
- Columns being affected (for INSERT/UPDATE)
- WHERE conditions (for UPDATE/DELETE)
- Values being set (for INSERT/UPDATE)

Information about what will be affected:
- Which table
- How many records (when determinable)
- Which columns are changing

- **Confirm/Execute**: Proceeds with the operation
- **Cancel**: Aborts the operation

### Checklist Before Confirming

Before clicking the confirmation button, verify:

Ensure the SQL targets the intended table and schema.

For UPDATE and DELETE, verify the WHERE clause targets the correct records.

For INSERT and UPDATE, check that values are correct and properly formatted.

Consider how many rows should be affected and if that matches the WHERE clause.

For UPDATE and DELETE, ensure a WHERE clause exists unless you truly intend to affect all records.

If anything in the confirmation dialog looks unexpected, click Cancel and rephrase your request to the AI assistant.

## Verifying Modifications

After executing a modification, verify the changes were applied correctly.

### Immediate Verification

Look for the "Action Executed" confirmation in the chat.

![Action Executed Feedback](/images/111-chat-action-executed.png)

The success message typically shows how many rows were affected.

- INSERT: Should show 1 row (or number inserted)
- UPDATE: Shows number of rows modified
- DELETE: Shows number of rows removed

Run a SELECT query to verify the changes:

**After INSERT**: "Show me the latest user record"
**After UPDATE**: "Show user with id 5"
**After DELETE**: "Show all users" (verify record is gone)

### Verification Queries

**After Inserting a User**
```
Show me the user with email newemail@example.com
```

**After Updating Prices**
```
Show all products in Electronics category with their prices
```

**After Deleting Old Records**
```
Count how many log entries are older than 90 days
```

Expected result: 0 (if deletion was complete)

## Rollback Strategies

WhoDB does not provide built-in undo for data modifications. However, you can employ strategies to recover from mistakes.

### Prevention is Best

Practice modifications in a development database first

Run critical operations in transaction blocks (via Scratchpad)

Maintain frequent database backups for recovery

Use status flags instead of permanent deletion where possible

### If You Make a Mistake

Don't make additional changes that might complicate recovery.

Note exactly what operation was executed and approximately when.

Determine how many records were affected and which tables.

Identify the most recent database backup before the mistake.

For small mistakes (wrong value in one record), you might correct it with another UPDATE.

**Example**: If you accidentally set price to 100 instead of 10:
```
Update product_id 123 to set price 10
```

For significant data loss, restore the database from backup.

Restoring from backup will lose any changes made after the backup was created.

### Using Transactions for Safety

For critical modifications, use the Scratchpad to wrap operations in transactions:

``` Transaction Example
BEGIN;

UPDATE users SET status = 'inactive' WHERE last_login < '2023-01-01';

-- Review the affected rows
SELECT * FROM users WHERE status = 'inactive' AND last_login < '2023-01-01';

-- If everything looks correct:
COMMIT;

-- If something is wrong:
ROLLBACK;
```

The AI Chat Assistant executes operations immediately. For transaction control, use the Scratchpad query interface where you can manually manage BEGIN, COMMIT, and ROLLBACK.

## Best Practices for Production Data

When modifying production databases, follow these practices to minimize risk.

### Pre-Modification Checklist

Confirm you're connected to the correct database (production vs. development).

Run SELECT queries to understand the current data state.

Determine approximately how many records will be affected.

Ensure recent backups exist and have been tested.

Schedule modifications during low-traffic periods if possible.

Know how you'll recover if something goes wrong.

### During Modification

Always read the entire confirmation dialog before clicking confirm.

Take your time. Rushing leads to mistakes.

For bulk operations, test with a small subset first.

Instead of "Delete all inactive users", try "Delete user with id 5" first to verify the process.

After each modification, verify the result before proceeding.

Don't chain multiple modifications without checking intermediate results.

Keep notes of what modifications you're making and why.

Useful for troubleshooting if issues arise later.

### After Modification

Run SELECT queries to confirm modifications were applied correctly.

Verify that cascade effects or related data are in the expected state.

Watch for any application errors that might indicate data inconsistencies.

Record what was modified for future reference and audit purposes.

### Production Safety Rules

Always specify which records to affect in UPDATE and DELETE

Verify operations work correctly in non-production environments

Connect with read-only credentials when just exploring data

Perform large modifications during maintenance windows

## Common Modification Patterns

Learn effective patterns for common data modification scenarios.

### Pattern 1: Safe User Deletion

```
Show me user with email user@example.com
```
Review the user details to confirm it's the right record.

```
Show all orders for user with email user@example.com
```
Verify what related records exist.

```
Delete all orders for user with email user@example.com
```

```
Delete user with email user@example.com
```
Confirm when prompted.

```
Show me user with email user@example.com
```
Should return no results.

### Pattern 2: Bulk Status Update

```
Show all orders with status pending older than 30 days
```
Review the records that will be updated.

```
Count orders with status pending older than 30 days
```
Know how many records will be affected.

```
Update all orders with status pending older than 30 days to status cancelled
```

```
Show all orders with status cancelled from the last 30 days
```
Confirm the update was applied correctly.

### Pattern 3: Conditional Insert

```
Show user with email newuser@example.com
```
Verify the record doesn't already exist.

```
Add a user with email newuser@example.com, name New User, and role member
```

```
Show user with email newuser@example.com
```
Verify the new record was created.

### Pattern 4: Data Cleanup

```
Show all products with stock 0 and discontinued true
```

```
Count products with stock 0 and discontinued true
```

```
Delete all products with stock 0 and discontinued true
```

```
Count products with discontinued true
```
Should only show active discontinued products.

### Pattern 5: Price Adjustment

```
Show all products in Electronics category with their prices
```

Mentally verify the math for the update.

```
Increase all product prices in Electronics category by 10 percent
```

```
Show all products in Electronics category with their prices
```
Confirm prices were updated correctly.

## Troubleshooting

Common issues and solutions when modifying data with the AI assistant.

**Symptom**: You request a modification, but no confirmation dialog shows.

**Possible Causes**:
- The AI might have generated a read-only query instead
- The request might not have been recognized as a modification
- An error occurred before the confirmation stage

**Solutions**:
- Rephrase your request more explicitly: "Delete user with id 5"
- Check the AI response for error messages
- Try a simpler, more direct modification request
- Refresh the page and try again

**Symptom**: "Foreign key constraint violation" error when deleting or updating.

**Cause**: Other tables have records referencing the record you're trying to modify or delete.

**Solutions**:
- Delete or update referencing records first
- Check foreign key relationships: "Show all tables that reference users"
- Consider CASCADE rules in your schema
- If updating, ensure new values don't violate constraints

**Symptom**: AI warns "This will affect all records in the table".

**Cause**: Your request didn't specify which records to modify.

**Solutions**:
- Cancel the operation
- Rephrase with specific conditions: "Delete user with id 5" not "Delete user"
- Add WHERE conditions to limit scope
- If you truly want to affect all records, explicitly confirm

**Symptom**: The operation modified different records than intended.

**Cause**: The WHERE clause or conditions were ambiguous or incorrect.

**Solutions**:
- Check what was actually modified with a SELECT query
- If correctable, run a compensating UPDATE or INSERT
- If serious, consider restoring from backup
- In future, use SELECT first to verify target records

**Symptom**: "Permission denied" or "Insufficient privileges" error.

**Cause**: Your database user lacks INSERT, UPDATE, or DELETE permissions.

**Solutions**:
- Verify your database user has modification permissions
- Contact your database administrator for appropriate privileges
- Use a different database connection with proper permissions
- Check if the database is read-only

**Symptom**: "Data type mismatch" or "Invalid input syntax" error.

**Cause**: Values don't match the expected column data type.

**Solutions**:
- Check the table schema to understand column types
- Format dates as 'YYYY-MM-DD' for DATE columns
- Use numbers without quotes for numeric columns
- Use proper boolean values (true/false or 1/0)
- Rephrase your request with correct data type format

**Symptom**: Operation times out when modifying many records.

**Cause**: The modification affects too many rows for the timeout limit.

**Solutions**:
- Break the operation into smaller batches
- Use the Scratchpad for complex operations with custom timeouts
- Filter to smaller subsets: Update 1000 records at a time
- Consider database-level bulk operations for very large datasets
- Contact your administrator about timeout settings

## Comparing AI Chat vs. Traditional Methods

Understanding when to use AI Chat versus traditional data modification interfaces.

| Scenario | AI Chat Assistant | Traditional UI | Scratchpad SQL |
|----------|-------------------|----------------|----------------|
| **Quick single record change** | Fast and convenient | Multiple clicks required | Overkill for simple changes |
| **Bulk updates with conditions** | Natural language, easy | Must use SQL | Most control and visibility |
| **Complex multi-table operations** | May require multiple steps | Not supported | Best option |
| **Exploratory modifications** | Great for discovery | Requires knowing structure | Requires SQL knowledge |
| **Production-critical changes** | Good with careful review | Limited capabilities | Recommended for control |
| **Learning database structure** | Excellent | Good | Assumes SQL knowledge |

Use AI Chat for quick modifications and exploration. Use Scratchpad for complex operations requiring transaction control or multiple related statements.

## Security Considerations

Important security practices when modifying data through AI Chat.

### Data Exposure

Your modification requests and table/column names may be sent to external AI providers (OpenAI, Anthropic). However, actual data values are not sent.

**What's Sent to AI Providers**:
- Your natural language request
- Database schema (table and column names)
- Database type (PostgreSQL, MySQL, etc.)

**What's NOT Sent**:
- Actual data values
- Query results
- Existing record contents

**For Maximum Privacy**:
- Use Ollama (local AI models) for complete data isolation
- Avoid mentioning sensitive values in your requests
- Use Scratchpad for modifications involving sensitive data

### Audit and Compliance

All executed SQL is logged by WhoDB for audit purposes.

Modifications are associated with the database user credentials used.

Operation timestamps are recorded for compliance tracking.

Consider enabling database audit logs for comprehensive tracking.

### Permission Management

**Principle of Least Privilege**:
- Use database users with only necessary permissions
- Consider read-only users for data exploration
- Grant INSERT/UPDATE/DELETE only when required
- Use separate credentials for production vs. development

**Read-Only Exploration**:
``` Creating Read-Only User (PostgreSQL)
CREATE USER explorer WITH PASSWORD 'secure_password';
GRANT CONNECT ON DATABASE mydb TO explorer;
GRANT USAGE ON SCHEMA public TO explorer;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO explorer;
```

Connect with read-only credentials to prevent accidental modifications.

## Next Steps

Learn how to retrieve and analyze data without modifications

Master multi-turn conversations for complex operations

Use SQL directly for complex modifications requiring transactions

Learn comprehensive data safety strategies

## Summary

WhoDB's AI Chat Assistant makes data modification accessible through natural language while maintaining strict safety controls:

**Safe Modifications**: Every INSERT, UPDATE, and DELETE requires explicit confirmation before execution

**Clear Visibility**: See exactly what SQL will execute before confirming

**Immediate Feedback**: Action Executed messages confirm successful operations

**Comprehensive Verification**: Always verify modifications with follow-up queries

Remember that data modifications are permanent. Always review confirmation prompts carefully, verify the target records before confirming, and maintain regular backups for recovery scenarios. When in doubt, test in a development environment first or use the Scratchpad for transaction-controlled operations.

---


# ai/conversation-features.mdx
## Conversation Management & Advanced Features

Master multi-turn conversations, chat history navigation, scratchpad integration, and provider management in WhoDB's AI assistant

# Conversation Management & Advanced Features

WhoDB's AI Chat Assistant is designed for interactive, contextual conversations that build on previous exchanges. This guide covers advanced features that help you work more efficiently with the AI, including conversation history, chat navigation, scratchpad integration, and provider management.

The AI assistant maintains full context throughout your conversation, allowing natural follow-up questions without repeating information

## Understanding Conversational Context

Unlike traditional SQL editors where each query is independent, WhoDB's AI assistant maintains conversational context throughout your session. This enables a natural, iterative approach to data exploration and analysis.

### How Context Works

When you ask a question, the AI assistant considers:
1. Your current question
2. All previous messages in the conversation
3. Your database schema
4. Previous query results (implicitly)

This context awareness allows the assistant to:
- Understand pronouns and references ("show me that data", "what about the previous month?")
- Refine previous queries based on feedback
- Build complex analyses through multiple steps
- Maintain topic continuity across questions

![Multiple Messages Conversation](/images/112-chat-multiple-messages.png)

### Context Window Limitations

Each AI provider has limits on how much conversation history can be maintained:

| Provider | Context Window | Approximate Messages |
|----------|---------------|---------------------|
| OpenAI GPT-4 Turbo | 128K tokens | 150-200 messages |
| OpenAI GPT-3.5 Turbo | 16K tokens | 20-30 messages |
| Anthropic Claude 3.5 Sonnet | 200K tokens | 250-300 messages |
| Anthropic Claude 3 Haiku | 200K tokens | 250-300 messages |
| Ollama (varies by model) | 2K-128K tokens | 10-150 messages |

One token is approximately 4 characters. Longer schemas and verbose queries consume more tokens

When the context window approaches its limit, consider starting a new conversation to maintain optimal performance.

## Multi-Turn Conversations

Multi-turn conversations allow you to build complex analyses through natural dialogue, refining and iterating on results.

### Basic Follow-Up Questions

After receiving initial results, ask follow-up questions that reference previous context:

**Initial Query**:
```
Show me all users
```

**Follow-Up Questions**:
```
How many are there?
```
```
What about users created this week?
```
```
Can you group them by email domain?
```

The assistant understands that "them" refers to the users from your initial query.

### Refining Queries

Use follow-up questions to refine and improve results:

**You**: "Show me recent orders"

**Assistant**: [Returns orders from the last 30 days]

**You**: "Actually, just from the last week"

**Assistant**: [Adjusts the query to show 7 days instead]

**You**: "Sort by total amount descending"

**Assistant**: [Adds ORDER BY clause to the refined query]

### Building Complex Analysis

Break down complex questions into multiple steps:

**You**: "What tables contain customer information?"

**Assistant**: Lists relevant tables with descriptions

**You**: "Show me the structure of the customers table"

**Assistant**: Displays columns, types, and relationships

**You**: "Get all customers from California"

**Assistant**: Generates SELECT query with WHERE clause

**You**: "What's the average order value for these customers?"

**Assistant**: Generates aggregation query building on previous context

**You**: "Show me monthly trends"

**Assistant**: Creates time-series query with grouping

### Conversation Patterns

Discover database contents through iterative questions:

1. "What tables exist in this database?"
2. "Tell me about the products table"
3. "Show me a sample of products"
4. "How many products are out of stock?"
5. "Which categories have the most out-of-stock items?"

Start general and narrow down results:

1. "Show me all sales data"
2. "Just from Q4 2024"
3. "Only sales over $10,000"
4. "Group by sales representative"
5. "Show only the top 10 performers"

Compare different data sets:

1. "Show me revenue for January"
2. "Now show me February"
3. "What's the percentage difference?"
4. "Which products drove the increase?"
5. "Compare to the same period last year"

Drill down into specific issues:

1. "Are there any duplicate email addresses?"
2. "Show me the duplicates"
3. "Which accounts were created most recently?"
4. "What other data do these accounts have in common?"
5. "Generate a query to merge the duplicates"

## Chat History Navigation

WhoDB provides keyboard shortcuts to quickly access your previous questions, making it easy to rerun or modify earlier queries.

### Keyboard Shortcuts

| Shortcut | Action | Description |
|----------|--------|-------------|
| ↑ (Arrow Up) | Previous message | Navigate backward through your message history |
| ↓ (Arrow Down) | Next message | Navigate forward through your message history |
| Enter | Send message | Submit your current message to the AI |

### Using Arrow Keys

Click in the chat input field at the bottom of the screen.

Press ↑ to load your most recent message.

The input field will populate with your last sent message.

Continue pressing ↑ to move backward through your message history.

Press ↓ to move forward.

Edit the message if needed, then press Enter to send.

Or press Enter without changes to rerun the exact same query.

### Use Cases for History Navigation

**Rerunning Queries**:
- Check for data changes after modifications
- Refresh results periodically during active development
- Verify that updates or deletes completed successfully

**Modifying Previous Queries**:
- Change date ranges (e.g., "last week" → "last month")
- Adjust filters (e.g., "status = active" → "status = pending")
- Refine search terms

**Learning from History**:
- Review what questions produced useful results
- Build on successful query patterns
- Recall exact phrasing that worked well

Use ↑ to quickly access and modify your last query instead of retyping similar questions

## Starting New Conversations

When you need to start fresh or when your current conversation becomes too long, use the New Chat feature to clear context and begin a new session.

### When to Start a New Chat

Consider starting a new conversation when:

You're switching to a completely different topic or database area

Your conversation has 30+ messages and performance is slowing

The AI seems confused by mixed context from different topics

You want to approach a problem with a clean slate

### Creating a New Chat

Find the **New Chat** button at the top of the Chat interface, next to the AI provider dropdowns.

![New Chat Button](/images/114-chat-new-chat-button.png)

Click the button to clear all messages from the current conversation.

This action cannot be undone. All conversation history will be cleared

The chat interface will reset to the initial state with example prompts.

Your provider and model selections remain unchanged.

### Preserving Important Queries

Before starting a new chat, consider saving important queries to Scratchpad:

1. Hover over any query result
2. Click the **Scratchpad icon** (command line symbol)
3. Choose or create a page for the query
4. Start your new chat

Moving queries to Scratchpad lets you preserve useful queries before clearing conversation history

## Moving Queries to Scratchpad

The Scratchpad integration allows you to save generated SQL queries for later use, modification, or documentation. This bridges the gap between conversational exploration and traditional SQL editing.

### What is Scratchpad Integration?

Scratchpad is WhoDB's SQL editor with support for:
- Multiple query pages
- Saved query history
- Direct query execution
- Query sharing and documentation

When you move a chat query to Scratchpad, the generated SQL becomes available for:
- Manual refinement and optimization
- Saving for future use
- Sharing with team members
- Building complex multi-query workflows

![Move to Scratchpad Dialog](/images/113-chat-move-to-scratchpad-dialog.png)

### Moving a Query to Scratchpad

Ask the AI assistant a question that generates SQL. Wait for the query to execute and display results.

Only SQL queries can be moved to Scratchpad. Text responses and errors cannot be transferred

Move your mouse over the query results table or chart.

A toolbar will appear with action buttons.

Click the **command line icon** (⌘) in the toolbar.

This opens the "Move to Scratchpad" dialog.

Select where to add the query:

**Option A: Existing Page**
- Select a page from the dropdown
- Query will be added as a new cell at the bottom

**Option B: New Page**
- Select "Create new page" from the dropdown
- Enter a descriptive name (e.g., "User Analysis Queries")
- A new page will be created with your query

Click **Move to Scratchpad** to complete the transfer.

WhoDB will:
- Save the query to the selected page
- Navigate you to the Scratchpad
- Highlight the newly added query

### Database Support

Scratchpad integration is available for SQL databases:

| Database Type | Scratchpad Support | Notes |
|--------------|-------------------|-------|
| PostgreSQL | ✅ Full support | All features available |
| MySQL | ✅ Full support | All features available |
| SQLite | ✅ Full support | All features available |
| MariaDB | ✅ Full support | All features available |
| Redis | ❌ Not supported | Key-value store, no SQL |
| MongoDB | ❌ Not supported | Uses query language, not SQL |
| Cassandra | ❌ Not supported | Uses CQL, not standard SQL |

For NoSQL databases, copy the generated query manually if needed for documentation

### Organizing Scratchpad Queries

Develop a structure for organizing saved queries:

**By Functionality**:
- User Queries
- Order Analysis
- Reporting Queries
- Data Cleanup

**By Frequency**:
- Daily Reports
- Weekly Analytics
- Monthly Summaries
- Ad-Hoc Queries

**By Complexity**:
- Simple Lookups
- Complex Joins
- Aggregation Queries
- Multi-Step Operations

Create descriptive page names that make it easy to find queries later

### Best Practices for Scratchpad

1. **Save Before Modifying**: Move the AI-generated query to Scratchpad before making manual changes
2. **Add Comments**: Document why the query was saved and what it accomplishes
3. **Test Modifications**: When you modify a saved query, test it thoroughly
4. **Archive Old Queries**: Create an "Archive" page for queries you no longer use but want to keep
5. **Share with Team**: Use descriptive names so teammates can understand saved queries

## Managing Conversation History

As you work with the AI assistant, managing your conversation history becomes important for maintaining clarity and performance.

### Conversation Lifecycle

Start with exploratory questions to understand your data.

Context is minimal, responses are quick.

Build on previous questions, refine queries, and drill into details.

Context accumulates, enabling sophisticated follow-ups.

After 20-30 messages, the conversation has substantial context.

Performance may slow slightly as more history is processed.

Approaching provider limits (varies by model).

Consider starting a new chat or being more concise.

### Signs You Should Start a New Chat

Watch for these indicators:
- Responses become slower (>10 seconds for simple queries)
- The AI references incorrect previous context
- You're switching to a completely different task
- Error messages about context length
- You've had 50+ messages in one conversation

### Strategies for Long Sessions

If you need extended conversations:

**Use Claude for Long Context**:
- Anthropic's Claude models have 200K token context windows
- Can maintain 200+ message conversations
- Best for extended analysis sessions

**Be Concise**:
- Avoid unnecessarily verbose questions
- Use shorthand in follow-ups ("show top 10" vs. "can you please show me the top 10 results")
- Reference previous results by position ("those top 3" instead of repeating criteria)

**Periodically Summarize**:
```
Summarize what we've learned about the users table so far
```
This helps the AI consolidate context and identify what's important.

**Save to Scratchpad**:
Move important queries to Scratchpad, then start a new chat with a fresh context.

## Provider Management

Managing your AI providers allows you to switch between different AI services, update credentials, and remove unused providers.

### Viewing Current Provider

Your active provider and model are displayed in two dropdowns at the top of the Chat interface:
- **Left dropdown**: AI Provider (OpenAI, Anthropic, Ollama, etc.)
- **Right dropdown**: Specific model (GPT-4, Claude 3.5 Sonnet, etc.)

### Switching Providers

Click the AI Provider dropdown on the left.

All configured providers appear in the list.

Click on another provider to switch immediately.

The model dropdown will update with available models for that provider.

Choose the desired model from the updated model dropdown.

You can now chat using the new provider and model.

Switching providers does NOT clear your conversation history. The new provider will have access to previous messages

### Adding New Providers

Click the AI Provider dropdown.

At the bottom of the dropdown, click the green **"Add a provider"** option with the plus icon.

Select the provider type and enter the required credentials (API key).

See [Setting Up AI Providers](/ai/setup-providers) for detailed instructions.

Click Submit to save the provider.

The new provider will appear in your provider list.

### Deleting Providers

When you no longer need a provider (e.g., expired API key, switching to different service), you can remove it:

![Delete Provider Button](/images/115-chat-delete-provider-button.png)

Use the provider dropdown to select the provider you want to remove.

This ensures you're deleting the correct provider.

Click the **Delete Provider** button next to the New Chat button.

A confirmation dialog will appear.

Click **Delete** in the confirmation dialog.

This action cannot be undone. You'll need to re-enter credentials to use this provider again

The provider is removed from your configuration.

If this was your only provider, you'll need to add a new one to continue using the AI assistant.

### Managing Multiple Providers

You can configure multiple providers simultaneously, useful for:

**Cost Optimization**:
- Use GPT-3.5 Turbo for simple queries (cheaper)
- Switch to GPT-4 for complex analysis (more accurate)

**Feature Comparison**:
- Compare how different models handle the same question
- Identify which provider works best for your schema

**Redundancy**:
- Have backup providers if primary service is down
- Avoid being blocked by rate limits on one provider

**Privacy Flexibility**:
- Use Ollama for sensitive queries (local processing)
- Use cloud providers for faster responses on non-sensitive data

Keep at least two providers configured for reliability and flexibility

### Provider-Specific Settings

Each provider has different characteristics to consider:

**Credentials**: API key (starts with `sk-proj-`)
**Best Models**: GPT-4 Turbo, GPT-3.5 Turbo
**Notes**: Fast, accurate, pay-per-use

**Credentials**: API key (starts with `sk-ant-api`)
**Best Models**: Claude 3.5 Sonnet
**Notes**: Large context window, excellent reasoning

**Credentials**: None (local installation)
**Best Models**: Llama 3.1, Mistral, CodeLlama
**Notes**: Privacy-focused, free, requires local setup

**Credentials**: Varies by implementation
**Best Models**: Depends on your deployment
**Notes**: Full control, enterprise features

## Advanced Conversation Patterns

Master these advanced patterns to get more from your conversations with the AI assistant.

### Iterative Refinement

Build complex queries through gradual refinement:

```
Show me user signups
```

```
Just from the last 30 days
```

```
Group them by day
```

```
Compare to the previous 30 days
```

```
What's the percentage change?
```

### Context Anchoring

Reference specific previous results:

```
Using the results from two questions ago, show me...
```

```
Take that query and add a filter for...
```

```
Apply the same logic to the orders table instead
```

### Explaining AI Decisions

Ask the AI to explain its query choices:

```
Why did you use a LEFT JOIN instead of INNER JOIN?
```

```
Explain how this aggregation query works
```

```
Is there a more efficient way to write this?
```

This helps you learn SQL while using the assistant.

### Multi-Step Workflows

Combine conversation with Scratchpad for complex workflows:

1. **Explore with AI**: Ask questions to understand data structure
2. **Generate Base Query**: Get AI to create initial query
3. **Move to Scratchpad**: Save the query for refinement
4. **Refine Manually**: Add complexity or optimization
5. **Return to AI**: Ask follow-up questions about results
6. **Iterate**: Move refined queries back to Scratchpad

This hybrid approach combines AI speed with manual control.

### Conversation Templates

Common conversation templates for recurring tasks:

**Data Quality Check**:
```
1. Show me row counts for all tables
2. Are there any null values in critical columns?
3. Find duplicate records in [table]
4. Identify records with invalid data formats
5. Generate a summary of data quality issues
```

**Performance Analysis**:
```
1. What are the largest tables in the database?
2. Show me tables without indexes
3. Find the slowest queries (if query logs available)
4. Identify tables with many relationships
5. Suggest optimization opportunities
```

**Business Intelligence**:
```
1. What's our total revenue this month?
2. How does that compare to last month?
3. Which products contributed most to revenue?
4. Show me revenue by customer segment
5. Identify trends over the last 6 months
```

## Troubleshooting Conversation Issues

Common issues and solutions when working with conversations:

**Symptoms**: Incorrect query generated, wrong table used, unexpected results

**Solutions**:
- Be more specific with table and column names
- Provide examples in your question
- Ask the AI to confirm understanding before generating query
- Break complex questions into smaller parts
- Use full names instead of pronouns in early messages

**Example Fix**:
- ❌ "Show me the data" (too vague)
- ✅ "Show me all rows from the users table"

**Symptoms**: Queries take >10 seconds, loading messages appear for extended periods

**Solutions**:
- Start a new chat to clear context
- Switch to a faster model (GPT-3.5 Turbo, Claude Haiku)
- Reduce complexity of questions
- Check your internet connection (for cloud providers)
- For Ollama, ensure sufficient local resources

**For Long Conversations**:
- Context size affects performance
- Save important queries to Scratchpad and start fresh
- Use providers with larger context windows (Claude)

**Symptoms**: Follow-up questions use incorrect table or data from earlier in conversation

**Solutions**:
- Be explicit in your question (mention table name)
- Start a new chat if context is too mixed
- Ask the AI to confirm what it's referencing
- Use more recent context by being specific

**Example Fix**:
- ❌ "Now show me those records" (ambiguous)
- ✅ "Show me users from the last query who have orders"

**Symptoms**: Scratchpad button is missing or disabled

**Reasons**:
- Current database doesn't support SQL (Redis, MongoDB)
- Message is a text response, not a SQL query
- Message is an error response

**Solutions**:
- Only SQL query results can be moved to Scratchpad
- For NoSQL databases, manually copy query text
- Ensure query executed successfully before moving

**Symptoms**: "Provider not available" errors, need to reconfigure frequently

**Solutions**:
- Verify API key is still valid
- Check billing status on provider's website
- Ensure API key has correct permissions
- For Ollama, verify service is running
- Check for rate limiting from provider

**For Ollama Specifically**:
```
# Verify Ollama is running
ollama list

# Restart Ollama service if needed
```

**Symptoms**: Arrow keys don't populate previous messages

**Solutions**:
- Ensure input field is focused (click in input box)
- Verify messages were actually sent (look for user messages in chat)
- Check that chat has history (arrow keys only work with existing messages)
- Try clicking in input field before pressing arrow keys

**Note**: Arrow keys only navigate through your sent messages, not AI responses

## Best Practices Summary

Mention table names and columns explicitly in your questions

Start simple and refine through follow-up questions

Move valuable queries to Scratchpad before clearing chat

Use New Chat when switching topics or after long conversations

Press ↑ to quickly access and modify previous questions

Create well-named pages to organize saved queries

Configure backup providers for reliability

Watch for performance degradation in long conversations

## Next Steps

Now that you understand conversation management and advanced features:

Learn techniques for effective data retrieval with natural language

Safely update, insert, and delete data with AI assistance

Master the Scratchpad for advanced SQL editing and query management

Discover tips for optimal AI assistant usage

Mastering conversation features transforms the AI assistant from a query generator into a powerful collaborative partner for database work

---


# data/viewing-data.mdx
## Viewing Data

Learn how to view and interact with your table data in WhoDB

# Viewing Data

WhoDB provides an intuitive data grid interface that makes it easy to view, browse, and interact with your table data. This guide covers how to navigate the data view, understand the interface, and work with different data types.

## Accessing the Data View

To view table data:

1. Navigate to your desired table in the sidebar
2. Click on the table name to open the data view
3. The data will load automatically and display in the grid

![Data View Users Table](/images/09-data-view-users-table.png)

The data view loads the first page of results by default. You can adjust the page size and navigate through pages as needed.

## Understanding the Data Grid

The data grid displays your table data in a familiar spreadsheet-like format with several key components:

![Data View Table Content](/images/10-data-view-table-content.png)

### Column Headers

Each column header displays the column name and provides several interactive features:

- **Column Name**: Shows the field name from your database
- **Data Type Indicator**: Visual indicator of the column's data type
- **Sort Controls**: Click to sort ascending or descending
- **Column Width**: Drag edges to resize columns to your preference

Column headers are sticky and remain visible when you scroll vertically through your data, making it easier to reference column names while reviewing rows.

### Row Display

Each row in the grid represents a single record from your table:

- **Row Number**: Sequential identifier on the left side
- **Data Cells**: Display the actual values for each column
- **Null Indicator**: Empty cells represent NULL values
- **Row Highlighting**: Hover over a row to see it highlighted for easier readability

### Data Type Rendering

WhoDB intelligently displays different data types:

**Text & Strings**
![Table Long Text Content](/images/80-table-long-text-content.png)

Text fields display their full content with word wrapping for long values.

**Dates & Timestamps**
![Table Dates Timestamps](/images/81-table-dates-timestamps.png)

Dates and timestamps are formatted consistently and are sortable chronologically.

**Numeric Types**
![Table Numeric Types](/images/82-table-numeric-types.png)

Numbers are right-aligned for easier comparison and calculation visualization.

**Null Values**
![Table with Null Values](/images/79-table-with-null-values.png)

NULL values are displayed as empty cells with subtle styling to distinguish them from empty strings.

## Interacting with the Grid

### Scrolling & Navigation

- **Horizontal Scrolling**: Scroll left and right to view all columns (or use arrow keys)
- **Vertical Scrolling**: Scroll up and down to view more rows
- **Keyboard Navigation**: Use arrow keys to move between cells

The grid maintains a smooth scrolling experience even with large datasets and many columns.

### Column Resizing

Adjust column widths to better view your data:

1. Position your cursor on the border between two column headers
2. When the cursor changes to a resize cursor, drag left or right
3. The column width will adjust and be remembered for the current session

### Row Selection

Select rows for batch operations:

1. Click the checkbox at the start of a row to select it
2. Click the header checkbox to select all rows on the current page
3. Selected rows are highlighted with a subtle background color

Batch operations typically affect only the selected rows on the current page. Check your selection before performing bulk operations.

## Viewing Empty Tables

When a table contains no records:

![Table Empty State No Results](/images/76-table-empty-state-no-results.png)

The grid displays an empty state message explaining that no data is available. You can:
- Add new records using the "Add Row" button
- Generate mock data for testing
- Check your filters if you were expecting to see data

## Viewing Large Tables

When working with tables containing many columns:

![Table Many Columns Wide](/images/78-table-many-columns-wide.png)

WhoDB automatically handles wide tables by:
- Allowing horizontal scrolling to view all columns
- Keeping the first column (or key columns) visible while scrolling
- Maintaining responsive performance even with many columns

## Data Refresh

The data view can be refreshed to show the latest changes:

- **Automatic**: Some operations automatically refresh the view
- **Manual**: Look for a refresh button in the action bar to reload data
- **Real-time**: The view updates when you add, edit, or delete records through WhoDB

If other applications modify the database, you may need to manually refresh the view to see those changes in WhoDB.

## Best Practices

When first exploring a large table, start with the default page size to get a quick overview of the data structure and content.

Sort by key columns to group similar data together and spot patterns or outliers quickly.

Pay attention to how different data types are displayed to ensure data integrity and identify any formatting issues.

Use the page navigation to jump to specific sections of large tables rather than scrolling through every page.

Understand which columns allow NULL values and how that might affect your application or analysis.

## Keyboard Shortcuts

Make data viewing more efficient with these keyboard shortcuts:

| Shortcut | Action |
|----------|--------|
| `Arrow Keys` | Navigate between cells |
| `Ctrl/Cmd + Home` | Jump to the first row |
| `Ctrl/Cmd + End` | Jump to the last row |
| `Ctrl/Cmd + Left/Right` | Navigate between columns |
| `Page Up/Down` | Navigate between pages |

## Limitations & Considerations

- **Large Result Sets**: Very large tables may take longer to load initially
- **Memory Usage**: Extremely wide tables with thousands of columns may impact performance
- **Real-time Updates**: Changes made outside WhoDB require manual refresh to display

For very large tables (100,000+ rows), consider using filters or pagination to work with manageable subsets of data rather than loading everything at once.

## Next Steps

Once you're comfortable viewing data, explore these related features:

Learn how to organize your data with sorting and navigate through pages

Discover how to search for specific records and apply filters

Learn how to insert new records into your tables

Find out how to modify existing data

The data grid is just one way to interact with your data. For more complex queries and analysis, check out the Scratchpad interface in the Query section.

---


# data/adding-records.mdx
## Adding Records

Learn how to insert new records into your tables using the add row dialog

# Adding Records

WhoDB makes it easy to add new records to your tables through an intuitive dialog interface. This guide covers how to insert new data, handle different field types, and work with constraints.

## Opening the Add Row Dialog

To add a new record:

1. Navigate to the table where you want to add data
2. Click the "Add Row" button in the top action bar
3. The add row dialog will open with form fields for each column

![Data View Add Row Dialog](/images/11-data-view-add-row-dialog.png)

The add row dialog automatically shows all columns from your table, making it easy to fill in complete records.

## Understanding the Form

The add row dialog displays a form with fields organized by column:

![Data View Add Row Filled](/images/12-data-view-add-row-filled.png)

### Form Field Organization

- **Column Name**: Clear label for each field
- **Data Type**: Indicator showing what type of data is expected
- **Input Field**: Appropriately typed input control based on column data type
- **Required Fields**: Marked to indicate columns that cannot be NULL
- **Optional Fields**: Can be left empty if the column allows NULL values

### Supported Field Types

WhoDB provides specialized input controls for different data types:

**Text & String Fields**
- Regular text input for VARCHAR, TEXT, and similar types
- Multi-line support for longer text content
- Character limits based on column constraints

**Numeric Fields**
- Number input with appropriate controls
- Support for integers, decimals, and floating-point numbers
- Validation to prevent invalid numeric entries

**Date & Time Fields**
- Date picker for DATE type columns
- DateTime picker for TIMESTAMP type columns
- Time input for TIME type columns

**Boolean Fields**
- Toggle or checkbox for BOOLEAN type columns
- Easy true/false selection

**Special Types**
- UUID fields with generation options
- JSON fields with syntax highlighting
- Array fields with item management

## Filling Out the Form

### Step-by-Step Process

Identify which columns are required (marked with an asterisk) and which are optional.

Enter values for all required fields. The dialog will prevent submission if any required field is empty.

Add values for optional columns if needed. Leave them empty if NULL values are acceptable.

Double-check all values before submission for accuracy and correctness.

Click the "Save" or "Add" button to insert the new record into your table.

### Input Validation

WhoDB validates your input as you fill out the form:

- **Type Checking**: Ensures values match the expected data type
- **Length Constraints**: Respects maximum length limits for text fields
- **Format Validation**: Validates dates, emails, and other formatted fields
- **Constraint Verification**: Checks unique constraints and other database constraints

If validation errors occur, review the highlighted fields and correct the invalid entries before attempting to submit again.

## Working with Different Data Types

### Text Insertion

For text and string columns:
- Type content directly into the field
- Multi-line text will wrap automatically
- Be aware of character limits specified by your column

### Numeric Insertion

For numeric columns:
- Enter numbers without formatting (no commas or currency symbols)
- Decimal values use a period as separator
- The system validates range constraints automatically

### Date/Time Insertion

For temporal columns:
- Use the date picker for consistent date formatting
- Select time separately from date if applicable
- Format is automatically handled by WhoDB

### Boolean Insertion

For boolean columns:
- Toggle the checkbox or switch control
- Values are stored as true or false
- Clear indication of the current state

### Foreign Keys

When your table has foreign key relationships:
- A dropdown shows available values from the related table
- Select the appropriate related record
- The foreign key ID is automatically set

If a foreign key reference doesn't exist yet, you may need to add it to the related table first.

## Handling NULL Values

### Allowing NULL

If a column allows NULL values:
- Leave the field empty or unchecked
- NULL will be stored in the database
- No error will occur

### Required Columns

If a column doesn't allow NULL values:
- You must provide a value
- The form will prevent submission without it
- The field is marked with an asterisk indicator

### Default Values

Some columns may have default values:
- The form may pre-populate with the default
- You can override with your own value
- Check your database schema to see default values

Auto-increment columns (like primary keys) are typically not shown in the add form, as the database generates these values automatically.

## Constraints & Validation

### Unique Constraints

If a column has a unique constraint:
- WhoDB may check for duplicate values
- An error message will appear if you try to insert a duplicate
- Modify your entry to provide a unique value

### Check Constraints

Tables with check constraints:
- Values must meet the constraint criteria
- Validation will prevent invalid entries
- Check your schema to understand the constraints

### Foreign Key Constraints

When inserting records with foreign keys:
- The referenced record must exist in the related table
- Foreign key constraints are validated before insertion
- An error message appears if the reference is invalid

## Submission & Feedback

### Successful Submission

When you submit a valid form:
1. A confirmation message appears
2. The dialog closes
3. The new record is added to the table
4. The data grid refreshes to show the new row

After successfully adding a record, you can immediately add another by clicking "Add Row" again, or the dialog can remain open for batch insertion.

### Error Handling

If submission fails:
- An error message explains what went wrong
- The form remains open for correction
- Identify the invalid field and fix it
- Try submitting again

### Common Issues

**"Field required" error**
- Ensure all marked required fields have values
- Check for any fields you may have missed

**"Invalid format" error**
- Verify the data format matches the column type
- For dates, use the provided date picker

**"Constraint violation" error**
- Check unique constraints for duplicate values
- Verify foreign key references exist
- Review any check constraints in your schema

## Keyboard Shortcuts

Make form entry faster with these shortcuts:

| Shortcut | Action |
|----------|--------|
| `Tab` | Move to next field |
| `Shift + Tab` | Move to previous field |
| `Enter` | Submit the form (from last field) |
| `Escape` | Close the dialog |

## Best Practices

Review your data for accuracy before inserting, especially for critical fields like IDs or references.

Understand your table's constraints before adding data to avoid repeated errors.

Let the database use default values when sensible to ensure consistency.

Ensure referenced records exist before creating relationships through foreign keys.

If adding multiple similar records, consider keeping the form open between additions for faster entry.

## Limitations

- **Auto-increment Fields**: Not editable in the add form; values are generated automatically
- **Computed Fields**: Cannot be set manually; values are generated by the database
- **Large BLOBs**: May not be suitable for direct entry through the form
- **One Record at a Time**: The dialog adds one record per submission (use Mock Data for bulk insertion)

## Troubleshooting

**Dialog Won't Submit**
- Check for required fields left empty
- Look for validation errors on specific fields
- Review the error message for specific guidance

**Data Not Appearing After Submit**
- Check if you're viewing the correct table
- Verify the page you're on (new records appear on the first page)
- Try refreshing the data view

**Can't Insert Due to Constraint**
- Review the constraint error message
- Verify you're not creating duplicates
- Ensure foreign key references exist

## Next Steps

Once you've added records, explore these related features:

Learn how to modify existing records you've added

Discover how to remove records when needed

Generate test data in bulk for your tables

Review and manage all your table data

For bulk data insertion, consider using Mock Data generation in the Advanced Features section, which can quickly populate tables with test data.

---


# data/editing-records.mdx
## Editing Records

Learn how to modify existing records in your tables using WhoDB's edit interface

# Editing Records

WhoDB provides straightforward methods to edit and update existing records in your tables. This guide covers how to open the edit dialog, modify data, and handle different types of changes.

## Opening the Edit Dialog

There are several ways to edit a record:

### Method 1: Context Menu

Right-click on any row to open the context menu:

![Data View Context Menu](/images/13-data-view-context-menu.png)

1. Right-click on the row you want to edit
2. Select "Edit Row" from the context menu
3. The edit dialog opens with the current data

### Method 2: Double-Click

Some interfaces allow direct editing:
1. Double-click on a row to open the edit dialog
2. The dialog appears with the row's current data

### Method 3: Action Bar

If a row is selected:
1. Look for an "Edit" button in the action bar
2. Click to open the edit dialog for the selected row

![Data View Edit Row Dialog](/images/14-data-view-edit-row-dialog.png)

The context menu method is the most reliable way to edit a specific row.

## Understanding the Edit Dialog

The edit dialog displays the same form as the add dialog, but pre-populated with existing data:

### Dialog Structure

- **Column Name**: Clearly labeled for each field
- **Current Value**: Shows the existing data
- **Input Field**: Editable field matching the column's data type
- **Read-Only Fields**: Primary keys and computed fields may be disabled
- **Save/Cancel Buttons**: Action buttons at the bottom

### Identifying Changes

- **Modified Fields**: May be highlighted or marked to show changes
- **Original Values**: Sometimes preserved for reference
- **Unsaved Changes**: Indicated by visual feedback

## Modifying Data

### Editing Different Data Types

**Text & String Fields**
- Click in the field and modify the text
- Clear and retype for complete replacement
- Use backspace and delete for corrections

**Numeric Fields**
- Modify the number value
- Ensure the new value meets any numeric constraints
- The system validates type compatibility

**Date & Time Fields**
- Use the date/time picker to select a new value
- Or type the date in the expected format
- Timezone information may be relevant for timestamps

**Boolean Fields**
- Toggle the checkbox or switch
- True/false state clearly indicated

**Foreign Keys**
- Use the dropdown to select a different related record
- Must reference an existing record in the related table

### Step-by-Step Editing

Find and right-click on the row you want to edit to open the context menu.

Choose "Edit Row" from the context menu to open the edit dialog.

The dialog shows all current data for the record.

Click on the fields you want to change and enter new values.

Only modify the columns you actually want to update.

Review all modifications before saving.

Click the "Save" or "Update" button to apply the changes.

## Validation During Editing

### Real-Time Validation

As you modify fields:
- **Type Validation**: Ensures values match the expected data type
- **Length Checking**: Respects maximum character limits
- **Format Validation**: Validates dates, emails, and formatted fields

### Constraint Checking

Before saving, WhoDB checks:
- **Unique Constraints**: No duplicate values for unique columns
- **Foreign Keys**: References still exist in related tables
- **Check Constraints**: Values meet any check constraints
- **NOT NULL**: Required fields have values

If your changes violate constraints, an error message will appear explaining the issue. Modify your entry and try again.

## Special Field Types

### Primary Keys

- **Read-Only**: Cannot be modified after creation
- **Grayed Out**: Field appears disabled in the edit dialog
- **Identity/Auto-Increment**: These fields are set by the database

Primary keys identify unique records. Changing them could break relationships and data integrity, so they cannot be edited.

### Computed/Generated Columns

- **Read-Only**: Cannot be edited manually
- **Auto-Generated**: Values are calculated by the database
- **Disabled State**: Field appears grayed out

### Timestamps (Updated At)

Some tables have automatic timestamp columns:
- **Auto-Updated**: Modified automatically when the record changes
- **Read-Only**: Cannot be manually set
- **Updated Automatically**: Will reflect the current time after saving

### Foreign Key Fields

When editing foreign key columns:
- Dropdown shows all available related records
- Select the appropriate new reference
- The system validates the reference exists

## Handling NULL Values

### Setting Values to NULL

If a column allows NULL:
1. Clear the field completely
2. Some interfaces show an explicit "NULL" checkbox
3. The value becomes NULL when saved

### Clearing Non-Required Fields

For optional columns:
- Clear the field to set it to NULL
- Only when NULL values are allowed
- Required fields cannot be cleared

### Viewing NULL Values

Cells with NULL values appear empty in the grid:
- Clearly distinguished from empty strings
- You can edit to set a value or leave as NULL
- Proper NULL handling is important for data integrity

## Saving Changes

### Successful Save

When you click Save and the changes are valid:
1. Confirmation message appears
2. The dialog closes automatically
3. The data grid refreshes with updated values
4. The row shows the new data

After saving, the new data immediately appears in the table, confirming your changes were successful.

### Handling Save Errors

If there's an issue saving:

**Constraint Violation**
- A constraint prevented the save
- Review the error message
- Fix the violating value
- Try saving again

**Database Error**
- An unexpected database error occurred
- Check your database connection
- Review the error details
- Try again, or contact support if it persists

**Permission Error**
- You may lack permission to edit this record
- Check your database user permissions
- Contact your database administrator

### Canceling Edits

- Click "Cancel" or close the dialog
- All changes are discarded
- The original values remain unchanged in the database

## Batch Editing

For editing multiple similar records:

1. Edit one record and save
2. Open another record for editing
3. Modify the new record
4. Continue for each record needing changes

For bulk updates of similar data, consider using the Scratchpad to write an UPDATE query that changes multiple records at once.

## Keyboard Shortcuts

Speed up editing with these shortcuts:

| Shortcut | Action |
|----------|--------|
| `Tab` | Move to next field |
| `Shift + Tab` | Move to previous field |
| `Ctrl/Cmd + S` | Save changes |
| `Escape` | Cancel editing |
| `Enter` | Save (from last field) |

## Best Practices

Always verify changes before clicking save to prevent unintended modifications.

Know which fields are unique or required to avoid constraint violations.

Ensure new values are consistent with your data model and business logic.

When updating foreign key references, verify the new reference exists.

Think about how changes might affect dependent data or application behavior.

## Limitations

- **Primary Keys**: Cannot be edited after creation
- **Computed Columns**: Cannot be manually edited
- **System Timestamps**: May be auto-updated by the database
- **Large BLOBs**: May not be efficiently editable through the dialog
- **One Record at a Time**: Edit dialog works with one record per session

## Troubleshooting

**Can't Save Changes**
- Check for validation errors on specific fields
- Ensure required fields have values
- Look for constraint violations in the error message
- Try canceling and editing again

**Changes Don't Appear**
- Check if you're viewing the correct table and page
- Verify the save completed successfully
- Try refreshing the data view

**Field Is Grayed Out/Disabled**
- This is a read-only field (typically a primary key)
- You cannot edit primary keys or computed columns
- This is intentional for data integrity

**Foreign Key Error**
- The referenced record may no longer exist
- Select a valid reference from the dropdown
- You may need to add the referenced record first

## Next Steps

Continue managing your data with these related features:

Learn how to remove records you no longer need

Insert new records into your tables

Navigate and view all your table data

Use SQL for bulk updates and complex modifications

For complex edits affecting many records, the Scratchpad query interface may be more efficient than editing individual rows through the dialog.

---


# data/deleting-records.mdx
## Deleting Records

Learn how to remove records from your tables safely with WhoDB

# Deleting Records

WhoDB provides secure methods for deleting records from your tables with safeguards to prevent accidental data loss. This guide covers different deletion methods, confirmation dialogs, and best practices for safe record removal.

## Single Record Deletion

### Accessing the Delete Option

To delete a single record:

1. Locate the row you want to delete in the data grid
2. Right-click on the row to open the context menu
3. Select the delete option from the menu

![Context Menu Delete Option](/images/43-context-menu-delete-option.png)

Deleting records is permanent. Once deleted, the record cannot be recovered unless you have a backup of your database.

### Delete Confirmation Dialog

After selecting delete:

1. A confirmation dialog appears
2. It shows the record details or a confirmation message
3. You must confirm the deletion to proceed
4. Option to cancel available if you change your mind

### Confirmation Process

The confirmation dialog typically shows:

- **Record Identifier**: Which record will be deleted
- **Warning Message**: Clear statement that deletion is permanent
- **Confirm Button**: To proceed with deletion
- **Cancel Button**: To abort the deletion

Always review the record being deleted in the confirmation dialog to ensure you're deleting the correct row.

## Batch Record Deletion

For deleting multiple records at once:

### Selecting Multiple Rows

First, select the records you want to delete:

1. Click the checkbox at the start of a row to select it
2. Click additional checkboxes to select more rows
3. Use the header checkbox to select all rows on the current page

![Table Row Selection Single](/images/44-table-row-selection-single.png)

Note that batch selection typically works only on the current page. Pagination may limit selections to visible rows.

### Accessing Batch Delete

Once rows are selected:

![Context Menu Select Row](/images/45-context-menu-select-row.png)

1. Right-click on any selected row
2. Select the delete option from the context menu
3. A bulk delete confirmation appears

### Batch Delete Confirmation

The confirmation for batch deletion shows:

- **Count of Records**: How many records will be deleted
- **Warning**: Clear indication that multiple records will be removed
- **Confirm Button**: Proceeds with deleting all selected records
- **Cancel Button**: Abandons the deletion

Batch deletion is faster than deleting records one by one, but use it carefully to avoid accidentally deleting multiple records.

## Delete Operations

### Successful Deletion

When deletion is confirmed:

1. The deletion processes
2. A success message appears
3. The row(s) disappear from the data grid
4. Page count updates if all records on a page are deleted
5. The view refreshes to show remaining data

After deletion, if you're on a page that now has no records, you'll automatically be moved to the previous page with data.

### Failed Deletions

If deletion fails, you'll see an error message. Common reasons include:

**Foreign Key Constraint Violations**
- Another table has records referencing this one
- You must delete the referencing records first
- The error message specifies which constraint is violated

**Permission Denied**
- Your database user lacks delete permissions
- Contact your database administrator
- Check your user role and permissions

**Record Locked**
- Another user or process is accessing the record
- Wait a moment and try again
- The record may be in use elsewhere

## Cascade Deletion

When deleting a record with foreign key references:

### Understanding Cascade Rules

Your database may have cascade delete rules:

- **CASCADE**: Deletes referencing records automatically
- **RESTRICT**: Prevents deletion if references exist
- **SET NULL**: Sets referencing foreign keys to NULL
- **NO ACTION**: Similar to RESTRICT, prevents deletion

### Before Confirming Deletion

Understanding cascade behavior is important:
- Check if related records will be automatically deleted
- Be aware of any cascading effects on related tables
- Some deletions might remove more data than expected

If your table has cascading deletes enabled, deleting one record might remove multiple related records across tables. Understand your schema before deleting.

## Advanced Deletion Methods

### Using the Scratchpad for Complex Deletions

For more sophisticated deletion patterns:

1. Navigate to the Scratchpad query interface
2. Write a DELETE query with WHERE conditions
3. Execute to delete multiple records matching criteria

Example:
```
DELETE FROM users WHERE status = 'inactive' AND created_at < '2020-01-01';
```

The Scratchpad gives you more control for conditional deletions affecting many records. See the Query section for more details.

### Batch Operations with Filters

Delete groups of records matching criteria:

1. Apply filters to show only records you want to delete
2. Select the filtered records
3. Perform batch deletion on the filtered set

Using filters before batch deletion helps you verify which records will be removed, reducing the risk of accidental deletion.

## Safety Features

### Confirmation Dialogs

- **Required Confirmation**: Every deletion requires explicit confirmation
- **Clear Messaging**: Dialog clearly states what will be deleted
- **Easy Cancellation**: Cancel button always available

### Undo Limitations

WhoDB does not provide an undo function for deletions. Once confirmed and completed, the deletion is permanent. Only database backups can restore deleted data.

### Best Practices for Safety

Always verify you're deleting the correct record before confirming.

When deleting multiple records, apply filters first and review the filtered results.

Know whether your deletion will cascade to related tables.

Ensure recent backups exist before performing bulk deletions.

Practice deletions in a non-production environment if unfamiliar.

## Deletion Scenarios

### Scenario 1: Delete a Single User Record

1. Find the user record in the table
2. Right-click to open context menu
3. Select "Delete Row"
4. Review the user details in confirmation dialog
5. Click "Confirm Delete"
6. Record is removed and grid updates

### Scenario 2: Delete Multiple Inactive Records

1. Use filters to show only inactive records
2. Select all visible inactive records
3. Right-click and select batch delete
4. Confirm in the bulk deletion dialog
5. All selected inactive records are removed

### Scenario 3: Delete Records with Cascade

1. Understand the cascade rules for your table
2. Select the record(s) to delete
3. Note that related records may also be deleted
4. Confirm the deletion knowing the cascade impact
5. All related records are removed automatically

## Troubleshooting

**Can't Delete - Foreign Key Error**
- Other tables reference this record
- Delete the referencing records first
- Or modify the foreign key references
- Then retry the deletion

**Can't Delete - Permission Denied**
- You lack delete permissions
- Contact your database administrator
- Request appropriate user permissions
- Check your database user role

**Delete Confirmation Won't Appear**
- Ensure you right-clicked on the row
- Try again with a different selection method
- Check if the record is already deleted

**Wrong Records Deleted**
- Unfortunately, this is permanent
- Recovery requires restoring from backup
- Always verify selections before confirming

## Keyboard Shortcuts

| Shortcut | Action |
|----------|--------|
| `Delete` key | May open delete menu (context-dependent) |
| `Escape` | Cancel deletion confirmation |
| `Ctrl/Cmd + Z` | Note: Undo is NOT available for deletions |

## Limitations

- **No Undo**: Deletions cannot be undone through WhoDB
- **Permanent**: No soft-delete recovery mechanism
- **One Operation at a Time**: Must wait for each deletion to complete
- **Page-Limited Batch Ops**: Batch operations limited to current page
- **No Scheduled Deletion**: Cannot schedule deletions for later

If you need to recover deleted data, you'll need to restore from a database backup. Always maintain regular backups of critical databases.

## Preventing Accidental Deletions

### Strategies to Stay Safe

1. **Use Filters First**: Always filter to verify what you're about to delete
2. **Batch Operations Carefully**: Carefully select rows before batch operations
3. **Read Confirmations**: Always read and review confirmation dialogs
4. **Test in Dev First**: Practice deletions in development before production
5. **Keep Backups**: Maintain recent backups for recovery if needed

## Next Steps

After managing record deletions, explore related features:

Learn how to modify existing records

Insert new records into your tables

Navigate and review your table data

Use SQL queries for complex deletions

Remember: Deletions in WhoDB affect your actual database. Be cautious and always verify before confirming deletion of any records. When in doubt, check a backup first before attempting significant deletions.

---


# data/filtering-searching.mdx
## Filtering & Searching

Learn how to search for records and apply advanced filters using WHERE conditions

# Filtering & Searching

WhoDB provides powerful search and filtering capabilities to help you quickly find the data you need. This guide covers the different filtering methods, search operators, and advanced techniques for data discovery.

## Quick Search

### Basic Search

The simplest way to find data is using the quick search feature:

![Data View Search Highlight](/images/19-data-view-search-highlight.png)

1. Click the search box in the action bar
2. Type your search term
3. Results are highlighted in real-time as you type
4. The search applies to all visible columns

### Search Behavior

- **Real-time Matching**: Results update as you type
- **Case-Insensitive**: Searches are typically not case-sensitive
- **Partial Matching**: Matches anywhere within cell values
- **All Columns**: Searches across all columns in the table

Use quick search when you know a specific value you're looking for but aren't sure which column contains it.

### Clearing Search

- Click the clear button (X) to remove the search term
- Press Escape to exit search mode
- The full dataset reappears

## Advanced WHERE Conditions

For more sophisticated filtering beyond simple search, use WHERE conditions:

### Opening WHERE Conditions

To access advanced filtering:

1. Click the filter button in the action bar
2. The WHERE conditions panel opens

![Data View Where Conditions Popover](/images/16-data-view-where-conditions-popover.png)

### Building Filter Conditions

The WHERE conditions interface allows you to build complex queries:

Click on the field selector dropdown to choose which column to filter.

![Data View Where Field Dropdown](/images/17-data-view-where-field-dropdown.png)

Select the comparison operator for your condition (see operators below).

Type or select the value to compare against.

Click "Add Condition" to add more filters.

Use AND/OR to combine multiple conditions logically.

Click "Apply" or "Filter" to execute the WHERE clause.

## Filter Operators

WhoDB supports multiple comparison operators for filtering:

### Equality Operators

**Equals (=)**
![Where Operator Equals](/images/59-where-operator-equals.png)

- Matches exact values
- Case-sensitive (depending on database)
- `WHERE status = 'active'`

**Not Equals (!=)**
![Where Operator Not Equals](/images/62-where-operator-not-equals.png)

- Matches all values except the specified one
- `WHERE role != 'admin'`
- Useful for excluding specific records

### Comparison Operators

**Greater Than (>)**
![Where Operator Greater Than](/images/60-where-operator-greater-than.png)

- Matches values larger than specified
- Primarily for numeric or date comparisons
- `WHERE age > 18`
- `WHERE created_at > '2023-01-01'`

**Less Than (\<)**
![Where Operator Less Than](/images/61-where-operator-less-than.png)

- Matches values smaller than specified
- Works with numbers, dates, and text
- `WHERE price < 100`

**Greater Than or Equal To (>=)**
![Where Operator GTE](/images/63-where-operator-gte.png)

- Matches values greater than or equal to specified
- `WHERE score >= 80`

**Less Than or Equal To (\<=)**
![Where Operator LTE](/images/64-where-operator-lte.png)

- Matches values less than or equal to specified
- `WHERE stock <= 10`

### String Operators

**Contains/LIKE**
- Partial text matching
- `WHERE email LIKE '%@company.com'`
- Useful for finding patterns

**Starts With**
- Matches beginning of text
- `WHERE code LIKE 'US%'`

**Ends With**
- Matches end of text
- `WHERE email LIKE '%@gmail.com'`

### NULL Operators

**Is NULL**
- Matches empty/NULL values
- `WHERE phone IS NULL`

**Is Not NULL**
- Matches non-empty values
- `WHERE updated_at IS NOT NULL`

### Range Operators

**Between**
- Matches values within a range
- `WHERE price BETWEEN 50 AND 150`
- `WHERE created_at BETWEEN '2023-01-01' AND '2023-12-31'`

**In**
- Matches if value is in a list
- `WHERE status IN ('active', 'pending')`

## Combining Multiple Conditions

### AND Logic

All conditions must be true:

![Data View Where Conditions Badge](/images/18-data-view-where-conditions-badge.png)

```
WHERE status = 'active' AND age > 18
```

- Shows only records that match ALL conditions
- Use when you need records meeting multiple criteria

### OR Logic

Any condition can be true:

```
WHERE status = 'active' OR role = 'admin'
```

- Shows records matching ANY condition
- Use when looking for records matching one of several options

### Complex Combinations

Combine AND and OR operators:

```
WHERE (status = 'active' AND age > 18) OR (role = 'manager')
```

- Group conditions with parentheses
- Build sophisticated queries
- The interface handles complex logic

When combining conditions, AND has higher precedence than OR. Use the interface to clearly structure your logic.

## Filtering Workflow

### Complete Filtering Example

Let's filter a users table to find active users over 30 years old:

Open the WHERE conditions dialog from the action bar.

Select "status" field, "=" operator, value "active"

Click "Add Condition" and select AND logic

Select "age" field, ">" operator, value "30"

Click "Apply" to execute the filter

The table now shows only active users over 30

### Viewing Active Filters

- **Filter Badge**: Shows active filters in the action bar
- **Condition Count**: Displays number of active conditions
- **Edit Filters**: Click the badge to modify or remove filters
- **Clear Filters**: Remove all conditions to see full dataset

## Search and Filter Combinations

### Using Search AND WHERE Conditions Together

You can combine quick search with WHERE conditions:

1. Apply WHERE conditions to filter by specific criteria
2. Use search to find specific values within the filtered results
3. The results show only records matching both the search and conditions

This combination is powerful for finding specific records within a large filtered subset.

## Advanced Filtering Scenarios

### Find All Inactive Users Created Before 2023

1. Open WHERE conditions
2. Add condition: status = 'inactive'
3. Add AND condition: created_at < '2023-01-01'
4. Apply
5. Results show matching records

### Find Records with Missing Data

To find records where optional fields are empty:

1. Filter: email IS NULL
2. Results show records without email addresses
3. Useful for data quality checks

### Find Records in a Date Range

To find all transactions in Q3 2023:

1. Open WHERE conditions
2. Add condition: transaction_date >= '2023-07-01'
3. Add AND condition: transaction_date \<= '2023-09-30'
4. Or use BETWEEN operator for simpler syntax

## Performance Considerations

### Filter Tips for Large Tables

- **Filter Early**: Apply filters before sorting or searching
- **Use Indexes**: Columns with database indexes filter faster
- **Simple Conditions**: Simpler conditions execute faster
- **Avoid Complex Patterns**: LIKE '%value%' is slower than LIKE 'value%'

For very large tables, applying restrictive filters first significantly improves performance and responsiveness.

## Best Practices

Begin with general filters and narrow down if needed.

Use AND for narrowing results, OR for expanding options.

Always check that filtered results make sense before further operations.

Choose operators matching your data types (e.g., > for numbers).

Remove filters when you want to see the full dataset again.

## Keyboard Shortcuts

| Shortcut | Action |
|----------|--------|
| `Ctrl/Cmd + F` | Open quick search |
| `Escape` | Close search or filter panel |
| `Enter` | Apply filter or search |
| `Ctrl/Cmd + Shift + F` | Open advanced WHERE conditions |

## Troubleshooting

**No Results Found**
- Verify the filter values are correct
- Check spelling and case sensitivity
- Try broadening the filter conditions
- Ensure column name is correct

**Too Many Results**
- Add additional AND conditions to narrow results
- Use more specific comparison operators
- Check that OR conditions aren't too broad

**Filter Not Applying**
- Confirm you clicked "Apply" button
- Check that all required fields have values
- Verify the filter logic is correct

**Unexpected Results**
- Review the conditions in the filter panel
- Check if multiple conditions are combining correctly
- Verify AND/OR logic is appropriate

## Limitations

- **One Table at a Time**: Filters apply to the current table only
- **No Cross-Table Filters**: Cannot filter by values in other tables through the UI (use Scratchpad for JOINs)
- **Pattern Complexity**: LIKE patterns have limitations depending on your database
- **Performance**: Very complex filters on large datasets may be slow

## Advanced: Using Scratchpad for Complex Queries

For filtering beyond WHERE conditions capabilities:

1. Use the Scratchpad query interface
2. Write custom SQL with WHERE clauses
3. Combine filters with JOINs, GROUP BY, or aggregations
4. See the Query section for more details

## Next Steps

Explore related data management features:

Learn how to organize and navigate through results

Understand the data grid interface

Write custom SQL queries for advanced filtering

Deep dive into advanced WHERE condition usage

Effective filtering is key to working efficiently with large databases. Master these tools to quickly find exactly the data you need.

---


# data/sorting-pagination.mdx
## Sorting & Pagination

Learn how to sort columns and navigate through large datasets using pagination

# Sorting & Pagination

WhoDB provides intuitive sorting and pagination features to help you organize and navigate through large datasets efficiently. This guide covers how to sort data, adjust page sizes, and navigate through pages.

## Column Sorting

### Basic Single Column Sort

To sort by a single column:

1. Click on any column header
2. The column sorts in ascending order
3. Click again to reverse to descending order
4. Click a third time to remove sorting

![Sorted Column Example](/images/38-data-view-sorted-column.png)

### Sort Indicators

- **Sort Arrow**: Shows which column is currently sorted
- **Ascending (↑)**: Data is sorted from lowest to highest
- **Descending (↓)**: Data is sorted from highest to lowest
- **No Arrow**: Column is not currently sorted

### Sort Behavior by Data Type

**Text Columns**
- Ascending: Alphabetical order (A-Z)
- Descending: Reverse alphabetical (Z-A)
- Case-sensitive or insensitive depending on database

**Numeric Columns**
- Ascending: Smallest to largest values
- Descending: Largest to smallest values
- NULL values typically appear at the end

**Date & Time Columns**
- Ascending: Oldest to newest dates
- Descending: Newest to oldest dates
- Timestamp precision respected

**Boolean Columns**
- FALSE typically sorts first
- TRUE sorts second
- Consistent regardless of ascending/descending

Click a column header to quickly sort by that column. It's the fastest way to organize your data.

## Multi-Column Sorting

For more sophisticated sorting across multiple columns:

### Using Shift+Click

To sort by multiple columns:

1. Click the first column header to sort by it
2. Hold Shift and click additional column headers
3. Each additional column becomes a secondary, tertiary sort level
4. Visual indicators show the sort order

### Sort Priority

- **First Click**: Primary sort (highest priority)
- **Second Shift+Click**: Secondary sort
- **Third Shift+Click**: Tertiary sort, etc.

### Clearing Multi-Column Sorts

- Click a column without Shift to make it the primary sort
- Shift+click the same column again to remove it from sorting
- Use the sort reset button if available in the interface

Multi-column sorting is useful for grouping related records together (e.g., sort by department, then by last name).

## Pagination Overview

For tables with many records, pagination breaks the data into manageable pages:

### Understanding Page Controls

![Page Size Dropdown](/images/15-data-view-page-size-dropdown.png)

The pagination controls show:

- **Current Page**: Which page you're viewing
- **Total Records**: Total number of records in the table
- **Total Pages**: How many pages total
- **Page Size**: Number of records per page
- **Navigation Buttons**: Move between pages

### Page Size Options

WhoDB provides several standard page sizes:

**Page Size: 10**
![Page Size 10 Selected](/images/56-page-size-10-selected.png)

- Default view for quick browsing
- Fast loading of small datasets
- Suitable for most tables

**Page Size: 25**
![Page Size 25 Selected](/images/57-page-size-25-selected.png)

- Balanced view showing more data per page
- Good for detailed inspection
- Still maintains good performance

**Page Size: 50**
![Page Size 50 Selected](/images/58-page-size-50-selected.png)

- Shows many records per page
- Reduces page navigation
- Suitable for less intensive operations

### All Options View

![Page Size All Options](/images/55-page-size-all-options.png)

Available page sizes typically include:
- 10 (default)
- 25
- 50
- 100 (if database supports it)
- All (load entire table)

Selecting "All" on a large table may cause performance issues. Use smaller page sizes when possible.

## Changing Page Size

### Setting Page Size

1. Look for the page size selector in the pagination controls
2. Click on the current page size to open the dropdown
3. Select your desired page size
4. The table reloads with the new page size

### Page Size Effects

- **Smaller Sizes** (10): Faster loading, more page navigation
- **Larger Sizes** (50+): Fewer pages, longer initial load
- **All Records**: No pagination, all records at once

If the table is very responsive, use a larger page size. If performance degrades, switch to a smaller page size.

## Navigating Between Pages

### Page Navigation Controls

**First Page**
- Button to jump directly to the first page
- Useful when viewing later pages

**Previous Page**
- Navigate one page backward
- Disabled when already on first page

**Next Page**
- Navigate one page forward
- Disabled when on the last page

**Last Page**
- Jump directly to the last page
- Useful for viewing most recent records (if sorted ascending by ID)

**Page Input**
- Type a specific page number to jump directly to it
- Enter the page number and press Enter

### Quick Navigation

- **Keyboard**: Use arrow keys to move between pages
- **Direct Entry**: Some interfaces allow typing a page number

## Pagination Workflow

### Complete Pagination Example

Viewing a table with 250 records:

Click the page size dropdown and select 25 to view 25 records per page.

The first page loads showing records 1-25 of 250 (total 10 pages).

Click a column header to sort the data. Sorting resets you to page 1.

Click Next to view the next 25 records (26-50).

Click Last Page to jump to page 10 (records 226-250).

Click First Page to return to the beginning.

## Advanced Pagination Scenarios

### Finding Records in Large Tables

To find a specific record in a 10,000+ record table:

1. **Use Filters**: Apply WHERE conditions to narrow the dataset
2. **Sort Strategically**: Sort by a key field to group similar records
3. **Use Pagination**: Navigate through the filtered/sorted results
4. **Or Use Search**: Quick search for a specific value

Combining search, filters, and sorting is faster than manually navigating through all pages.

### Viewing Most Recent Records

For tables with timestamp columns:

1. Sort by the date/timestamp column in descending order
2. Most recent records appear first on page 1
3. Navigate backward through pages for older records

### Grouping Similar Records

To review records of a specific type:

1. Apply a filter to show only that type (e.g., status = 'active')
2. Sort by a secondary field (e.g., name or date)
3. Review through the pages

## Sorting and Pagination Together

### Sort Then Paginate

The typical workflow:

1. **Sort**: Choose column(s) to sort by
2. **Filter** (Optional): Apply conditions if needed
3. **Set Page Size**: Choose appropriate page size
4. **Navigate**: Go through pages as needed

### Maintaining Sort After Navigation

- Sort order is maintained when navigating between pages
- Changing page size preserves sort settings
- Return to sorted view after filtering

## Performance Considerations

### Large Dataset Optimization

**For Very Large Tables (100K+ rows)**

1. **Use Filters First**: Dramatically reduce result set before sorting
2. **Use Smaller Page Sizes**: Faster initial load
3. **Avoid Sorting Complex Data**: Numeric sorts are faster than text
4. **Sort by Indexed Columns**: Much faster on databases with indexes

Sorting and pagination performance depends significantly on your database and available indexes. Talk to your database administrator for optimization suggestions.

### Memory Usage

- **Larger Page Sizes**: Use more browser memory but reduce page transitions
- **Smaller Page Sizes**: Lower memory usage but more page navigation
- **Balance**: Choose page size based on your available resources

## Best Practices

Apply filters first to reduce the dataset before sorting or pagination.

Sort by columns that help you find what you're looking for quickly.

Choose a page size that balances speed with information density.

Use Shift+click for secondary sorts when grouping data.

Check that sorting is showing expected results, especially for mixed data types.

## Keyboard Shortcuts

| Shortcut | Action |
|----------|--------|
| `Click Column Header` | Sort by column |
| `Shift + Click Column` | Multi-column sort |
| `Ctrl/Cmd + Click` | Sometimes alternative sort behavior |
| `Page Down` | Next page |
| `Page Up` | Previous page |
| `Ctrl/Cmd + Home` | First page |
| `Ctrl/Cmd + End` | Last page |

## Common Issues

### Sorting Not Working

**Issue**: Column doesn't sort when clicked

Solutions:
- Click directly on the column header text
- Check that the column is not locked or read-only
- Verify your browser has JavaScript enabled
- Try sorting by a different column

### Pagination Issues

**Issue**: Can't navigate to certain pages

Possible causes:
- Page number exceeds total pages
- Apply filters that reduce results
- Check the page count display
- Try using First/Last page buttons

**Issue**: Page size change not working

Solutions:
- Ensure you selected a different page size
- Try refreshing the page
- Check your browser console for errors

### Performance Issues

**Issue**: Sorting/pagination is very slow

For slow performance:
- Reduce page size for faster loading
- Apply filters to reduce dataset
- Check your database connection
- Consider using the Scratchpad for complex operations

## Limitations

- **Single Table**: Sorting and pagination work on one table at a time
- **Page-Based Navigation**: No continuous scroll (traditional pagination)
- **Sort Persistence**: Sorts may reset when filtering
- **Real-Time Data**: External changes require refresh to appear

## Advanced: Working with Large Datasets

### Using Scratchpad for Complex Operations

For sophisticated sorting and analysis:

1. Use the Scratchpad query interface
2. Write SQL with ORDER BY clauses
3. Use LIMIT and OFFSET for custom pagination
4. Combine with WHERE, GROUP BY, etc.

Example:
```
SELECT * FROM users
WHERE status = 'active'
ORDER BY created_at DESC, name ASC
LIMIT 25 OFFSET 0
```

See the Query section for more details.

## Next Steps

Explore related data management features:

Learn to filter and search your data

Understand the data grid interface

Write custom SQL for advanced operations

Explore all data management features

Efficient sorting and pagination are essential skills for working with large databases. Practice these techniques to become proficient at quickly finding and organizing your data.

---


# query/scratchpad-intro.mdx
## Scratchpad Introduction

Get started with WhoDB's powerful Scratchpad query interface

# Scratchpad Introduction

The Scratchpad is WhoDB's powerful SQL query editor that allows you to write, execute, and manage custom SQL queries. It combines a modern code editor with results visualization, query history, and multi-cell support for organizing complex workflows.

The Scratchpad is your SQL playground for exploring data, testing queries, and performing advanced operations

## Main Interface Overview

When you access the Scratchpad view for the first time, you'll see the complete query interface:

![Scratchpad Main View](/images/27-scratchpad-main-view.png)

The Scratchpad interface consists of several key components:

Write SQL queries with syntax highlighting and auto-completion

Run queries, clear results, and manage execution settings

View query results in an interactive data grid

Access previous queries and reuse saved commands

Create multiple cells to organize related queries

Clear error messages with debugging information

## Key Capabilities

### SQL Query Execution

Execute any SQL query supported by your database engine:

Retrieve data from your tables:

```
SELECT id, name, email, created_at
FROM users
WHERE status = 'active'
ORDER BY created_at DESC
LIMIT 50;
```

Results display in an interactive grid with sorting, filtering, and export capabilities.

Insert, update, or delete data:

```
UPDATE products
SET price = price * 1.1
WHERE category = 'electronics'
AND price > 100;
```

Execution results show how many rows were affected.

Perform calculations and groupings:

```
SELECT
  category,
  COUNT(*) as total_items,
  AVG(price) as avg_price,
  MAX(price) as max_price
FROM products
GROUP BY category
ORDER BY total_items DESC;
```

Perfect for analytics and reporting.

Combine data from multiple tables:

```
SELECT
  u.id,
  u.username,
  COUNT(o.id) as order_count,
  SUM(o.total) as total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.username
ORDER BY total_spent DESC;
```

Visualize complex relationships with ease.

### Multi-Cell Organization

Organize related queries in separate cells within the same Scratchpad:

![Scratchpad Multiple Cells](/images/31-scratchpad-multiple-pages.png)

Each cell operates independently, allowing you to:
- Run multiple queries without losing previous results
- Organize queries by task or workflow
- Compare results between different queries
- Reorder cells to match your workflow

Each cell maintains its own execution history and results

### Query History

All executed queries are automatically saved to history:

![Query History Panel](/images/33-scratchpad-query-history.png)

Quick access to:
- Recently executed queries
- Query cloning for quick variations
- Search through historical queries
- Timestamp and execution details

### Real-Time Feedback

Get instant feedback on query execution:

**Successful Execution:**

![Query Results](/images/29-scratchpad-query-results.png)

See row counts, execution time, and formatted results.

**Error Handling:**

![Query Error](/images/30-scratchpad-query-error.png)

Clear error messages help you debug SQL syntax issues.

## Database Support

The Scratchpad works with all WhoDB-supported databases:

Full SQL support including window functions and JSON operations

Complete MySQL dialect support with optimization tips

SQLite-specific SQL with full feature support

MongoDB aggregation pipeline queries

Key-value operations and data inspection

WhoDB continuously adds support for additional databases

## Common Use Cases

### 1. Data Exploration

Quickly explore your database structure and data:

```
-- Check table size
SELECT COUNT(*) as row_count FROM users;

-- View sample data
SELECT * FROM users LIMIT 10;

-- Check column data types and constraints
PRAGMA table_info(users);  -- SQLite
DESC users;                -- MySQL
```

### 2. Performance Testing

Test query performance before deploying to production:

```
-- Find slow-running queries
SELECT
  id,
  username,
  email
FROM users
WHERE email LIKE '%@example.com'
ORDER BY created_at DESC
LIMIT 1000;
```

### 3. Data Validation

Verify data quality and integrity:

```
-- Find missing or invalid data
SELECT id, name, email
FROM users
WHERE email IS NULL
  OR email NOT LIKE '%@%.%';

-- Check for duplicates
SELECT email, COUNT(*)
FROM users
GROUP BY email
HAVING COUNT(*) > 1;
```

### 4. Batch Operations

Perform bulk data modifications:

```
-- Archive old records
DELETE FROM audit_logs
WHERE created_at < NOW() - INTERVAL '1 year';

-- Update multiple records
UPDATE users
SET verified = true
WHERE email LIKE '%@company.com';
```

## Interface Elements

### Execution Controls

At the top of each cell, you'll find:

![Scratchpad Code Editor](/images/28-scratchpad-code-editor.png)

- **Run Button** (`Cmd/Ctrl + Enter`): Execute the query
- **Clear Button**: Remove results and start fresh
- **Cell Menu**: Additional cell options and settings
- **Status Indicator**: Shows query execution status

### Action Results

After executing action queries (INSERT, UPDATE, DELETE):

![Action Result](/images/40-scratchpad-action-result.png)

WhoDB displays:
- Number of rows affected
- Execution time
- Operation status (success/failure)

### Cell Options

Click the cell menu to access:

![Cell Options Menu](/images/32-scratchpad-cell-options-menu.png)

- Rename cell
- Duplicate cell
- Delete cell
- Copy results
- Clear results
- Export results

## Getting Started Tips

Begin with basic SELECT queries to understand your data structure before moving to complex operations.

The editor provides schema-aware suggestions—start typing a table name and let the editor help complete it.

Regularly executed queries are automatically saved to history. Pin frequently used queries for quick access.

Always test queries on development data before running them on production databases.

Use `Cmd/Ctrl + Shift + F` to auto-format SQL for better readability.

## Keyboard Shortcuts

Master these shortcuts for faster query work:

| Shortcut | Action |
|----------|--------|
| `Cmd/Ctrl + Enter` | Execute query |
| `Cmd/Ctrl + Shift + F` | Format code |
| `Cmd/Ctrl + N` | New cell |
| `Cmd/Ctrl + D` | Duplicate cell |
| `Cmd/Ctrl + /` | Toggle comment |
| `Cmd/Ctrl + Z` | Undo |
| `Cmd/Ctrl + Shift + Z` | Redo |

## Best Practices

Use consistent indentation and clear naming. Your future self will thank you.

```
SELECT
  u.id,
  u.username,
  COUNT(o.id) as total_orders
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.status = 'active'
GROUP BY u.id, u.username
ORDER BY total_orders DESC;
```

Document complex logic or important context within your SQL.

```
-- Find users who placed orders in the last 90 days
-- but haven't made a purchase in the last 30 days
SELECT u.id, u.username
FROM users u
WHERE u.id IN (
  SELECT user_id FROM orders
  WHERE created_at > NOW() - INTERVAL '90 days'
)
AND u.id NOT IN (
  SELECT user_id FROM orders
  WHERE created_at > NOW() - INTERVAL '30 days'
);
```

Always use LIMIT when exploring large tables or use transactions for modifications.

```
-- Safe exploration
SELECT * FROM large_table LIMIT 100;

-- Safe modification (if your database supports transactions)
BEGIN;
UPDATE users SET active = false WHERE inactive_days > 365;
-- Review the change, then COMMIT or ROLLBACK
```

Clone and modify previous queries instead of rewriting from scratch.

## Next Steps

Now that you understand the Scratchpad interface, dive deeper into specific features:

Query using natural language instead of SQL

Learn about the code editor, syntax highlighting, and auto-completion

Master result display and data grid controls

Learn to organize and reuse your queries

The Scratchpad is powerful but be careful with DELETE and UPDATE queries—always backup important data first

---


# query/writing-queries.mdx
## Writing Queries

Master the Scratchpad code editor with syntax highlighting and auto-completion

# Writing Queries

The Scratchpad code editor provides a professional SQL editing experience with syntax highlighting, auto-completion, and error detection. Whether you're writing simple queries or complex multi-statement scripts, the editor has tools to make you more productive.

Use the built-in editor features to write correct SQL faster

## Code Editor Overview

The query editor combines powerful features in a clean, focused interface:

![Scratchpad Code Editor](/images/28-scratchpad-code-editor.png)

### Key Editor Features

Keywords, functions, and values displayed in distinct colors

Schema-aware suggestions for tables, columns, and functions

Easy reference when debugging errors

Instant feedback on syntax issues

Write complex queries spanning multiple lines

Visual alignment for nested queries and formatting

## Syntax Highlighting

Every SQL element is color-coded for clarity:

### SQL Syntax Colors

Reserved words like SELECT, FROM, WHERE, JOIN are highlighted in blue:

```
SELECT id, name FROM users WHERE status = 'active';
```

All SQL keywords are recognized and highlighted consistently across all databases.

Built-in functions like COUNT, SUM, CONCAT are in purple:

```
SELECT
  COUNT(*) as total_count,
  AVG(salary) as average_salary,
  CONCAT(first_name, ' ', last_name) as full_name
FROM employees;
```

Functions are database-specific and highlighted according to your target database.

Text values in single quotes are displayed in green:

```
SELECT * FROM users
WHERE username = 'john_doe'
AND email LIKE '%@example.com';
```

Different quote styles are recognized: '', "", and backticks (database-specific).

Numeric values appear in orange:

```
SELECT id, price, discount
FROM products
WHERE price > 100
AND quantity >= 5;
```

Decimals, integers, and scientific notation are all recognized.

SQL comments are grayed out:

```
-- This is a single-line comment
/* This is a
   multi-line comment */
SELECT id, name FROM users; -- inline comment
```

Both single-line (--) and multi-line (/* */) comments are supported.

## Auto-Completion

The editor provides context-aware suggestions to speed up your typing:

### Schema-Aware Suggestions

Start typing a table name and WhoDB suggests tables from your schema:

```
SELECT * FROM u  -- Suggests: users, user_profiles, user_settings
```

### Column Suggestions

After selecting a table, suggestions include available columns:

```
SELECT us  -- Suggests: username, user_id, user_type, user_status
FROM users
```

### Function Suggestions

As you type functions, you get suggestions for available functions:

```
SELECT COUNT(  -- Suggests: COUNT, COUNT(DISTINCT)
```

### Trigger Suggestions

Auto-completion appears when you:
- Type a table name after FROM, JOIN, INTO, UPDATE
- Type a column name after SELECT
- Type a function name followed by `(`
- Need to reference aliases

Press `Ctrl + Space` (or `Cmd + Space` on Mac) to manually trigger auto-completion at any time

## Writing Different Query Types

### SELECT Queries

Retrieve data from your tables:

```
-- Simple selection
SELECT id, name, email
FROM users
WHERE created_at > '2024-01-01'
LIMIT 10;

-- With ordering
SELECT
  id,
  name,
  email,
  status,
  created_at
FROM users
ORDER BY created_at DESC, name ASC;

-- With filtering and multiple conditions
SELECT id, name, balance
FROM accounts
WHERE status = 'active'
  AND balance > 1000
  AND created_at >= DATE('2023-01-01')
LIMIT 100;
```

### Aggregation Queries

Calculate statistics and group data:

```
-- Count and average by category
SELECT
  category,
  COUNT(*) as item_count,
  AVG(price) as avg_price,
  MIN(price) as min_price,
  MAX(price) as max_price,
  SUM(quantity) as total_quantity
FROM products
GROUP BY category
HAVING COUNT(*) > 5
ORDER BY item_count DESC;

-- Time-based aggregation
SELECT
  DATE_TRUNC('month', created_at) as month,
  COUNT(*) as order_count,
  SUM(total) as revenue
FROM orders
WHERE created_at >= '2024-01-01'
GROUP BY DATE_TRUNC('month', created_at)
ORDER BY month DESC;
```

### JOIN Queries

Combine data from multiple tables:

```
-- Inner join (matching records only)
SELECT
  u.id,
  u.username,
  o.id as order_id,
  o.total,
  o.status
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE o.status = 'completed'
ORDER BY o.created_at DESC;

-- Left join (all users, with matching orders)
SELECT
  u.id,
  u.username,
  COUNT(o.id) as order_count,
  SUM(o.total) as total_spent,
  MAX(o.created_at) as last_order
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.username
ORDER BY total_spent DESC;

-- Multiple joins
SELECT
  u.username,
  o.id as order_id,
  p.name as product_name,
  oi.quantity,
  oi.price
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id
WHERE o.created_at >= '2024-10-01'
ORDER BY o.created_at DESC;
```

### UPDATE Queries

Modify existing data:

```
-- Simple update
UPDATE users
SET last_login = NOW()
WHERE id = 123;

-- Conditional update with multiple columns
UPDATE orders
SET status = 'shipped', shipped_at = NOW()
WHERE id = 456
  AND status = 'processing';

-- Update based on another table
UPDATE users u
SET premium = true
WHERE EXISTS (
  SELECT 1 FROM orders o
  WHERE o.user_id = u.id
  AND o.total > 1000
);

-- Bulk update with calculation
UPDATE products
SET price = price * 1.1
WHERE category = 'electronics'
  AND price < 50;
```

### DELETE Queries

Remove records (use with caution):

```
-- Delete specific record
DELETE FROM users WHERE id = 999;

-- Delete with multiple conditions
DELETE FROM audit_logs
WHERE created_at < '2024-01-01'
  AND severity = 'debug';

-- Delete unused records
DELETE FROM temporary_data
WHERE NOT EXISTS (
  SELECT 1 FROM main_table
  WHERE main_table.ref_id = temporary_data.id
);
```

### INSERT Queries

Add new records:

```
-- Simple insert
INSERT INTO users (username, email, status)
VALUES ('john_doe', 'john@example.com', 'active');

-- Insert multiple rows
INSERT INTO products (name, category, price)
VALUES
  ('Laptop', 'electronics', 1200.00),
  ('Mouse', 'electronics', 25.00),
  ('Keyboard', 'electronics', 75.00);

-- Insert from select
INSERT INTO user_archive
SELECT * FROM users
WHERE status = 'inactive'
  AND last_login < NOW() - INTERVAL '1 year';
```

### Subqueries

Nest queries for complex logic:

```
-- Subquery in SELECT
SELECT
  id,
  username,
  (SELECT COUNT(*) FROM orders WHERE user_id = users.id) as order_count
FROM users;

-- Subquery in WHERE (EXISTS)
SELECT * FROM users u
WHERE EXISTS (
  SELECT 1 FROM orders o
  WHERE o.user_id = u.id
    AND o.total > 500
);

-- Subquery in WHERE (IN)
SELECT * FROM products
WHERE category_id IN (
  SELECT id FROM categories
  WHERE active = true
);

-- Common Table Expression (CTE)
WITH active_users AS (
  SELECT id, username
  FROM users
  WHERE status = 'active'
),
user_orders AS (
  SELECT
    au.id,
    au.username,
    COUNT(o.id) as order_count
  FROM active_users au
  LEFT JOIN orders o ON au.id = o.user_id
  GROUP BY au.id, au.username
)
SELECT * FROM user_orders
WHERE order_count > 0
ORDER BY order_count DESC;
```

## Query Formatting

Clean, well-formatted SQL is easier to read and debug:

### Manual Formatting Tips

```
-- POOR - Hard to read
SELECT id,name,email,status FROM users WHERE created_at > '2024-01-01' AND status='active' ORDER BY created_at DESC LIMIT 50;

-- GOOD - Clear structure
SELECT
  id,
  name,
  email,
  status,
  created_at
FROM users
WHERE created_at > '2024-01-01'
  AND status = 'active'
ORDER BY created_at DESC
LIMIT 50;
```

### Auto-Format Your Code

Press `Cmd/Ctrl + Shift + F` to automatically format your entire query:

- Consistent indentation (2 spaces)
- Keywords on separate lines
- Proper alignment of SELECT columns
- WHERE conditions properly indented

Auto-formatting helps maintain consistency across all your queries

## Advanced Editing

### Multi-Line Comments

Document complex logic:

```
/*
  Find users who placed orders worth more than $1000
  but haven't purchased anything in the last 30 days.
  Used for targeting reactivation campaigns.
*/
SELECT
  u.id,
  u.email,
  MAX(o.created_at) as last_order_date
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.total > 1000
  AND o.created_at < NOW() - INTERVAL '30 days'
GROUP BY u.id, u.email;
```

### Inline Comments

Add quick notes on specific lines:

```
SELECT
  id,
  username,      -- Primary identifier
  email,          -- For notifications
  last_login,     -- Track activity
  status          -- User state
FROM users;
```

### Selection and Editing

- Select all text: `Cmd/Ctrl + A`
- Cut, copy, paste: Standard keyboard shortcuts
- Find and replace: `Cmd/Ctrl + H`
- Navigate to line: `Ctrl + G`

## Error Detection and Debugging

### Syntax Error Indicators

The editor highlights syntax errors in real-time:

```
SELECT * FROM users  -- Red underline if configured
```

Add the semicolon or click execute (semicolon is optional at line end).

```
SELCT * FROM users  -- Typo in keyword
```

Hover over the error or execute to see the error message.

```
SELECT id, name email FROM users  -- Missing comma before email
```

Check your column list for proper separation.

### Execution Errors

When you execute a query with errors:

![Query Error](/images/30-scratchpad-query-error.png)

The error message appears below the editor with:
- Error type and description
- Specific location information
- Database-specific error details

### Debugging Tips

Start with a simple query and add complexity:

```
-- First: Get the basic table
SELECT * FROM orders LIMIT 10;

-- Then: Add filtering
SELECT * FROM orders WHERE status = 'active' LIMIT 10;

-- Finally: Add joins and grouping
SELECT
  u.username,
  COUNT(o.id) as order_count
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.status = 'active'
GROUP BY u.id, u.username;
```

Verify column names match your schema exactly (case-sensitive in some databases).

```
-- Use the schema explorer or information schema
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'users';
```

Ensure your WHERE conditions match the column data type:

```
-- String comparison
WHERE username = 'john_doe'

-- Number comparison
WHERE age > 18

-- Date comparison
WHERE created_at > '2024-01-01'
```

Always use LIMIT when exploring large tables:

```
SELECT * FROM large_table LIMIT 100;  -- Safe
```

## Best Practices for Query Writing

Think through your query logic before typing. What data do you need? What joins are required? What filters apply?

Clear aliases make complex queries easier to understand:

```
SELECT
  u.id as user_id,
  u.username,
  COUNT(o.id) as total_orders,
  SUM(o.total) as total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id;
```

Maintain consistent formatting throughout your queries. Use auto-format to establish a baseline.

Explain the "why" not the "what":

```
-- Active users defined as status='active' AND not banned
WHERE u.status = 'active'
  AND u.banned_at IS NULL
```

- Use `IS NULL` not `= NULL`
- Use `LIKE` for pattern matching
- Quote identifiers if they contain special characters
- Be careful with data types in comparisons

## Keyboard Shortcuts for Editing

| Shortcut | Action |
|----------|--------|
| `Cmd/Ctrl + Z` | Undo |
| `Cmd/Ctrl + Shift + Z` | Redo |
| `Cmd/Ctrl + X` | Cut |
| `Cmd/Ctrl + C` | Copy |
| `Cmd/Ctrl + V` | Paste |
| `Cmd/Ctrl + F` | Find |
| `Cmd/Ctrl + H` | Find and Replace |
| `Cmd/Ctrl + Shift + F` | Format Code |
| `Cmd/Ctrl + /` | Toggle Comment |
| `Ctrl + G` | Go to Line |
| `Cmd/Ctrl + Space` | Trigger Auto-Complete |

## Next Steps

Master your query results and see how data is displayed:

Learn to work with query results and data visualization

Organize and reuse your written queries

You now have the tools to write clean, efficient SQL queries in WhoDB

---


# query/query-results.mdx
## Query Results

Master result display, data grids, and error handling in the Scratchpad

# Query Results

After executing a query, WhoDB displays results in an interactive data grid with powerful tools for viewing, analyzing, and exporting your data. Understanding how to work with results will help you extract maximum value from your queries.

Query results provide more than just data display—they include sorting, filtering, and export capabilities

## Result Display Types

WhoDB adapts the result display based on your query type:

### SELECT Query Results

Retrieve queries display data in an interactive grid:

![Query Results](/images/29-scratchpad-query-results.png)

Total number of rows returned by the query

How long the query took to execute

Clickable headers with sort and filter options

Scrollable area showing all retrieved rows

Navigate through large result sets

Save results in various formats

### Action Query Results

INSERT, UPDATE, and DELETE queries show operation details:

![Action Result](/images/40-scratchpad-action-result.png)

Information provided:
- **Rows Affected**: Number of records modified
- **Execution Time**: Query performance metric
- **Status**: Success or failure indicator
- **Message**: Additional details or warnings

### Example Results Displayed

```
SELECT id, name, email FROM users LIMIT 5;
```

Result:
- Row count: 5
- Columns: id, name, email
- Grid showing 5 rows of user data

```
SELECT
  category,
  COUNT(*) as count,
  AVG(price) as avg_price
FROM products
GROUP BY category;
```

Result:
- Row count: varies (one per category)
- Columns: category, count, avg_price
- Summary data displayed in grid

```
SELECT
  u.username,
  COUNT(o.id) as orders,
  SUM(o.total) as spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.username;
```

Result:
- Row count: number of users
- Columns: username, orders, spent
- Combined data from multiple tables

```
SELECT COUNT(*) as total FROM orders;
```

Result:
- Row count: 1
- Columns: total
- Single value displayed in grid

## Interactive Data Grid

The results grid provides powerful data manipulation capabilities:

### Column Interactions

Click any column header to sort the data:

```
Click header → Ascending (↑)
Click again → Descending (↓)
Click again → No sort
```

Multi-column sorting:
- Hold Shift and click multiple column headers
- Data sorts by the first column, then second, etc.
- Visual indicators show sort order and priority

Example: Sort by category ascending, then by price descending

Resize columns to see more or less content:

- Hover between column headers
- Drag the resize handle left or right
- Columns maintain size during scrolling

Tip: Double-click the resize handle to auto-fit column width to content

Show or hide columns as needed:

- Click the column menu (three dots)
- Check/uncheck columns to toggle visibility
- Column preferences persist during your session

Long values are truncated with ellipsis:

- Hover over truncated text to see full value in tooltip
- Resize column to see more content
- Copy the full value with cell selection

### Row Operations

Select rows for bulk operations:

- Click checkbox at start of row to select
- Shift+click to select range of rows
- Ctrl/Cmd+click to select multiple non-consecutive rows
- Click header checkbox to select all visible rows

Selected rows can be:
- Copied together
- Exported as a group
- Used for analysis

View full record details:

- Right-click a row to open context menu
- Select "View Details" or click the row
- Full record displayed in readable format
- Scroll through all fields

Perfect for records with many columns that don't fit on screen.

Copy individual or multiple values:

- Click a cell to select it
- Ctrl/Cmd+C to copy the value
- Select multiple cells: drag or shift+click
- Paste into spreadsheet or text editor

Copy formats:
- Single cell: just the value
- Multiple cells: tab-separated values
- Full rows: tab-separated, newline-separated rows

## Advanced Query Result Examples

### SELECT Query Result

```
SELECT
  id,
  username,
  email,
  created_at,
  last_login
FROM users
WHERE status = 'active'
ORDER BY last_login DESC
LIMIT 20;
```

Result Display:
- Rows: 20
- Columns: id, username, email, created_at, last_login
- Showing active users sorted by recent activity
- Can sort by any column or search within results

### Count Query Result

![Count Query Result](/images/87-scratchpad-count-query-result.png)

```
SELECT
  category,
  COUNT(*) as product_count
FROM products
GROUP BY category
ORDER BY product_count DESC;
```

Result Characteristics:
- Compact display showing grouped data
- Numeric values formatted with thousand separators
- Useful for quick analytics
- Can export for further analysis

### JOIN Query Result

![Join Query Result](/images/88-scratchpad-join-query-result.png)

```
SELECT
  u.id,
  u.username,
  COUNT(o.id) as total_orders,
  SUM(o.total) as total_spent,
  MAX(o.created_at) as last_order
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.username
ORDER BY total_spent DESC;
```

Result Display:
- Combined data from multiple tables
- Aggregated values (COUNT, SUM, MAX)
- Shows relationships between tables
- Perfect for business intelligence queries

## Working with Large Result Sets

### Pagination

Navigate large result sets efficiently:

- Results display with page indicators at the bottom
- Jump to specific page using page input
- Change page size: 10, 25, 50, 100, or custom
- Previous/Next buttons for sequential browsing

Use LIMIT in your query for better performance with very large datasets

### Scrolling

For horizontal scrolling:
- Scroll bar appears when columns exceed visible width
- Scroll left/right to see additional columns
- Shift+scroll for faster horizontal navigation

For vertical scrolling:
- Scroll down to see more rows within current page
- Pagination loads additional pages as needed
- Ctrl/Cmd+End jumps to last row

### Performance Considerations

Large queries can take longer to execute:

```
-- SLOW: Returns millions of rows
SELECT * FROM large_table;

-- FAST: Returns manageable subset
SELECT * FROM large_table LIMIT 1000;

-- FAST: Filters before transfer
SELECT * FROM large_table
WHERE date > '2024-10-01'
LIMIT 10000;
```

## Data Type Formatting

WhoDB formats data based on column type:

### Common Data Types

Displayed with proper formatting:

- Integers: 1000 (no decimal places)
- Decimals: 199.99 (with decimal places)
- Large numbers: Formatted with thousand separators
- Scientific notation: For very large/small values

Example: 1234567.89 displays as 1,234,567.89

Displayed in readable format:

- Format: YYYY-MM-DD
- Timestamps: YYYY-MM-DD HH:MM:SS
- Timezone info preserved where available
- Hover for full precision

Example: 2024-10-30 or 2024-10-30 14:32:15

Displayed as true/false or yes/no:

- Boolean values shown as true/false
- Some databases show 1/0 (database-specific)
- Easily identifiable in results
- Can be sorted and filtered

Example: true or false

Special handling for missing data:

- Displayed as "NULL" or empty cell
- Visually distinct from empty strings
- Cannot be sorted mixed with normal values
- Useful for data quality checks

Example: NULL or blank cell

Special display for complex data:

- JSON: Formatted and expandable
- Arrays: Displayed with elements
- Objects: Shown as formatted JSON
- Click to expand nested structures

Example: `{"key": "value"}` or `[1, 2, 3]`

## Error Handling

### Syntax Errors

When your query has a syntax error:

![Query Error](/images/30-scratchpad-query-error.png)

Error display shows:
- Error type: "Syntax Error"
- Error message: What went wrong
- Approximate location: Where the error occurred
- Hints: Suggestions for fixing the issue

### Runtime Errors

Errors that occur during execution:

```
Error: table "users" does not exist
```

Cause: Table name misspelled or doesn't exist
Solution: Check table name in schema explorer

```
Error: column "usename" does not exist
```

Cause: Column name typo
Solution: Verify exact column name

```
Error: cannot compare text with number
```

Cause: Comparing incompatible data types
Solution: Ensure WHERE conditions match data types

```
Error: connection refused
```

Cause: Database connection lost
Solution: Check database status and reconnect

### No Results

A successful query but returning no data:

```
SELECT * FROM users WHERE id = 99999;
```

Result:
- Row count: 0
- No error message (query executed successfully)
- Empty grid
- Execution time shows query ran

This is normal—adjust your WHERE conditions to find matching data.

## Exporting Results

Save your query results in multiple formats:

### Export Options

Comma-separated values for spreadsheets and data tools

Native Excel format (.xlsx) with formatting

JSON format for APIs and applications

SQL INSERT statements for data migration

### Exporting Steps

Located above or below the results grid

Choose CSV, Excel, JSON, or SQL

- All rows: Export entire result set
- Selected: Only checked rows
- Visible: Current page only

Set delimiters, encoding, and other options specific to format

File downloads to your device

### Export Examples

```
-- Simple SELECT
SELECT id, username, email FROM users;
-- Export: CSV, Excel, or JSON
```

```
-- Aggregation
SELECT category, COUNT(*) as count
FROM products
GROUP BY category;
-- Export: CSV for Excel analysis
```

```
-- Complex JOIN
SELECT u.username, COUNT(o.id) as orders
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.username;
-- Export: Excel for presentation
```

## Result Analysis Tips

Look for trends in numerical data:
- Highest/lowest values
- Clusters or outliers
- Missing expected results
- Unexpected data gaps

Check results for data issues:
- NULL values where unexpected
- Duplicate records
- Incorrect data types
- Out-of-range values

Compare results from different queries:
- Run count query, then detail query
- Verify totals match between queries
- Check for consistency across related tables
- Validate relationships

Analyze query performance:
- Note execution times for similar queries
- Identify slow-running queries
- Consider indexes for frequently filtered columns
- Break complex queries into simpler parts

## Practical Examples

### Example: User Activity Analysis

```
-- Find active users with recent orders
SELECT
  u.id,
  u.username,
  COUNT(o.id) as order_count,
  SUM(o.total) as total_spent,
  MAX(o.created_at) as last_order_date
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.created_at > NOW() - INTERVAL '30 days'
GROUP BY u.id, u.username
ORDER BY total_spent DESC
LIMIT 50;
```

Result Actions:
- Sort by total_spent to see top customers
- Filter by order_count for frequent buyers
- Export to Excel for customer outreach
- Copy top user IDs for detailed analysis

### Example: Data Quality Check

```
-- Find incomplete user records
SELECT
  id,
  username,
  email,
  phone,
  address,
  created_at
FROM users
WHERE email IS NULL
  OR phone IS NULL
  OR address IS NULL
ORDER BY created_at DESC;
```

Result Actions:
- Count NULL values by column
- Export for data cleanup
- Identify affected users
- Plan remediation strategy

## Keyboard Shortcuts for Results

| Shortcut | Action |
|----------|--------|
| `Cmd/Ctrl + C` | Copy selected cell or rows |
| `Cmd/Ctrl + A` | Select all rows |
| `Cmd/Ctrl + Down` | Move to last row |
| `Cmd/Ctrl + Home` | Move to first row |
| `Enter` | Edit selected row |
| `Delete` | Clear selected cells |

## Next Steps

Learn how to organize and reuse your queries:

Access and manage your query history

Organize complex workflows with multiple cells

You now know how to view, analyze, and export your query results effectively

---


# query/query-history.mdx
## Query History

Manage, access, and reuse your SQL queries with WhoDB's query history

# Query History

WhoDB automatically saves every query you execute, making it easy to revisit, modify, and reuse your SQL commands. The query history panel is your repository of all previous queries, organized chronologically and searchable for quick access.

Query history reduces the time spent rewriting queries and helps maintain consistency across your workflows

## History Panel Overview

Access your query history through the dedicated history panel:

![Query History Panel](/images/33-scratchpad-query-history.png)

The history panel displays:

Queries listed from newest to oldest

First 50 characters of each query

When each query was executed

Success or error indicators

Clone, copy, and delete options

Find specific queries by content

## Accessing Query History

### Opening the History Panel

Look for the history icon in the Scratchpad interface and click to open the panel.

Press `Cmd/Ctrl + H` to toggle the history panel visibility.

Right-click in the editor and select "Show History" from the context menu.

### History Auto-Population

Every query you execute is automatically added to history:
- Successful queries appear immediately
- Failed queries (with errors) also get saved
- Query syntax and execution results stored
- Timestamp recorded for each query

Even queries with errors are saved to history—useful for debugging

## Working with Query History

### Finding Queries

#### Search by Content

Find queries by searching for keywords:

```
Search: "WHERE status"
Results: All queries containing WHERE status condition
```

Useful for finding:
- Queries with specific table names
- Queries using certain functions
- Queries with particular WHERE conditions
- Queries from specific dates

#### Browse by Time

Queries are listed chronologically:
- Most recent queries at the top
- Older queries further down
- Scroll to find queries from earlier sessions
- Date grouping shows day boundaries

### Using Cloned Queries

The clone feature lets you quickly create variations:

![Clone Button](/images/49-scratchpad-history-clone-button.png)

Look through history to find the query you want to modify

Click the clone/copy icon next to the query

A new cell is created with the cloned query

Edit the cloned query for your new purpose

Run the modified query and it's added to history as a new entry

### Clone Scenarios

Original query:
```
SELECT * FROM orders
WHERE created_at > '2024-10-01'
LIMIT 100;
```

Clone and modify for different month:
```
SELECT * FROM orders
WHERE created_at > '2024-11-01'
LIMIT 100;
```

Original query:
```
SELECT * FROM users
WHERE status = 'active'
AND created_at > '2024-01-01';
```

Clone and change status:
```
SELECT * FROM users
WHERE status = 'inactive'
AND created_at > '2024-01-01';
```

Original query:
```
SELECT category, COUNT(*) FROM products
GROUP BY category;
```

Clone and add additional aggregations:
```
SELECT category, COUNT(*), AVG(price), SUM(stock)
FROM products
GROUP BY category;
```

Original query:
```
SELECT id, name, created_at
FROM users
ORDER BY created_at DESC
LIMIT 50;
```

Clone for different table:
```
SELECT id, name, created_at
FROM customers
ORDER BY created_at DESC
LIMIT 50;
```

## History Features

### View Full Query

Click any history entry to view the complete query:

```
SELECT
  u.id,
  u.username,
  COUNT(o.id) as order_count,
  SUM(o.total) as total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.created_at > '2024-10-01'
GROUP BY u.id, u.username
ORDER BY total_spent DESC
LIMIT 100;
```

Full query displayed in:
- Separate view panel
- With line numbers for reference
- Syntax highlighting preserved
- Full content shown (no truncation)

### Execution Metadata

Each history entry shows:

When the query was executed

- Format: HH:MM AM/PM or 24-hour format
- Date: If different from today
- Time zone: Your local time zone

Success or failure indicator

- Green checkmark: Successful execution
- Red X: Query returned error
- Hover for status details

Rows returned (for SELECT queries)

- Shows: "Returned 42 rows"
- For INSERT/UPDATE/DELETE: "Affected 5 rows"
- Helpful for quick result reference

Execution time

- Shows: "Executed in 234 ms"
- Useful for performance tracking
- Compare durations for similar queries

## Managing Query History

### Copying Queries

Copy entire queries from history for reuse:

```
Click copy icon → Query copied to clipboard
Paste in editor or external tool
```

Perfect for:
- Sharing queries with teammates
- Documenting in documentation
- Running in different database tools
- Storing in external files

### Deleting History Entries

Remove specific queries from history:

- Right-click on history entry
- Select "Delete" from context menu
- Or click delete icon if available
- Confirmation required (optional)

- Settings menu → Clear History
- Deletes entire history for current session
- Cannot be undone—use with caution
- History from other sessions preserved

### Exporting History

Save your query history for documentation or sharing:

Useful for:
- Creating query libraries
- Team knowledge base
- Audit trails
- Backup before clearing
- Analysis of query patterns

## Best Practices for Query History

### Organizing Frequently Used Queries

Note which queries you use most frequently for various tasks

Update cloned queries with comments explaining their purpose:

```
-- Daily active user count
-- Used for dashboard reporting
SELECT
  DATE(created_at) as date,
  COUNT(*) as active_users
FROM user_sessions
GROUP BY DATE(created_at);
```

Prefix queries with their purpose when adding to cells:

```
Cell name: "Daily Report Query"
Cell name: "User Analysis"
Cell name: "Data Validation Check"
```

Keep most-used queries at the top of history or in separate cells

### Searching Effectively

#### Use Specific Keywords

```
Search: "WHERE status = 'active'"
Results: All queries with this condition

Search: "JOIN orders"
Results: All queries joining orders table

Search: "GROUP BY category"
Results: All aggregation queries by category
```

#### Date-Based Searching

While history is chronological:
- Look at timestamps to narrow down
- Remember when you last ran similar queries
- Scroll to approximate time period
- Then refine with keyword search

### Maintaining History Quality

Periodically remove test and temporary queries:
- Delete failed experiment queries
- Remove debugging queries
- Keep only production-ready queries
- Maintains clean history for future reference

Add context to complex queries:

```
/*
  Complex user segmentation query:
  - Active: Logged in last 7 days
  - Engaged: Made purchase last 30 days
  - At-risk: No activity in 90 days

  Used for customer lifecycle analysis
*/
SELECT
  CASE
    WHEN last_login > NOW() - INTERVAL '7 days' THEN 'active'
    WHEN last_purchase > NOW() - INTERVAL '30 days' THEN 'engaged'
    WHEN last_activity < NOW() - INTERVAL '90 days' THEN 'at-risk'
    ELSE 'inactive'
  END as status,
  COUNT(*) as user_count
FROM users
GROUP BY status;
```

Track query versions in history:
- Original version at bottom (oldest)
- Modifications cloned and updated
- Timestamp shows iteration history
- Easy to see query evolution

Export useful queries for team use:
- Share common queries with colleagues
- Maintain team query library
- Document purpose and usage
- Reduce duplicate effort

## History-Based Workflows

### Workflow: Daily Report Generation

```
Step 1: Execute weekly user activity query
Step 2: Clone from history for next week
Step 3: Modify date range in cloned query
Step 4: Run new query with updated dates
Step 5: Export results to CSV
Step 6: Share report (query saved to history)
```

### Workflow: Data Quality Monitoring

```
Step 1: Store data validation queries in history
Step 2: Each day, clone validation queries
Step 3: Run updated versions with current dates
Step 4: Compare results with previous days
Step 5: Alert if anomalies detected
```

### Workflow: Analysis Iteration

```
Step 1: Write initial analysis query
Step 2: Review results (saved to history)
Step 3: Clone and modify query based on findings
Step 4: Refine through multiple iterations
Step 5: Final version serves as reference
Step 6: Future analysis starts from final version
```

### Workflow: Team Collaboration

```
Step 1: Create standard queries in your workspace
Step 2: Share query history export with team
Step 3: Teammates import queries into their history
Step 4: Everyone uses standardized queries
Step 5: Modifications tracked in each person's history
```

## Advanced History Features

### Filtering History

Access advanced history filters:

Show only:
- Successful queries
- Failed queries
- All queries

Show queries by type:
- SELECT (read)
- INSERT/UPDATE/DELETE (write)
- All types

Filter by execution time:
- Today
- This week
- This month
- Custom date range

Filter by execution time:
- Fast queries (< 100ms)
- Medium queries (100ms - 1s)
- Slow queries (> 1s)

### History Statistics

View patterns in your query history:

- Most frequently executed queries
- Average execution time per query type
- Distribution of query types
- Peak query execution times
- Slowest running queries

Use these insights to:
- Optimize frequently used queries
- Identify performance problems
- Understand usage patterns
- Plan database optimization

## Integration with Multiple Cells

History works seamlessly with multiple cells:

![Multiple Cells Results](/images/48-scratchpad-multiple-cells-results.png)

Set up separate cells for different queries or workflows

Execute queries in each cell—history accumulates for all cells

All history appears in unified history panel

Clone queries into different cells for organization

Full history of all cells available for reference

## Keyboard Shortcuts for History

| Shortcut | Action |
|----------|--------|
| `Cmd/Ctrl + H` | Toggle history panel |
| `Cmd/Ctrl + Shift + H` | Clear all history |
| `Cmd/Ctrl + F` | Search history |
| `Enter` | Load selected query |
| `Delete` | Delete selected entry |

## Storage and Limits

### History Persistence

Query history is:
- Stored locally on your device
- Persists across sessions
- Organized by workspace/database
- Searchable and indexed
- Easily accessible

### Best Practices for Storage

Export important queries regularly:
- Backup critical queries
- Share with team
- Archive for reference
- Prevents loss if history is cleared

Each session maintains separate history:
- Login to same account: Same history
- New browser/device: New history
- Clear browser data: History may be lost
- Export before clearing browser cache

## Next Steps

Organize your queries more effectively with multiple cells:

Create and organize multiple query cells

Master working with query results

Query history is your query reference library—use it to work faster and smarter

---


# query/multiple-cells.mdx
## Multiple Cells

Organize complex workflows with WhoDB's multi-cell Scratchpad support

# Multiple Cells

WhoDB's Scratchpad supports multiple cells, allowing you to organize related queries, conduct parallel analyses, and maintain separate workflows within a single Scratchpad session. This powerful feature helps keep your work organized and enables sophisticated query orchestration.

Multiple cells let you organize your SQL experiments and workflows like a notebook with multiple pages

## Multi-Cell Concept

### What Are Cells?

Cells are independent query execution environments:

![Scratchpad Multiple Cells](/images/31-scratchpad-multiple-pages.png)

Each cell:
- Contains its own SQL query
- Executes independently
- Maintains separate results
- Can be named and organized
- Operates without affecting other cells
- Has its own execution history

### Cell Navigation

Click tabs to switch between cells

Create additional cells for new queries

Current cell highlighted or shown in status bar

Rearrange cells to match your workflow

Keyboard shortcuts to navigate cells

Right-click for cell options

## Creating and Managing Cells

### Creating a New Cell

Click the "+" button next to cell tabs to create a new cell

Press `Cmd/Ctrl + N` to create a new cell

Right-click cell tab and select "New Cell"

Click the cell menu (three dots) and select "New Cell"

New cells:
- Appear at the end of existing cells
- Start with empty editor
- Are ready for query input
- Can be moved after creation

### Cell Naming

Give cells meaningful names for organization:

![Cell Options Menu](/images/32-scratchpad-cell-options-menu.png)

Click the three dots or right-click cell tab

Choose "Rename Cell" from menu

Type descriptive name for the cell

Press Enter or click Save

### Naming Examples

```
"User Analysis"
"Daily Report"
"Data Validation"
"Performance Testing"
"Schema Exploration"
"Archive Migration"
"Quality Checks"
```

Use names that describe:
- Query purpose
- Analysis type
- Data being examined
- Workflow stage

### Duplicating Cells

Clone an entire cell including its query:

Right-click cell tab → Select "Duplicate Cell"

Press `Cmd/Ctrl + D` to duplicate current cell

Cell menu (•••) → "Duplicate"

Duplication includes:
- Current query content
- Current results (if any)
- Query history for that cell
- Results formatting settings

Perfect for:
- Variations of same query
- A/B testing different approaches
- Preserving working versions
- Creating templates

### Deleting Cells

Remove cells you no longer need:

Right-click cell tab → "Delete Cell" (or context menu)

`Cmd/Ctrl + Backspace` (with confirmation)

Before deleting:
- Query content is lost (unless saved to history)
- Results are cleared
- Cannot undo (confirmation required)
- Consider duplicating first if unsure

Cell deletion is permanent—save important queries to history first

### Reordering Cells

Organize cells to match your workflow:

Click the cell tab and drag left/right to new position

Drop zone highlighted as you drag

Cell moves to new position

Cell order maintained during session

Strategic ordering:
- Foundation queries first
- Dependent queries after
- Validation checks last
- Frequently used cells front and center

## Multi-Cell Workflows

### Workflow: Data Analysis Project

Create a multi-step analysis organized across cells:

```
-- Get table sizes and row counts
SELECT
  table_name,
  (SELECT COUNT(*) FROM information_schema.tables) as total_tables
FROM information_schema.tables
WHERE table_schema = 'public';
```

Purpose: Understand data structure

```
-- View sample records
SELECT * FROM users LIMIT 10;
```

Purpose: Examine data format

```
-- Check for NULL values and inconsistencies
SELECT
  COUNT(*) as total,
  COUNT(email) as email_count,
  COUNT(phone) as phone_count
FROM users
WHERE email IS NULL OR phone IS NULL;
```

Purpose: Identify data quality issues

```
-- Perform actual analysis
SELECT
  status,
  COUNT(*) as count,
  AVG(account_age) as avg_age
FROM users
GROUP BY status;
```

Purpose: Generate insights

```
-- Prepare final dataset for export
SELECT
  id,
  username,
  email,
  created_at
FROM users
WHERE status = 'active'
ORDER BY created_at DESC;
```

Purpose: Ready data for download

### Workflow: Performance Testing

Compare different query approaches:

```
Cell 1: Original Query
├─ Time: 2500ms
├─ Rows: 10000
└─ Notes: Using nested subquery

Cell 2: Optimized with JOIN
├─ Time: 850ms
├─ Rows: 10000
└─ Notes: Replaced subquery with JOIN

Cell 3: Indexed Query
├─ Time: 120ms
├─ Rows: 10000
└─ Notes: Added index on filter column

Result: 95% performance improvement!
```

Execute each cell and compare execution times to find the fastest approach.

### Workflow: Data Migration

Track multiple stages of data migration:

```
-- Verify source data before migration
SELECT COUNT(*) as source_count FROM source.users;
SELECT COUNT(DISTINCT id) as unique_ids FROM source.users;
```

```
-- Check data transformation logic
SELECT id, old_status,
  CASE
    WHEN old_status = 'A' THEN 'active'
    WHEN old_status = 'I' THEN 'inactive'
  END as new_status
FROM source.users LIMIT 100;
```

```
-- Verify all data migrated correctly
SELECT COUNT(*) as destination_count FROM destination.users;
SELECT COUNT(DISTINCT id) as unique_ids FROM destination.users;
```

```
-- Find any discrepancies
SELECT source_id FROM source.users
WHERE source_id NOT IN (SELECT id FROM destination.users);
```

## Cell Organization Patterns

### Linear Workflow

Sequential cells for step-by-step processes:

```
Cell 1: Extract
  ↓
Cell 2: Transform
  ↓
Cell 3: Validate
  ↓
Cell 4: Load
  ↓
Cell 5: Verify
```

Best for:
- ETL processes
- Data pipelines
- Analysis sequences
- Testing workflows

### Parallel Analysis

Independent cells for different analyses:

```
Analysis A          Analysis B          Analysis C
├─ Cell 1          ├─ Cell 3          ├─ Cell 5
└─ Cell 2          └─ Cell 4          └─ Cell 6
```

Best for:
- A/B testing queries
- Multiple perspectives on data
- Concurrent analysis
- Independent investigations

### Hierarchical Organization

Related cells grouped conceptually:

```
Foundation Queries
├─ Cell 1: Schema exploration
├─ Cell 2: Data overview
└─ Cell 3: Sample data

Analysis Queries
├─ Cell 4: User analysis
├─ Cell 5: Order analysis
└─ Cell 6: Product analysis

Quality Checks
├─ Cell 7: NULL checks
├─ Cell 8: Duplicate detection
└─ Cell 9: Constraint validation
```

Best for:
- Complex analysis projects
- Multi-faceted investigations
- Organized documentation
- Team collaboration

## Advanced Cell Features

### Cell Execution Status

Each cell shows execution information:

![Multiple Cells Results](/images/48-scratchpad-multiple-cells-results.png)

Status indicators:
- **No status**: Not yet executed
- **Green check**: Last execution successful
- **Red X**: Last execution had error
- **Clock icon**: Currently executing
- **Time display**: Execution duration

### Switching Between Cells

Navigate efficiently:

Click cell tab to switch instantly

- `Cmd/Ctrl + 1`: Switch to first cell
- `Cmd/Ctrl + 2`: Switch to second cell
- `Cmd/Ctrl + Left/Right Arrow`: Previous/next cell

Press Tab multiple times to cycle through tabs

### Cell-Specific Results

Each cell maintains its own results:

- Switching cells shows that cell's results
- Results persist until new query executed
- Scroll through large result sets independently
- Export results from specific cell only

### Shared History

Query history includes all cells:

```
History:
├─ Cell 1 Query: SELECT * FROM users
├─ Cell 1 Query: UPDATE users SET...
├─ Cell 2 Query: SELECT * FROM orders
├─ Cell 1 Query: SELECT COUNT(*)...
└─ Cell 3 Query: DELETE FROM...
```

Benefits:
- Complete execution history across cells
- Clone queries from any cell to any cell
- Reference previous versions
- Track all work

## Copy and Paste Between Cells

### Transfer Queries

Copy query from one cell to another:

In source cell, select the query text

`Cmd/Ctrl + C` to copy

Navigate to destination cell

`Cmd/Ctrl + V` to paste

Edit as needed for new cell

### Copy Results

Copy results from one cell's query to use in another:

```
-- Cell 1: Find specific user IDs
SELECT id FROM users WHERE status = 'active' LIMIT 10;

-- Copy the IDs, then in Cell 2:
SELECT * FROM orders
WHERE user_id IN (1, 2, 3, 4, 5);  -- IDs from Cell 1
```

## Cell Limitations and Considerations

### Performance

Multiple cells don't directly impact performance, but:

- Each cell executes independently
- Large result sets consume memory
- Close unused cells if experiencing slowness
- Results cached only for current cells

### Best Practices

Use clear names to remember what each cell does:

Good: "Active Users Analysis"
Poor: "Query 1"

Delete cells when no longer needed:
- Reduces clutter
- Keeps workspace organized
- Improves focus
- Easier navigation

Add comments within queries:

```
-- Find users who:
-- 1. Have made purchases > $100
-- 2. Didn't purchase in last 30 days
-- 3. Account created > 1 year ago
SELECT ...
```

Export complex queries to external storage:
- Backup against accidental deletion
- Share with team
- Document for future reference
- Version control if needed

## Multi-Cell Examples

### Example 1: A/B Testing Query Performance

```
Cell 1: Original Query (Nested Subquery)
SELECT user_id, purchase_count
FROM users
WHERE user_id IN (
  SELECT user_id FROM orders
  WHERE total > 100
);
-- Result: 1450ms

Cell 2: Optimized Query (JOIN)
SELECT u.id, COUNT(o.id) as purchase_count
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.total > 100
GROUP BY u.id;
-- Result: 320ms

Result: Use Cell 2 approach—75% faster!
```

### Example 2: Dataset Preparation

```
Cell 1: Explore Source Table
SELECT * FROM raw_data LIMIT 100;

Cell 2: Data Quality Check
SELECT COUNT(*) as missing_values
FROM raw_data
WHERE column1 IS NULL OR column2 IS NULL;

Cell 3: Transform Data
SELECT
  id,
  UPPER(name) as name,
  DATE(created_at) as date
FROM raw_data;

Cell 4: Load Clean Data
INSERT INTO clean_data
SELECT * FROM ... (previous query);

Cell 5: Verify Load
SELECT COUNT(*) FROM clean_data;
```

### Example 3: Multi-Database Comparison

```
Cell 1: PostgreSQL Query
SELECT version(), COUNT(*) FROM users;

Cell 2: MySQL Query (if available)
SELECT @@version, COUNT(*) FROM users;

Cell 3: SQLite Query (if available)
SELECT sqlite_version(), COUNT(*) FROM users;

Compare results across database systems
```

## Keyboard Shortcuts for Cell Management

| Shortcut | Action |
|----------|--------|
| `Cmd/Ctrl + N` | New cell |
| `Cmd/Ctrl + D` | Duplicate cell |
| `Cmd/Ctrl + Shift + Delete` | Delete cell |
| `Cmd/Ctrl + Right Arrow` | Next cell |
| `Cmd/Ctrl + Left Arrow` | Previous cell |
| `Cmd/Ctrl + 1-9` | Jump to cell number |

## Integration with Other Features

### Multi-Cell with History

Each cell maintains history:
- Clone queries from any cell's history
- Combine approaches from different cells
- Reference previous attempts

### Multi-Cell with Export

Export results from specific cells:
- Each cell's results exportable separately
- Export all cells' results at once
- Export to different formats per cell

### Multi-Cell with Auto-Completion

Auto-completion works within each cell:
- Schema suggestions in every cell
- Column names current for each database
- Function names updated per cell

## Next Steps

Master advanced query techniques and visualization:

Learn advanced SQL patterns for your queries

Analyze and export results from multiple cells

Visualize your database schema relationships

Multiple cells transform your Scratchpad into a powerful workspace for complex analysis and testing

---


# visualization/graph-view.mdx
## Graph View

Interactive visualization of your database schema with table relationships and navigation controls

# Graph View

The Graph view provides an interactive, visual representation of your entire database schema. Instead of navigating through tables and relationships in text form, you can see your entire data structure at a glance, including all table connections, foreign keys, and relationships.

The Graph view is particularly useful for understanding complex schemas, identifying data dependencies, and designing new features

## Overview

The Graph view displays your database as an interactive diagram with:
- **Table nodes**: Each table appears as a node showing key information
- **Relationship lines**: Connections between tables based on foreign keys
- **Cardinality indicators**: Visual markers showing one-to-one, one-to-many, and many-to-many relationships
- **Navigation controls**: Tools for zooming, panning, and fitting the view

## Accessing Graph View

To view your database schema as a graph:

Select a database and schema from the sidebar to load its tables

Look for the "Graph" tab in the main content area and click it

The schema will load and render automatically. Complex schemas may take a few moments to layout

## Graph Interface

![Graph View - Schema Topology](/images/24-graph-view-schema-topology.png)

The graph displays your complete schema with all table nodes and their relationships. The layout algorithm automatically positions tables to minimize line crossings and make relationships clear.

## Navigation Controls

WhoDB provides several tools to navigate and interact with the graph:

![Graph View - With Layout Controls](/images/25-graph-view-with-controls.png)

### Zoom Controls

Click the plus button or scroll up to get a closer view of specific areas

Click the minus button or scroll down to see more of the schema at once

Click the fit button to automatically zoom and center all nodes in the viewport

Return to the default zoom level and position

### Pan and Drag

- **Click and drag** the canvas to pan around the graph
- The cursor changes to a grab hand when hovering over empty space
- Useful for exploring large schemas or focusing on specific table clusters

### Keyboard Shortcuts

| Shortcut | Action |
|----------|--------|
| `+` / `=` | Zoom in |
| `-` | Zoom out |
| `0` | Fit all nodes in view |
| `Space + Drag` | Pan the canvas |
| `Arrow Keys` | Navigate between nodes |

## Viewing Node Details

Each table node displays important information and allows interaction:

![Graph View - Node Details](/images/26-graph-view-node-details.png)

### Node Information

Each node shows:
- **Table name**: The name of the database table
- **Key columns**: Important columns like primary keys highlighted
- **Column list**: A preview of the table's columns (on hover or click)
- **Foreign key indicators**: Visual markers for columns that reference other tables

### Clicking Nodes

Click on any table node to:

See the full column list with data types and constraints

Use the quick access button to view the table's data

Check indexes, constraints, and other metadata for the table

### Quick Data Access

![Graph Node - Data Button Hover](/images/50-graph-node-data-button-hover.png)

Hover over a node to reveal the data button, allowing you to jump directly to the table's data view without leaving the graph.

This is great for quick navigation when exploring related tables

## Understanding Relationships

The lines connecting nodes represent foreign key relationships. The visual style and direction indicate the type of relationship:

### Relationship Indicators

- **Solid lines**: Direct foreign key relationships
- **Line direction**: Shows which table references which (arrow points to referenced table)
- **Line color**: Different colors may be used for different relationship types
- **Multiple lines**: Indicate multiple foreign keys between the same tables

## Zooming for Detail

WhoDB automatically adjusts the graph layout based on zoom level for optimal viewing:

### Zoomed In View

![Graph - Zoomed In View](/images/95-graph-zoomed-in-view.png)

When zoomed in, you see detailed information about specific tables:
- Full column names and data types
- Constraint details
- Complete relationship labels

Use this view when examining specific table structures or relationships in detail.

### Zoomed Out View

![Graph - Zoomed Out View](/images/96-graph-zoomed-out-view.png)

When zoomed out, you see the overall structure and patterns:
- All tables in the schema
- Overall relationship topology
- Table clusters and groupings
- High-level schema organization

Use this view to understand the big picture or plan major structural changes.

## Fit to View

The fit view control ensures your entire schema is visible:

![Graph - Fit View Control](/images/97-graph-fit-view-control.png)

Click the fit icon in the control panel

WhoDB automatically zooms and pans to show all nodes

Use zoom and pan controls for detailed exploration

## Workflow: Exploring Complex Schemas

When working with complex databases, use this workflow:

Click fit view to see the entire schema and understand its overall structure

Look for clusters of related tables, central tables with many connections, and independent groups

Zoom in on specific areas of the schema you want to understand better

Click nodes to see the full details of table structures and connections

Use quick data access to examine sample data for specific tables

Fit view again to see how changes or findings fit in the larger context

## Common Patterns

### Hub-and-Spoke Pattern

Large central table with many incoming and outgoing relationships. Common in:
- User management systems (users table with many related tables)
- Event tracking systems (events table as central hub)
- Master data tables in data warehouses

### Hierarchical Pattern

Tables arranged in clear parent-child hierarchies. Common in:
- Organizational structures (departments → teams → members)
- Product hierarchies (categories → subcategories → products)
- File systems or tree structures

### Many-to-Many Pattern

Two main tables connected through a junction table. Common in:
- User-role assignments
- Product-category assignments
- Article-tag systems

### Independent Clusters

Groups of unrelated tables. Common in:
- Multi-tenant systems (separate clusters per tenant)
- Modular applications (features as separate components)
- Legacy systems with data silos

## Performance Tips

- Start with zoomed-out view to understand clusters
- Zoom in to examine specific areas
- Use fit view frequently to maintain context
- Consider filtering views for specific schema areas

- The graph uses efficient rendering for smooth interaction
- Relationship lines are simplified at zoom-out levels
- Details load as you zoom in
- Pan and zoom operations are GPU-accelerated

- Graph data is cached locally after first load
- Interactions are instant and don't require server calls
- Relationship data is loaded once during graph initialization

## Exporting Graph Visualizations

While viewing the graph:
- **Screenshot**: Use your system's screenshot tool to capture the current view
- **Print to PDF**: Use browser print functionality to save as PDF
- **Document**: The graph can be included in documentation by capturing screenshots at different zoom levels

For documentation, capture multiple views: full schema (zoomed out), relationship details (normal zoom), and specific areas (zoomed in)

## Troubleshooting

For very large schemas (1000+ tables), the initial layout calculation may take several seconds. This is normal. Once loaded, interactions are instant.

Try using the fit view to reset the layout. If overlapping persists, zoom out to see the overall structure, then zoom in to areas of interest.

Use the search functionality to find the table, or zoom out to see more of the schema. You can also use Ctrl+F to search within the browser.

Click individual nodes to see which specific columns are involved in relationships. The detailed view clarifies connections that might be unclear in the full graph.

## Related Topics

Understand how table nodes are organized and positioned in the graph

Learn about foreign keys, cardinality, and relationship types

Navigate your schema structure in detail

View and interact with table data

---


# visualization/schema-topology.mdx
## Schema Topology

Understanding how WhoDB visualizes and organizes your database schema

# Schema Topology

Schema topology refers to the overall structure and organization of tables and relationships within your database. WhoDB visualizes this topology using an interactive graph that automatically arranges tables to show their connections and dependencies.

Understanding your schema topology is crucial for making informed database design decisions and optimizing queries

## What is Schema Topology?

Schema topology is the complete picture of:
- **Tables**: All database objects (tables, views, materialized views)
- **Relationships**: Connections between tables via foreign keys
- **Cardinality**: The nature of relationships (one-to-one, one-to-many, many-to-many)
- **Organization**: How tables are logically grouped and dependent on each other

## The Graph Visualization

![Graph View - Schema Topology](/images/24-graph-view-schema-topology.png)

WhoDB displays your schema topology as a graph where:
- Each **node** represents a table
- Each **edge** (connecting line) represents a foreign key relationship
- **Node positioning** is algorithmically optimized to minimize visual complexity
- **Line directions** show the direction of the relationship

## Table Nodes

Each table node in the graph displays:

### Node Content

The name of the database table displayed prominently at the node's center

An icon indicating the table type (regular table, view, materialized view)

Primary key columns highlighted with a special marker

Columns that reference other tables shown with connection indicators

### Node States

**Idle State**
- Table name visible
- Key columns marked
- Ready to interact

**Hover State**
- Highlights connected tables
- Shows quick action buttons
- Reveals data access option

**Selected State**
- Full details displayed
- All columns and constraints shown
- Relationship details expanded

## Understanding Node Positioning

The graph uses a force-directed layout algorithm that:

1. **Minimizes overlaps**: Positions nodes to avoid covering each other
2. **Reduces line crossings**: Arranges tables to minimize complex crossing patterns
3. **Groups related tables**: Tables with many mutual relationships cluster together
4. **Maintains hierarchy**: Parent tables often appear above or beside their children

### Layout Evolution

As you interact with the graph:
- **Initial load**: All nodes position based on the complete schema
- **Zoom in**: Focused view emphasizes nearby relationships
- **Zoom out**: High-level view shows overall patterns and clusters
- **Pan**: Navigate to different areas while maintaining relative positions

## Relationship Topology Patterns

Different schemas exhibit different topology patterns:

### Hub-and-Spoke

A central table with many outgoing relationships:
- Common in: User management, event tracking, master data systems
- Characteristics: One large node with many connecting lines
- Example: `users` table connecting to `orders`, `reviews`, `comments`, etc.

### Hierarchical

Clear parent-child chains:
- Common in: Organizational systems, taxonomies, file hierarchies
- Characteristics: Linear chains of tables
- Example: `categories` → `subcategories` → `products` → `variants`

### Many-to-Many Network

Multiple tables interconnected through junction tables:
- Common in: Role-based systems, tagging systems, flexible associations
- Characteristics: Complex interconnected relationships
- Example: `users` ↔ `roles` (via `user_roles`), `articles` ↔ `tags` (via `article_tags`)

### Modular/Clustered

Separate groups of related tables with few cross-cluster relationships:
- Common in: Multi-tenant systems, microservice databases, feature-based organization
- Characteristics: Distinct clusters with minimal inter-cluster connections
- Example: `billing_*` tables, `shipping_*` tables, `notification_*` tables

### Linear Chain

Tables arranged in a sequence:
- Common in: Event pipelines, workflow systems, data transformation pipelines
- Characteristics: Sequential dependencies
- Example: `raw_data` → `processed_data` → `aggregated_data` → `reports`

## Analyzing Topology

### Key Metrics

How many relationships each table has. Central tables may be performance critical.

The maximum distance from any table to any other. Affects query complexity.

The ratio of actual relationships to possible relationships. Indicates design complexity.

Circular relationships that might indicate design issues or complex queries needed.

### Design Quality Indicators

**Good Topology:**
- Clear grouping of related tables
- Minimal deep chains (3-4 levels typical)
- Reasonable number of relationships per table
- Mostly unidirectional relationships

**Problematic Topology:**
- Excessive hub-and-spoke with one table having 20+ relationships
- Very deep hierarchies (8+ levels)
- Circular relationships that complicate queries
- Unused tables with no relationships

## Exploring Your Schema Topology

### Workflow: Understanding Your Schema

Start with a zoomed-out view using the fit view control. This shows the overall structure and any obvious patterns.

Look for groups of closely related tables. These usually represent specific business areas or features.

Locate tables with many connections. These are often critical to your application's functionality.

Identify any parent-child hierarchies and understand their depth.

Look for unusual patterns that might indicate design problems (excessive centralization, deep nesting, unexpected isolation).

Zoom in on areas of interest to see detailed column information and constraint details.

### Workflow: Schema Planning

When planning new features or schema changes:

Understand the existing structure and where your new tables fit

Determine which existing tables your new tables should relate to

Ensure your relationships don't create problematic patterns or cycles

Design relationships that maintain the schema's logical organization

Return to the graph to ensure your design fits the overall structure

## Topology and Performance

Your schema's topology directly affects query performance:

### Topology Characteristics Affecting Performance

| Characteristic | Impact | Optimization |
|---|---|---|
| **Hub Tables** | Often bottleneck, may need indexes | Index foreign keys, consider denormalization |
| **Deep Hierarchies** | Many JOINs needed, complex queries | Flatten if possible, use materialized views |
| **Dense Networks** | Complex queries, many relationships | Simplify relationships, consider denormalization |
| **Isolated Tables** | No performance concerns | Normal queries |

Use the topology view to identify which tables should be indexed and which queries might benefit from optimization

## Common Topology Issues and Solutions

**Issue**: One table has 30+ relationships
**Solutions**:
- Consider if some relationships could be indirect
- Evaluate if some related tables could be consolidated
- Add appropriate indexes to the hub table
- Use denormalization carefully for read-heavy operations

**Issue**: Tables reference each other directly or indirectly
**Solutions**:
- Introduce a new table to break the cycle if possible
- Use views to simplify complex circular queries
- Document the circular relationship for query optimization

**Issue**: 6+ levels of parent-child relationships
**Solutions**:
- Flatten unnecessary levels
- Use materialized views for common query paths
- Add denormalized "level skipping" columns for direct access

**Issue**: Groups of tables with no relationships to other clusters
**Solutions**:
- This is often intentional (different features or tenants)
- Ensure this is by design, not an oversight
- Document the separation for maintenance teams

## Topology Evolution

Your schema topology will likely change over time:

### Adding Tables

New tables should fit logically into your existing topology:
- Placed near related tables in the hierarchy
- Connected to appropriate parent or sibling tables
- Not creating unnecessary new hubs or isolated clusters

### Removing Tables

When removing tables:
- Check what depends on them
- Plan migrations for foreign keys
- Verify no orphaned references remain

### Refactoring Relationships

Sometimes you need to restructure relationships:
- Use the topology view to plan changes
- Identify affected queries and applications
- Use transactions for data consistency
- Test thoroughly before production

## Exporting and Documenting Topology

### Creating Documentation

Take a screenshot of the zoomed-out view for your architecture documentation

Annotate which tables are central to important workflows

Note the design patterns you identify

Include notes about business logic and dependencies

### Sharing Topology Information

- **PDF Export**: Use browser print-to-PDF for sharing
- **Screenshots**: Document different zoom levels
- **Description**: Write narrative descriptions of your schema organization
- **Diagrams**: Create supplementary diagrams for complex areas

## Related Topics

Learn how to interact with the graph visualization

Deep dive into foreign keys and relationship types

Navigate and explore schema structure

Use topology understanding to write better queries

---


# visualization/relationships.mdx
## Relationships

Understanding foreign keys, cardinality, and relationship types in your database

# Relationships

Database relationships define how tables connect to each other through foreign keys. Understanding relationship types is essential for designing effective queries, maintaining data integrity, and working with complex schemas.

Master relationship types to understand your data model and write efficient queries

## What is a Relationship?

A relationship is a connection between two tables created when a column in one table references a column (usually the primary key) in another table. This connection is enforced by a **foreign key constraint**.

### Key Components

The table being referenced. Its primary key is the target of the relationship.

The table doing the referencing. Contains the foreign key column.

One or more columns that reference the parent table's primary key

Describes how many rows in each table can participate in the relationship

## Relationship Types

WhoDB supports and visualizes several standard relationship types:

### One-to-Many (1:N)

The most common relationship type.

![Graph - One to Many Relationship](/images/92-graph-one-to-many-relationship.png)

**Definition**
- One row in the parent table can relate to many rows in the child table
- One row in the child table relates to exactly one row in the parent table

**Example**
```
Department (1) ─── (N) Employee
- Each department can have multiple employees
- Each employee belongs to exactly one department
```

**Real-World Examples**
- One user has many orders
- One category has many products
- One department has many employees
- One post has many comments

**SQL Example**
```
-- Parent table
CREATE TABLE departments (
  id INT PRIMARY KEY,
  name VARCHAR(100)
);

-- Child table with foreign key
CREATE TABLE employees (
  id INT PRIMARY KEY,
  name VARCHAR(100),
  department_id INT NOT NULL,
  FOREIGN KEY (department_id) REFERENCES departments(id)
);
```

**Query Pattern**
```
-- Find all employees in a department
SELECT e.* FROM employees e
WHERE e.department_id = 5;

-- Find department with employee count
SELECT d.name, COUNT(e.id) as employee_count
FROM departments d
LEFT JOIN employees e ON d.id = e.department_id
GROUP BY d.id, d.name;
```

### Many-to-One (N:1)

The reverse perspective of one-to-many.

![Graph - Many to One Relationship](/images/93-graph-many-to-one-relationship.png)

**Definition**
- Many rows in one table relate to one row in another table
- This is the same as one-to-many, viewed from the child table's perspective

**Example**
```
Employee (N) ─── (1) Department
- Many employees belong to one department
- Each department has one organization
```

**When to Use This Perspective**
- When describing relationships FROM the many side
- In queries that start from the "many" table
- When emphasizing the parent dependency

**Query Pattern**
```
-- Start from the many side
SELECT e.name, d.name as department
FROM employees e
JOIN departments d ON e.department_id = d.id
WHERE e.salary > 50000;
```

### One-to-One (1:1)

Each row in one table relates to exactly one row in another.

**Definition**
- One row in table A relates to exactly one row in table B
- One row in table B relates to exactly one row in table A

**Example**
```
User (1) ─── (1) Profile
- Each user has exactly one profile
- Each profile belongs to exactly one user
```

**Real-World Examples**
- One user has one account profile
- One person has one passport
- One device has one current configuration
- One employee has one personnel file

**SQL Example**
```
CREATE TABLE users (
  id INT PRIMARY KEY,
  email VARCHAR(100) UNIQUE
);

CREATE TABLE user_profiles (
  id INT PRIMARY KEY,
  user_id INT NOT NULL UNIQUE,  -- UNIQUE makes it 1:1
  bio TEXT,
  FOREIGN KEY (user_id) REFERENCES users(id)
);
```

**Query Pattern**
```
-- One-to-one relationships are simple to join
SELECT u.email, p.bio
FROM users u
LEFT JOIN user_profiles p ON u.id = p.user_id;
```

### Many-to-Many (N:N)

Multiple rows in one table relate to multiple rows in another.

**Definition**
- Many rows in table A can relate to many rows in table B
- This relationship is mediated by a **junction table**

**Example**
```
Student (N) ─┬─ (N) Course
             │
        Enrollment (junction table)
- Each student can take many courses
- Each course can have many students
```

**Real-World Examples**
- Users have many roles; roles have many users
- Articles have many tags; tags have many articles
- Products have many categories; categories have many products
- Employees serve on many projects; projects have many employees

**SQL Example**
```
-- Parent tables
CREATE TABLE students (
  id INT PRIMARY KEY,
  name VARCHAR(100)
);

CREATE TABLE courses (
  id INT PRIMARY KEY,
  title VARCHAR(100)
);

-- Junction table creates N:N relationship
CREATE TABLE enrollments (
  student_id INT NOT NULL,
  course_id INT NOT NULL,
  grade VARCHAR(1),
  PRIMARY KEY (student_id, course_id),
  FOREIGN KEY (student_id) REFERENCES students(id),
  FOREIGN KEY (course_id) REFERENCES courses(id)
);
```

**Query Pattern**
```
-- Find all courses for a student
SELECT c.* FROM courses c
JOIN enrollments e ON c.id = e.course_id
WHERE e.student_id = 42;

-- Find all students in a course
SELECT s.* FROM students s
JOIN enrollments e ON s.id = e.student_id
WHERE e.course_id = 7;

-- Find students and their course count
SELECT s.name, COUNT(e.course_id) as course_count
FROM students s
LEFT JOIN enrollments e ON s.id = e.student_id
GROUP BY s.id, s.name;
```

### Self-Referencing (1:N within same table)

A table references itself, creating hierarchical relationships.

**Example**
```
Employee (self-reference via manager_id)
- Each employee may have one manager (also an employee)
- Each employee may manage many other employees
```

**Real-World Examples**
- Employee has a manager (who is also an employee)
- Forum post has a parent post
- File has a parent directory
- Organization has a parent organization

**SQL Example**
```
CREATE TABLE employees (
  id INT PRIMARY KEY,
  name VARCHAR(100),
  manager_id INT,
  FOREIGN KEY (manager_id) REFERENCES employees(id)
);
```

**Query Pattern**
```
-- Find employees who report to a manager
SELECT e.name FROM employees e
WHERE e.manager_id = 5;

-- Find all employees under a manager (all levels)
WITH RECURSIVE org_tree AS (
  SELECT id, name, manager_id, 1 as level
  FROM employees
  WHERE manager_id = 5
  UNION ALL
  SELECT e.id, e.name, e.manager_id, t.level + 1
  FROM employees e
  JOIN org_tree t ON e.manager_id = t.id
)
SELECT * FROM org_tree;
```

## Complex Relationships

### Multiple Foreign Keys Between Tables

Sometimes two tables have more than one relationship.

![Graph - Multiple Foreign Keys](/images/94-graph-multiple-foreign-keys.png)

**Example**
```
Booking relationships:
- Booking references Customer as booker
- Booking references Customer as alternate_contact
- Multiple different relationships between same tables
```

**SQL Example**
```
CREATE TABLE bookings (
  id INT PRIMARY KEY,
  customer_id INT NOT NULL,
  alternate_contact_id INT,
  FOREIGN KEY (customer_id) REFERENCES customers(id),
  FOREIGN KEY (alternate_contact_id) REFERENCES customers(id)
);
```

**Query Pattern**
```
-- Distinguish between relationships
SELECT
  b.id,
  c.name as primary_customer,
  alt.name as alternate_contact
FROM bookings b
JOIN customers c ON b.customer_id = c.id
LEFT JOIN customers alt ON b.alternate_contact_id = alt.id;
```

## Reading Relationship Visualizations

WhoDB shows relationships in the graph with visual indicators:

### Isolated Table Node

![Graph - Isolated Table Node](/images/91-graph-isolated-table-node.png)

A table with no incoming or outgoing foreign key relationships. While not involved in direct relationships, it may be referenced indirectly or through junction tables.

**Characteristics**
- No connection lines
- Stands alone in the schema
- May still be important for data organization

## Relationship Design Patterns

### Star Schema

One central fact table with many dimension tables.

**Pattern**
```
DimCustomer (1) ─── (N) FactSales ─── (1) DimProduct
DimDate (1) ─────────────────────────── (N) FactSales
```

**Use Case**
- Data warehouses and analytics databases
- Efficient aggregate queries
- Clear separation of facts and dimensions

### Snowflake Schema

Normalized star schema with dimension hierarchies.

**Pattern**
```
DimCategory ─ DimProductSubcategory ─ DimProduct ─ FactSales
```

**Use Case**
- Complex hierarchical dimensions
- Reduced data redundancy
- More normalized than star schema

### CQRS-Inspired Pattern

Separate read and write models with independent schemas.

**Pattern**
```
Write Model (normalized) ─→ Sync ← Read Model (denormalized)
```

**Use Case**
- High-read-volume applications
- Specific query optimization needs
- Complex domain models

## Referential Integrity

Foreign keys enforce referential integrity:

### Foreign Key Constraints

**Constraints Prevent**
- Adding a row to child table with non-existent parent
- Deleting a parent row while children exist (without cascade)
- Orphaned rows in child tables

**Common Actions**
- `RESTRICT`: Prevent deletion if children exist
- `CASCADE`: Delete children when parent is deleted
- `SET NULL`: Set child's foreign key to NULL when parent is deleted
- `SET DEFAULT`: Set child's foreign key to default when parent is deleted

### Example

```
CREATE TABLE orders (
  id INT PRIMARY KEY,
  customer_id INT NOT NULL,
  FOREIGN KEY (customer_id) REFERENCES customers(id)
    ON DELETE RESTRICT
    ON UPDATE CASCADE
);
```

## Querying Multiple Relationships

### INNER JOIN

Returns rows where relationship exists in both tables.

```
SELECT o.id, c.name, o.total
FROM orders o
INNER JOIN customers c ON o.customer_id = c.id;
```

### LEFT JOIN

Returns all rows from left table, matching rows from right.

```
SELECT c.name, COUNT(o.id) as order_count
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id, c.name;
```

### Multiple JOINs

```
SELECT o.id, c.name, p.title, oi.quantity
FROM orders o
JOIN customers c ON o.customer_id = c.id
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id;
```

## Relationships and Performance

### Indexing Relationships

Always index foreign key columns for better JOIN performance

The referenced primary key is usually indexed automatically

Index both foreign keys for efficient many-to-many queries

Consider index order based on query patterns

### Query Performance Tips

Reduce the number of tables involved in a single query. Deep joins (5+ tables) can be slow.

INNER JOIN is typically faster than LEFT JOIN due to simpler execution

For many-to-many queries, ensure junction table has proper indexes

In read-heavy scenarios, consider storing denormalized copies of frequently joined data

## Relationship Best Practices

Make relationships intuitive and match real-world entities. A developer should understand the schema quickly.

Foreign key column names should clearly indicate what they reference: `customer_id` instead of `cust_id`.

Add comments or documentation for non-obvious relationship purposes.

Avoid more than 4-5 levels of joins in typical queries. If necessary, use denormalization or materialized views.

Use CASCADE delete judiciously; consider the business impact of mass deletions.

Verify that your foreign key constraints properly prevent data inconsistencies.

## Common Issues and Solutions

**Issue**: Table A references B, B references C, C references A

**Solution**:
- Introduce a bridge table to break the cycle
- Use application-level logic instead of database-level relationships
- Document the circular dependency and its handling

**Issue**: Child records exist without matching parent

**Solution**:
- Use referential integrity constraints
- Run cleanup queries to remove orphans
- Audit data for missing references

**Issue**: Loading parent, then one query per child

**Solution**:
- Use JOINs instead of separate queries
- Use batching or bulk loading
- Consider query caching

**Issue**: Queries slow with many JOINs

**Solution**:
- Add appropriate indexes
- Denormalize frequently joined data
- Use materialized views
- Break complex query into multiple steps

## Related Topics

Visualize relationships in your schema

Understand overall schema organization

Maintain data consistency with relationships

Master complex queries with multiple relationships

---


# advanced/mock-data.mdx
## Mock Data Generation

Generate realistic test data for development and testing

# Mock Data Generation

WhoDB provides a powerful mock data generation feature that allows you to populate your tables with realistic test data. This is invaluable for development, testing, and demonstration purposes.

Mock data generation helps you quickly create test datasets without manual data entry

## Overview

The mock data generator creates realistic data based on your table schema, supporting various data types and constraints. You can generate anywhere from a single row to hundreds of rows of test data.

![Mock Data Dialog](/images/22-data-view-mock-data-dialog.png)

## Accessing Mock Data Generation

1. Navigate to any table in WhoDB
2. Click the **Add Data** menu or action button
3. Select **Generate Mock Data** from the options

The mock data generation dialog will open with several configuration options.

## Configuration Options

### Data Handling Modes

The first critical choice is how to handle existing data:

![Mock Data Handling Options](/images/72-mock-data-handling-options.png)

### Append Mode

**Safe Operation** - Adds new mock data to existing rows

![Append Mode](/images/71-mock-data-append-mode.png)

Use this mode when:
- You want to preserve existing data
- You're adding test data to a partially populated table
- You're conducting safe testing that doesn't require a fresh state

All generated rows are inserted as new records without modifying existing data.

Recommended for development environments where data preservation is important

---

### Overwrite Mode

**Destructive Operation** - Replaces all existing data with mock data

![Overwrite Confirmation](/images/47-mock-data-overwrite-confirmation.png)

Use this mode when:
- You want a completely fresh dataset
- You're resetting test data
- You're preparing demonstration data

All existing rows in the table are deleted and replaced with newly generated mock data.

This operation is destructive and cannot be undone. Ensure you have backups before using overwrite mode.

### Row Count

Specify how many rows of mock data to generate. WhoDB provides several preset options:

**Minimum - 1 Row**

![Row Count Minimum](/images/73-mock-data-row-count-minimum.png)

Perfect for quick testing or creating a single example record.

**Medium - 100 Rows**

![Row Count Medium](/images/74-mock-data-row-count-medium.png)

Ideal for standard testing scenarios with a reasonable dataset size.

**Maximum - 200 Rows**

![Row Count Maximum](/images/75-mock-data-row-count-maximum.png)

For comprehensive testing with large datasets. Note that row counts may be clamped based on your database configuration.

You can manually enter custom row counts between these presets by editing the field directly

## Data Type Handling

WhoDB intelligently generates appropriate data for each column type:

### Text Columns
- **String**: Lorem ipsum-style placeholder text
- **Email**: Realistic email addresses (user@example.com format)
- **URL**: Valid URLs with example domains
- **Names**: Common first and last name combinations

### Numeric Columns
- **Integer**: Random whole numbers within typical ranges
- **Decimal/Float**: Random decimal values
- **Percentage**: Values between 0-100
- **Age**: Values between 18-80
- **Price**: Currency values with appropriate decimal places

### Date/Time Columns
- **Date**: Random dates within the past 5 years
- **DateTime**: Timestamps with realistic times
- **Time**: Valid time values
- **Year**: Recent calendar years

### Boolean/Enum Columns
- **Boolean**: Random true/false values
- **Enum**: Random selections from defined options

### Relationship Columns
- **Foreign Keys**: Valid references to existing records
- **Relationships**: Maintains referential integrity

## Using Mock Data Effectively

### Best Practices

Always test mock data generation on development or staging databases first. Verify the generated data meets your needs before using in production contexts.

Production databases should rarely receive mock data generation, especially in overwrite mode

Before using overwrite mode, always export or backup your existing data:

1. Use **Export All** to save current data to CSV/Excel
2. Store the backup file securely
3. Confirm the backup is successful before proceeding with overwrite

```
# Recommended: Maintain a backup
mysqldump database_name table_name > backup.sql
```

Consider what your tests actually need:

- **Unit Tests**: 1-10 rows is usually sufficient
- **Integration Tests**: 50-100 rows provides good coverage
- **Performance Tests**: 200+ rows to test at scale
- **UI/Demo**: 20-50 rows for visual appeal without being overwhelming

After generation, verify that:

- Foreign key relationships are valid
- No constraint violations occurred
- Data ranges are appropriate for your use case
- Null/Not Null constraints are respected
- Unique constraints have no duplicates

Keep notes about mock datasets:

- Record the row count and timestamp
- Document which mode was used (append/overwrite)
- Note any special configuration
- Track which tests use which datasets

### Common Workflows

### Fresh Start Testing

Export all existing data to a safe location

Navigate to your test table and open the dialog

Choose "Overwrite" to replace all existing data

Choose an appropriate count (50-100 rows for most tests)

Click "Generate Mock Data" to create the test dataset

Check that the data was generated correctly

Execute your tests against the fresh dataset

### Incremental Testing

Navigate to the table that needs more test data

Choose "Append" to add data without removing existing rows

Choose the number of additional rows to add (10-20 is typical)

Click "Generate Mock Data" to add to the table

Scroll through to see the newly added rows

The additional data is now available for testing

### Demo Preparation

Set up a separate database for demonstrations

Populate each table with 20-30 rows for visual appeal

Add data gradually to show realistic scenarios

Use WHERE conditions to show relevant data subsets

Save demo queries and results for consistency

## Security and Safety Considerations

### Never Generate Mock Production Data

**Critical**: Do not generate mock data using production database credentials or connection settings

Production databases should have strict access controls. Generating mock data in production can:
- Corrupt existing critical data in overwrite mode
- Violate data compliance requirements (GDPR, HIPAA, etc.)
- Create unexpected downtime if performance is affected
- Introduce invalid or inconsistent data

### Separate Environments

Maintain clear separation:

| Environment | Mock Data | Purpose |
|------------|-----------|---------|
| **Local Dev** | Yes | Individual development |
| **Development** | Yes | Team development/testing |
| **Staging** | Limited | Pre-production testing |
| **Production** | No | Live data only |

### Data Privacy

When generating mock data:

- No sensitive personal information is included
- Generated emails and names are fictional
- No real phone numbers or addresses are used
- All data is placeholder/test quality

However, if your table contains sensitive fields:

Configure field-level encryption or masking after generation to ensure privacy compliance

### Audit Trail

WhoDB logs mock data generation events for audit purposes:

- **Timestamp**: When data was generated
- **User**: Who initiated generation
- **Mode**: Append or overwrite
- **Row Count**: Number of rows generated
- **Table**: Which table was affected

Review audit logs regularly in sensitive environments.

## Troubleshooting

### Generation Fails with Constraint Errors

**Cause**: Foreign key constraints cannot be satisfied

**Solution**:
1. Ensure parent tables have sufficient data
2. Generate parent table data first
3. Then generate child table data
4. Check referential integrity settings

### Generated Data Looks Unrealistic

**Cause**: Schema metadata may be incomplete

**Solution**:
1. Verify table structure in Explore view
2. Check column types and constraints
3. Update any custom field naming patterns
4. Try with a smaller row count first

### Overwrite Mode Deleted Data Unexpectedly

**Cause**: Confirmation was accepted on the wrong table

**Solution**:
1. Check if you have a backup
2. Review audit logs for timestamp
3. Restore from backup if available
4. Use version control to recover data

Always maintain backups before using overwrite mode to prevent data loss

### Performance Issues After Generation

**Cause**: Large dataset may impact performance

**Solution**:
1. Add appropriate indexes to frequently queried columns
2. Archive old test data periodically
3. Use smaller row counts (50 instead of 200)
4. Configure database connection pooling

## Related Features

Learn how to import your own data from CSV or other formats

Master exporting your data in various formats

Work with multiple rows at once

Write custom SQL to generate specific test data

## Summary

Mock data generation in WhoDB provides:

- Quick population of test tables with realistic data
- Flexible row count configuration
- Safe append mode and powerful overwrite mode
- Intelligent data type handling
- Comprehensive audit trail for compliance

Use mock data generation to streamline your development workflow, accelerate testing, and prepare demonstration environments—while maintaining security and data integrity in production systems.

You're now ready to generate effective test data for your development and testing needs!

---


# advanced/export-options.mdx
## Export Options

Export your data in multiple formats with advanced configuration options

# Export Options

WhoDB provides comprehensive export capabilities that allow you to extract your data in multiple formats with fine-grained control over options and content selection.

Export your data exactly the way you need it for downstream analysis, backups, or sharing

## Overview

The export functionality lets you choose:

- **What to export**: All data, filtered rows, or selected rows
- **Format**: CSV, Excel, JSON, or SQL
- **Configuration**: Delimiters, encoding, headers, and more

![Export Dialog](/images/20-data-view-export-dialog.png)

## Accessing the Export Function

1. Navigate to any table in WhoDB
2. Click the **Export** button in the action bar
3. The export configuration dialog opens

## Export Modes

### Export All

Exports every row in the current table, regardless of filters or selections.

**When to use**:
- Creating complete data backups
- Migrating entire tables
- Getting the full dataset for analysis
- Compliance/audit requirements

**Result**: All rows with all columns are included in the export.

### Export Filtered

Exports only rows that match your current WHERE conditions.

**When to use**:
- Extracting specific subsets of data
- Working with particular time periods or categories
- Exporting results of complex queries
- Creating focused datasets

**How it works**:
1. Apply WHERE conditions to filter your table
2. Click Export
3. Only filtered rows are included

This is perfect for exporting quarterly reports or specific customer segments

### Export Selected

Exports only the rows you've manually selected.

![Export Selected Rows Mode](/images/46-export-selected-rows-dialog.png)

**When to use**:
- Exporting specific records you've chosen
- Creating focused samples for testing
- Sharing specific data points with colleagues
- Excluding unwanted rows

**How it works**:
1. Select rows by clicking their checkboxes
2. Click Export
3. Only selected rows are included in the export

Selection and export is a powerful combination for granular data extraction

## Export Formats

### CSV (Comma-Separated Values)

![CSV Format Option](/images/65-export-format-csv-option.png)

The most universal format, compatible with Excel, Google Sheets, and data analysis tools.

**Advantages**:
- Universal compatibility
- Human-readable and editable
- Compact file size
- Easy to version control
- Scriptable processing

**Configuration**:
- **Delimiter**: Choose separator (comma, semicolon, pipe)
- **Encoding**: UTF-8 (recommended), Latin-1, or other encodings
- **Headers**: Include or exclude column names as first row

**Best for**:
- Spreadsheet analysis in Excel/Sheets
- Data import into other tools
- Web application data feeds
- Cross-platform data sharing

```
id,username,email,status
1,alice,alice@example.com,active
2,bob,bob@example.com,inactive
3,charlie,charlie@example.com,active
```

### Excel (.xlsx)

![Excel Format Option](/images/66-export-format-excel-option.png)

Native Excel format with advanced formatting and features.

**Advantages**:
- Familiar Excel interface
- Multiple sheets support
- Built-in formatting options
- Formula compatibility
- Widely used in business

**Configuration**:
- **Sheet Name**: Custom name for the worksheet
- **Include Headers**: Column names on first row
- **Column Width**: Auto-fit or fixed width

**Best for**:
- Business reports and presentations
- Sharing with non-technical users
- Financial data analysis
- Creating polished deliverables

### JSON

Structured format ideal for APIs and applications.

**Advantages**:
- Structured data format
- Nested object support
- API-friendly
- Programmatically processable
- Self-documenting

**Configuration**:
- **Pretty Print**: Human-readable formatting with indentation
- **Array Format**: Single array or line-delimited JSON

**Best for**:
- API data transfer
- Web application consumption
- Programmatic data processing
- NoSQL database import

```
[
  {
    "id": 1,
    "username": "alice",
    "email": "alice@example.com",
    "status": "active"
  },
  {
    "id": 2,
    "username": "bob",
    "email": "bob@example.com",
    "status": "inactive"
  }
]
```

### SQL

SQL INSERT statements for database backup and transfer.

**Advantages**:
- Database-agnostic backup format
- Portable to other systems
- Readable and auditable
- Transaction-safe with proper formatting
- Ideal for version control

**Configuration**:
- **Include DROP**: Optional DROP TABLE statement
- **Include CREATE**: Optional CREATE TABLE statement
- **Batch Size**: Rows per INSERT statement

**Best for**:
- Database backups
- Cross-database migration
- Archival storage
- Version control (with diffs)

```
INSERT INTO users (id, username, email, status)
VALUES (1, 'alice', 'alice@example.com', 'active');
INSERT INTO users (id, username, email, status)
VALUES (2, 'bob', 'bob@example.com', 'inactive');
```

## CSV Delimiter Options

When exporting to CSV format, choose your delimiter:

### Comma (,)

![Comma Delimiter](/images/67-export-delimiter-comma.png)

**Most Common** - Standard CSV format recognized by all tools.

```
id,name,email
1,Alice,alice@example.com
2,Bob,bob@example.com
```

**Use when**: Target system expects standard CSV format.

### Semicolon (;)

![Semicolon Delimiter](/images/68-export-delimiter-semicolon.png)

**European Standard** - Common in countries that use comma as decimal separator.

```
id;name;email
1;Alice;alice@example.com
2;Bob;bob@example.com
```

**Use when**:
- Working with European software/users
- Regional requirements specify semicolon
- Target system configured for semicolon

### Pipe (|)

![Pipe Delimiter](/images/69-export-delimiter-pipe.png)

**Specialized Format** - Useful when data contains commas or semicolons.

```
id|name|email
1|Alice|alice@example.com
2|Bob|bob@example.com
```

**Use when**:
- Data contains commas or semicolons
- Legacy system requires pipe delimiter
- Working with fixed-width or complex data

## Complete Export Workflow

Open the table you want to export data from

Use WHERE conditions to select specific data if exporting filtered rows

![Where Conditions](/images/16-data-view-where-conditions-popover.png)

Click checkboxes to select specific rows if using export selected mode

![Row Selection](/images/44-table-row-selection-single.png)

Click the Export button to open the configuration dialog

![Export Dialog](/images/21-data-view-export-dialog.png)

Select "All", "Filtered", or "Selected"

Choose your desired output format (CSV, Excel, JSON, SQL)

Set delimiter, encoding, sheet name, or other format-specific options

Click "Export" to download the file to your computer

## Advanced Scenarios

### Exporting for Data Analysis

Use WHERE conditions to limit to current year/quarter

Select Excel format for familiar interface

Ensure column names are included for clarity

Save to your computer for analysis in Excel

Excel file is editable—add your own analysis formulas

**Result**: Excel file ready for pivot tables, charts, and formulas.

Choose delimiter that doesn't conflict with your data

Export complete dataset for processing

Save to accessible location

Use Python, JavaScript, or other tools to manipulate data

```
import csv

with open('export.csv', 'r') as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(f"User: {row['username']}, Status: {row['status']}")
```

**Result**: Data ready for integration into your workflows.

Choose JSON for structured data

Use WHERE conditions to get specific records

Make JSON human-readable for verification

Save to project directory

Load JSON data into your application or API

```
// In your Node.js application
const userData = require('./export.json');
userData.forEach(user => {
  console.log(`${user.username}: ${user.email}`);
});
```

**Result**: JSON data ready for application consumption.

### Creating Database Backups

Use SQL export format for creating portable database backups

Select SQL format from export options

Enable "Include CREATE TABLE" for complete table definition

Enable "Include DROP TABLE" for safe overwrites

Export complete dataset

Save backup file in version control or secure location

Review SQL file to ensure all data is present

The resulting SQL file is portable and can be imported into any compatible database.

## Security and Best Practices

### Handling Sensitive Data

**Sensitive Data Alert**: Exported files may contain sensitive information like passwords, API keys, or personal details

**Before Exporting**:

1. **Verify File Destination**: Ensure you're saving to a secure location
2. **Check Permissions**: Verify who has access to the downloaded file
3. **Filter Columns**: Use WHERE conditions to exclude sensitive fields if possible
4. **Review Recipients**: Confirm the intended recipient should receive this data
5. **Encryption**: Consider encrypting exported files before sharing

**After Exporting**:

1. **Secure Storage**: Store backups in encrypted locations
2. **Access Control**: Restrict file permissions appropriately
3. **Compliance**: Verify export complies with data privacy regulations (GDPR, HIPAA, etc.)
4. **Audit Log**: WhoDB logs export operations for compliance
5. **Retention**: Don't keep exports longer than necessary

### Compliance Considerations

Export functions are logged for audit trails:

- **What**: Which table was exported
- **When**: Timestamp of the export
- **Who**: User account that performed export
- **How Many**: Number of rows exported
- **Format**: File format used

Review audit logs regularly to ensure exports comply with your data governance policies

### Performance Notes

**Large Exports**:
- Export All on very large tables may take time
- Consider filtering to smaller date ranges
- Use pagination to break exports into chunks
- Monitor browser memory during download

**Network**:
- Large files may timeout on slow connections
- Use local installations for better performance
- Consider breaking into multiple smaller exports

## Troubleshooting

### Export File is Corrupted

**Cause**: Browser or connection issue during download

**Solution**:
1. Try exporting again
2. Use a different browser
3. Check disk space on your computer
4. Try with smaller row count first

### Special Characters Appear Garbled

**Cause**: Encoding mismatch between export and reader

**Solution**:
1. Change CSV encoding to UTF-8 (default)
2. Verify Excel is configured for UTF-8 import
3. Try ISO-8859-1 if UTF-8 doesn't work
4. Use plain text editor to verify content

### Excel File Won't Open

**Cause**: File size or format compatibility

**Solution**:
1. Verify file isn't corrupted (check file size)
2. Try opening in newer Excel version
3. Try Excel import wizard: Data → From Text
4. Export as CSV and open in Excel instead

### JSON Shows as Binary/Unreadable

**Cause**: Browser is attempting to open rather than download

**Solution**:
1. Right-click the download link → Save As
2. Verify downloaded file is valid JSON
3. Open in text editor to verify content
4. Try with smaller export first

## Related Features

Learn advanced filtering for targeted exports

Select multiple rows efficiently for export

Generate test data for import and testing

Run queries to prepare data for export

## Summary

WhoDB export options provide:

- Multiple format choices (CSV, Excel, JSON, SQL)
- Flexible delimiter and encoding options
- Selective export (all, filtered, or selected rows)
- Portable backup capabilities
- Audit trail for compliance

Whether you need to analyze data in Excel, migrate databases, or integrate with applications, WhoDB's export functionality gives you complete control over your data extraction.

You're ready to export your data in any format you need!

---


# advanced/where-conditions.mdx
## Where Conditions - Advanced Filtering

Filter your data with advanced conditions, operators, and complex queries

# Advanced Filtering with Where Conditions

WhoDB's WHERE conditions feature enables sophisticated data filtering through a visual interface. Build complex queries without writing SQL, or combine conditions to extract exactly the data you need.

WHERE conditions are the foundation of effective data exploration and targeted exports

## Overview

The WHERE conditions interface allows you to:

- Apply single or multiple filters
- Use various comparison operators
- Combine conditions with AND/OR logic
- Build complex queries visually
- Save and reuse filter presets

![Where Conditions Popover](/images/16-data-view-where-conditions-popover.png)

## Basic Filtering

### Opening the Filter Interface

1. Navigate to any table in WhoDB
2. Click the **Filter** button in the action bar (funnel icon)
3. The WHERE conditions popover opens

The interface shows an empty filter state ready for your first condition.

### Creating Your First Condition

Click the field dropdown to choose which column to filter

![Where Field Dropdown](/images/18-data-view-where-field-dropdown.png)

All available columns in the table are shown in the list.

Select the comparison operator for your condition (e.g., =, >, \<)

Available operators depend on the column's data type.

Type or select the value to compare against

Click "Apply" or "Done" to filter the table

The table instantly updates to show only matching rows.

## Comparison Operators

WhoDB supports various operators for different filter scenarios:

### Equality Operators

#### Equals (=)

![Equals Operator](/images/59-where-operator-equals.png)

Exact match filter. Returns rows where the column value exactly matches your input.

**Use for**:
- Exact category matching (status = 'active')
- Finding specific users by ID
- Filtering by exact date or timestamp

```
SELECT * FROM users WHERE status = 'active'
```

Case-sensitive for text fields on most databases

#### Not Equals (!=)

![Not Equals Operator](/images/62-where-operator-not-equals.png)

Inverse match. Returns all rows EXCEPT those matching the value.

**Use for**:
- Excluding specific categories (status != 'deleted')
- Finding inactive records
- Filtering out placeholder values

```
SELECT * FROM users WHERE status != 'archived'
```

**Example Results**:
- Row 1: status = 'active' ✓ (included)
- Row 2: status = 'inactive' ✓ (included)
- Row 3: status = 'deleted' ✗ (excluded)

### Comparison Operators

#### Greater Than (>)

![Greater Than Operator](/images/60-where-operator-greater-than.png)

Returns rows where the column value is strictly greater than the specified value.

**Use for**:
- Age > 18 (find adults)
- price > 100 (expensive items)
- created_at > '2024-01-01' (recent records)

```
SELECT * FROM products WHERE price > 50
```

#### Less Than (\<)

![Less Than Operator](/images/61-where-operator-less-than.png)

Returns rows where the column value is strictly less than the specified value.

**Use for**:
- inventory < 10 (low stock alert)
- age < 18 (minors)
- discount < 0.5 (discounts under 50%)

```
SELECT * FROM products WHERE inventory < 10
```

#### Greater Than or Equal (>=)

![Greater Than or Equal Operator](/images/63-where-operator-gte.png)

Inclusive greater than comparison.

**Use for**:
- age >= 18 (18 and older)
- score >= 80 (passing grades)
- created_at >= '2024-10-01' (October or later)

```
SELECT * FROM orders WHERE total >= 100
```

#### Less Than or Equal (\<=)

![Less Than or Equal Operator](/images/64-where-operator-less-than-equal.png)

Inclusive less than comparison.

**Use for**:
- discount \<= 0.3 (30% discount or less)
- attempts \<= 3 (three or fewer attempts)
- deadline \<= '2024-12-31' (by year end)

```
SELECT * FROM tasks WHERE deadline <= '2024-12-31'
```

## Working with Multiple Conditions

### Adding More Conditions

Click **Add Condition** to add additional filters. Multiple conditions are combined with AND logic by default.

![Multiple Where Conditions](/images/39-data-view-multiple-conditions.png)

**AND Logic**: All conditions must be true
```
WHERE status = 'active' AND age > 18
```

Only rows where status IS 'active' AND age IS greater than 18 are shown.

### Condition Logic

### AND Conditions

**All conditions must be satisfied**

```
WHERE status = 'active' AND created_at > '2024-01-01'
```

**Result**: Shows only active users created after Jan 1, 2024

Use AND when you want to narrow results and require multiple criteria.

**Example Scenarios**:
- Active users who registered in 2024
- Products priced over $50 with inventory > 10
- Orders placed this month and not yet shipped

---

### OR Conditions

**At least one condition must be satisfied**

When filtering within a single column, OR logic can show alternatives:

```
WHERE status = 'active' OR status = 'pending'
```

**Result**: Shows users with either active or pending status

Use OR to broaden results and show alternative options.

**Example Scenarios**:
- Show users with active or trial status
- Products in category A OR category B
- Orders shipped OR in-progress (exclude pending)

### Editing and Removing Conditions

1. Click the condition you want to change
2. Edit the field, operator, or value
3. The table updates automatically

1. Hover over the condition
2. Click the delete/remove icon (X)
3. The table updates with one fewer filter

1. Click "Clear" or "Reset" in the filter interface
2. All conditions are removed
3. Table shows unfiltered data

## Real-World Filtering Scenarios

### User Management

**Find active users who haven't logged in recently**

Set status = 'active'

Set last_login < '2024-09-30' (more than 30 days ago)

**Result**: Identifies users to re-engage or remove

### Inventory Management

**Low stock items that need reordering**

Set quantity \<= 10

Set status != 'discontinued'

**Result**: List of in-stock items needing replenishment

### Financial Analysis

**Orders over $1,000 placed this quarter**

Set total > 1000

Set created_at >= '2024-10-01'

Set created_at \<= '2024-12-31'

**Result**: High-value Q4 2024 orders

### Content Management

**Published posts by a specific author**

Set status = 'published'

Set author = 'Sarah Johnson'

Set created_at > '2024-01-01'

**Result**: All published content from this author in 2024

## Advanced Filtering Patterns

### Date Range Filtering

Filter for records within a specific time period:

Set created_at >= '2024-01-01'

Set created_at < '2024-12-31'

```
WHERE created_at >= '2024-01-01' AND created_at < '2024-12-31'
```

### Numeric Range Filtering

Filter for values within a range:

Set price >= 50

Set price \<= 200

```
WHERE price >= 50 AND price \<= 200
```

**Use cases**: Price ranges, age brackets, score thresholds

### Exclusion Patterns

Show everything EXCEPT specific items:

Set status != 'archived'

Set deleted_at IS NULL

```
WHERE status != 'archived' AND deleted_at IS NULL
```

### Combining Categories

Show records from multiple categories:

Since visual interface uses AND, you'd typically show one category at a time. For multiple categories, use SQL mode:

```
WHERE category = 'Electronics' OR category = 'Computers'
```

## Filter Indicators and Status

### Active Filter Display

When filters are applied, the filter button shows a badge indicating the number of active conditions.

![Where Conditions Badge](/images/19-data-view-search-highlight.png)

- **1 condition**: One filter is active
- **2+ conditions**: Multiple filters applied
- **No badge**: No filters active (showing all data)

### Row Count Changes

Notice how the table updates instantly when you apply or modify filters. The page count and total row display changes to reflect filtered results.

## Saving and Reusing Filters

Save frequently used filter combinations for quick access

### Saving a Filter Set

After creating a useful set of conditions:

1. Complete your filter configuration
2. Click **Save Filter** (if available)
3. Give it a descriptive name
4. Access it later from the saved filters list

### Common Saved Filters

Examples of filter sets worth saving:

- **Active Users**: status = 'active'
- **Recent Orders**: created_at > current_date - 30 days
- **Overdue Tasks**: status = 'pending' AND due_date < current_date
- **High Priority**: priority = 'high' AND status != 'completed'
- **Q4 2024 Revenue**: date >= '2024-10-01' AND date \<= '2024-12-31'

## Exporting Filtered Data

After applying filters, you can export only the filtered results:

Use WHERE conditions to narrow your data

Click the Export button

Select the "Export Filtered" option

Choose CSV, Excel, JSON, or SQL

Get your filtered data in desired format

This is perfect for:
- Exporting quarterly reports (filtered by date)
- Sharing specific customer data (filtered by status)
- Creating demo datasets (filtered to relevant records)

## Performance Considerations

### Filtering Large Tables

**Use Indexes for Fast Filtering**: Filters on indexed columns deliver the best performance.
- Fast: status = 'active' AND created_at > '2024-01-01' (if both columns are indexed)
- Slow: status NOT IN ('deleted', 'archived', 'inactive') (especially if status isn't indexed)

**Apply Filters Before Other Operations**: Filtering at the start reduces workload for subsequent steps.
- Filter using WHERE conditions before applying sorting or searching
- Paginate after filtering to minimize total data processed

**Check Column Indexing**: Filtering on unindexed columns can result in slower queries.
- Commonly indexed columns: id, status, created_at (see Explore tab for details)
- Avoid filtering heavily on text fields or columns without indexes for large tables

### Complex Query Alternatives

For very complex filtering beyond the visual interface:

1. Switch to **Scratchpad** SQL editor
2. Write custom SQL with complex logic
3. Use subqueries, JOINs, and window functions
4. Export results as needed

```
SELECT * FROM users
WHERE status = 'active'
  AND created_at > DATE_SUB(NOW(), INTERVAL 30 DAY)
  AND user_id IN (
    SELECT user_id FROM orders
    WHERE total > 100
    GROUP BY user_id
    HAVING COUNT(*) > 5
  )
```

## Best Practices

Begin with one condition, verify results, then add more. This helps you understand data and debug issues.

Choose the right operator for your data type:
- Text: Equals, Not Equals
- Numbers: Range operators (>=, \<=)
- Dates: Range and comparison operators
- Booleans: Equals for true/false

After applying filters, scan the results to ensure they match your expectations. Mismatches reveal data issues.

If using complex conditions, save them with clear names or note them for reproducibility.

Use WHERE conditions for structured filtering and Search for text pattern matching—they complement each other.

## Troubleshooting

### Filter Returns No Results

**Cause**: Conditions too restrictive or values don't exist

**Solution**:
1. Remove one condition to see if results appear
2. Verify the value you're filtering for exists
3. Check for case sensitivity (text fields)
4. Use the table preview to verify data

### Results Don't Match Expectations

**Cause**: Misunderstanding of operator behavior

**Solution**:
1. Clear filters and start fresh
2. Apply one condition at a time
3. Verify each step matches expectations
4. Check if AND/OR logic is correct

### Slow Filtering Performance

**Cause**: Large dataset or unoptimized query

**Solution**:
1. Use more specific filter values
2. Add date ranges to narrow results
3. Filter on indexed columns
4. Check database indexes in Explore view

### Need Complex Logic

**Alternative**: Switch to Scratchpad SQL editor for advanced queries

## Related Features

Export your filtered data in multiple formats

Combine WHERE conditions with text search

Work with filtered row selections

Write custom SQL for advanced filtering

## Summary

WHERE conditions provide:

- Visual interface for building filters
- Multiple comparison operators
- Support for single and complex multi-condition queries
- Real-time table updates
- Integration with export and batch operations
- Performance-conscious filtering

Master WHERE conditions to efficiently extract exactly the data you need from your databases, whether for analysis, reporting, or data maintenance tasks.

You're ready to filter your data with precision and confidence!

---


# advanced/batch-operations.mdx
## Batch Operations

Perform bulk actions on multiple rows with row selection and context menus

# Batch Operations

WhoDB supports efficient batch operations that allow you to select multiple rows and perform actions on them collectively. This is essential for bulk data management, cleanup, and targeted modifications.

Row selection and batch operations streamline workflows when working with multiple records

## Overview

Batch operations in WhoDB include:

- **Row selection**: Choose one or multiple rows
- **Bulk actions**: Perform operations on selected rows
- **Context menus**: Quick access to batch operations
- **Selective export**: Export only selected rows
- **Bulk deletion**: Delete multiple rows at once

![Row Selection Single](/images/44-table-row-selection-single.png)

## Row Selection Methods

### Individual Row Selection

Select a single row by clicking its checkbox:

1. Locate the checkbox at the left of any row
2. Click to select that row
3. The row highlights to indicate selection
4. Action buttons appear for the selected row

**Use for**: Targeting specific records for editing or deletion

### Multiple Row Selection

Select multiple rows to perform batch operations:

Click the checkbox for the first row you want to select

To select a contiguous range of rows, hold Shift and click the last row in your range

To select non-contiguous rows, hold Ctrl (Windows) or Cmd (Mac) and click additional rows

**Result**: Multiple rows are selected and highlighted

The selection indicator shows how many rows are currently selected (e.g., "3 rows selected")

### Select/Deselect All

Many interfaces include buttons to select or deselect all visible rows:

- **Select All**: Select every row currently displayed
- **Deselect All**: Clear all selections

These are useful for:
- Selecting all rows on the current page
- Selecting all filtered results
- Clearing selections after batch operations

## Context Menu Operations

### Accessing the Context Menu

Right-click on any row to open the context menu:

![Context Menu](/images/38-table-header-context-menu.png)

The context menu shows available actions for that row or selection.

### Select Row Option

![Select Row from Context Menu](/images/45-context-menu-select-row.png)

One option in the context menu is "Select Row" which:

- Selects the clicked row (or toggles selection)
- Prepares the row for batch operations
- Works alongside other selected rows

## Batch Actions

### Available Batch Operations

After selecting rows, several batch operations become available:

#### Export Selected Rows

Export only the rows you've selected:

![Export Selected Rows Mode](/images/46-export-selected-rows-dialog.png)

Choose one or multiple rows using checkboxes

Access the export function from the action bar

Select the "Export Selected" option in the dialog

Choose CSV, Excel, JSON, or SQL format

Click Export to download your selected rows

**Perfect for**:
- Sharing specific records with colleagues
- Creating sample datasets for demos
- Extracting problem records for analysis
- Exporting customer data for specific queries

**Example**: Select 5 customer records to send to sales team

#### Delete Selected Rows

Remove multiple rows in one operation:

Deletion is permanent. Always verify selections before confirming.

Choose the rows you want to remove

Right-click and select "Delete Row" or use action bar delete button

Accept the confirmation dialog

Confirm rows were deleted

**Example workflow**:
- Filter to status = 'inactive'
- Select all inactive users (Ctrl+A or Select All button)
- Delete selected rows to clean up old accounts

**Safety tip**: Always create an export backup before bulk deletion

#### Update Multiple Rows

Update a field value across multiple selected rows:

While individual row editing works on one row at a time, this workflow applies changes to multiple selected rows:

Choose rows that need the same change

Right-click context menu or action bar option

Choose which column to modify

Provide the new value for all selected rows

Apply the change to all selected rows

**Example scenarios**:
- Change status from 'pending' to 'approved' for multiple rows
- Update last_reviewed date for a group of records
- Mark multiple items as 'inactive'

## Real-World Batch Operation Workflows

### Cleaning Up Old Data

Remove records that meet cleanup criteria:

Use WHERE conditions: created_at < '2020-01-01' AND status = 'deleted'

Export filtered results to CSV for archive

Use Select All button on filtered results

Remove all selected rows

Confirm count decreased and backups were saved

**Result**: Clean database with archived backups

### Bulk Status Updates

Update multiple records to reflect a new state:

Use WHERE conditions: status = 'pending' AND created_at < '2024-09-01'

Click Select All to choose all pending items

Change status to 'archived' for all selected

Filter again to confirm change took effect

**Result**: Multiple records updated efficiently

### Data Export and Analysis

Extract specific record sets for external analysis:

Use WHERE conditions: department = 'Sales' AND year = 2024

Select all results or specific rows

Choose Excel format for spreadsheet analysis

Save to computer for further analysis

Use Excel, Tableau, or other tools

**Result**: Sales department data in Excel ready for analysis

### Archival Workflow

Move large amounts of old data out of active tables:

Filter by: created_at < '2023-01-01'

Export all matching rows to JSON/SQL for backup

Save backup file to archive location

Select filtered rows and delete

Confirm counts and backups succeeded

**Result**: Smaller active table with archived data safely backed up

## Selection Best Practices

### Clear Selection Before New Operations

Always clear selections between operations:

1. After completing an action with selected rows
2. Before starting a different batch operation
3. Click "Deselect All" or Select All then Deselect

This prevents accidental operations on stale selections.

### Verify Before Destructive Operations

For deletion and bulk updates:

- [ ] Correct rows are selected (spot check a few)
- [ ] Selection count matches expectation
- [ ] Backup or export completed (for deletion)
- [ ] No important data in selected rows
- [ ] Confirmation dialog shown and read carefully
- [ ] Ready to proceed with operation

### Use Filters with Selection

Combine filtering and selection for precision:

Narrow to specific data subset

Verify filter caught intended rows

Choose all matching rows or specific subset

Execute batch operation on selection

**Example**: Filter to inactive users (status = 'inactive'), select all, then delete

### Selective vs. All Selection

Choose the right approach:

### Select All (Fast)

Use when:
- All visible/filtered rows need the same operation
- You've already filtered to the correct subset
- Time is critical and accuracy confirmed

```
Filter → Select All → Export/Delete → Done
```

Advantages: Speed, simplicity

---

### Selective Selection (Safe)

Use when:
- Only some rows need the operation
- Criteria are complex or varied
- High cost of mistake (important data)

```
Manually select → Spot check → Operation → Verify
```

Advantages: Precision, control, safety

## Advanced Selection Scenarios

### A/B Testing Data

Select test and control groups separately:

Filter to user_id % 2 = 0, select half

Export selected rows to CSV

Filter different criteria, select other half

Export to separate CSV for comparison

### Progressive Processing

Handle large datasets in batches:

1. Filter to first 100 rows (pagination or date range)
2. Select and process
3. Move to next batch
4. Repeat until complete

Prevents overwhelming single operations on huge datasets.

### Anomaly Investigation

Select unusual records for analysis:

WHERE amount > expected_max OR status = 'error'

Select all or specific outliers

Export with all related fields

Investigate in spreadsheet or custom tool

## Troubleshooting

### Selection Seems Stuck

**Issue**: Selections not responding to new clicks

**Solution**:
1. Refresh the page (Cmd/Ctrl + R)
2. Click elsewhere first, then try again
3. Clear selections with Deselect All
4. Restart the batch operation

### Wrong Rows Selected

**Issue**: Selected rows don't match what you intended

**Solution**:
1. Click Deselect All to start fresh
2. Verify filter is correct
3. Manually reselect rows carefully
4. Use spot checks before confirming action

### Batch Operation Didn't Complete

**Issue**: Export or delete seemed to work but nothing happened

**Solution**:
1. Refresh page to see current state
2. Check browser console for errors
3. Verify selection wasn't cleared
4. Try operation again with fewer rows

### Performance Issues with Large Selection

**Issue**: Interface slows down with hundreds of selected rows

**Solution**:
1. Process in smaller batches
2. Break operation into multiple passes
3. Clear selections between passes
4. Contact support if persists

## Related Features

Filter rows before batch selection

Learn all export capabilities for selected rows

Individual row editing and operations

Batch operations via SQL for complex scenarios

## Security Considerations

### Batch Operation Audit Trail

All batch operations are logged:

- **What**: Which rows were affected
- **When**: Timestamp of operation
- **Who**: User account performing operation
- **Type**: Delete, update, export
- **Count**: Number of rows affected

### Access Control

Batch operations respect database permissions:

- Users can only select/modify rows they have access to
- Deletions are logged for compliance
- Exports are auditable for data governance
- Consider read-only accounts for sensitive data

Always verify you have proper authorization before performing batch deletions or exports on production databases

## Summary

Batch operations provide:

- Efficient row selection (single, multiple, all)
- Context menu access to bulk actions
- Selective and bulk export capabilities
- Safe deletion with verification
- Audit trails for compliance
- Integration with filtering for precision

Master batch operations to efficiently manage large datasets, perform bulk updates, and export precisely the data you need—while maintaining data integrity and security.

You're ready to work efficiently with multiple rows and perform powerful batch operations!

---


# guides/complete-beginner.mdx
## Complete Beginner's Guide to Database Management with WhoDB

Learn database management from scratch with WhoDB. Understand databases, connect safely, explore tables, run queries, and export data with our step-by-step beginner's guide.

# Complete Beginner's Guide to WhoDB

Welcome to the world of database management. This guide is designed for absolute beginners who are new to databases and want to learn how to work with data effectively using WhoDB.

No prior database experience needed. We'll walk you through everything step by step, starting from the very basics.

## What is a Database?

Before diving into WhoDB, let's understand what databases are and why they matter.

### Understanding Databases

A **database** is an organized collection of data stored electronically. Think of it as a digital filing cabinet where information is stored in a structured way that makes it easy to find, update, and manage.

**Examples You Use Every Day:**
- Your email inbox (stores messages, sender info, dates)
- Social media platforms (stores posts, comments, user profiles)
- Online stores (products, prices, inventory)
- Banking apps (account balances, transactions)
- Your phone contacts (names, numbers, addresses)

All these applications use databases behind the scenes to store and retrieve information quickly.

**Databases are essential because they:**
- Store large amounts of data efficiently
- Allow multiple people to access data simultaneously
- Keep data organized and easy to search
- Prevent data loss with backups
- Ensure data accuracy with rules and validations
- Enable fast searching even with millions of records

WhoDB supports several database types:

**Relational Databases (SQL)**
- PostgreSQL: Popular for web applications
- MySQL: Widely used, especially for websites
- SQLite: Simple, file-based database

**NoSQL Databases**
- MongoDB: Flexible document storage
- Redis: Fast key-value storage for caching

Don't worry about choosing right now—you can work with any of these using WhoDB.

### Database Tables Explained

Data in databases is organized into **tables**, similar to spreadsheets:

Each row represents a single item or entry, like one person or one product

Each column represents a specific piece of information, like name, email, or price

A unique identifier for each row, ensuring no duplicates

Connections between tables, like linking orders to customers

**Example: Users Table**

| id | username | email | created_at |
|----|----------|-------|------------|
| 1 | john_doe | john@example.com | 2024-01-15 |
| 2 | jane_smith | jane@example.com | 2024-01-16 |
| 3 | bob_wilson | bob@example.com | 2024-01-17 |

- **Rows**: Each person is one row
- **Columns**: id, username, email, created_at
- **Primary Key**: The "id" column uniquely identifies each user

Think of a database table like an Excel spreadsheet with extra rules to keep data consistent and organized.

## Getting Started with WhoDB

Now that you understand databases, let's connect to one using WhoDB.

### What is WhoDB?

**WhoDB** is a database management tool that provides a user-friendly interface to:
- Connect to your databases
- View and explore your data
- Add, edit, and delete records
- Run queries to analyze data
- Export data for reports

Think of WhoDB as a window into your database—it makes complex database operations simple and visual.

WhoDB is lightweight and fast. Unlike complex tools that require installation and configuration, WhoDB is ready to use immediately.

### Your First Connection

Let's connect to your first database. Don't worry—we'll guide you through every step.

Open WhoDB in your web browser. You'll see the login page where you'll configure your database connection.

![WhoDB Login Page](/images/01-login-page.png)

This is your starting point for accessing any database.

Click the database type dropdown to see all supported databases.

![Database Type Dropdown](/images/02-login-database-type-dropdown.png)

**If you're not sure which to choose:**
- For local testing: Choose **SQLite**
- For web applications: Usually **PostgreSQL** or **MySQL**
- Ask your team or check your application's documentation

Fill in the required information. Here's what each field means:

![Login Form Filled](/images/03-login-form-filled.png)

**Common Fields:**
- **Host**: The computer address where your database lives (often "localhost" for local databases)
- **Port**: The communication channel number (default: 5432 for PostgreSQL, 3306 for MySQL)
- **Username**: Your database account name
- **Password**: Your database password (kept secure and not stored)
- **Database**: The specific database name you want to access

If you're practicing, ask your instructor or team lead for these connection details. Never guess—incorrect details won't harm anything, but you won't be able to connect.

Click "Advanced Options" if you need special settings like SSL or SSH tunnels.

![Advanced Options](/images/04-login-advanced-options.png)

**For beginners:** You can usually skip advanced options for local or development databases.

**For production databases:** Your administrator will tell you if you need SSL or other security settings.

Click the "Connect" button. WhoDB will verify your credentials and connect to the database.

**If connection succeeds:** You'll see your database interface with all your tables listed.

**If connection fails:** Check these common issues:
- Is the database running?
- Are your credentials correct?
- Is the host and port correct?
- For local databases: Try "localhost" or "127.0.0.1" as the host

Keep your database credentials secure. Never share passwords or save them in insecure locations.

## Understanding the WhoDB Interface

Once connected, you'll see the main interface. Let's explore what everything means.

### The Main Screen

![Storage Unit List with Sidebar](/images/06-storage-unit-list-with-sidebar.png)

The interface has four main areas:

Shows your database structure: schemas, tables, views

Displays your data, queries, and results

Navigation between different views (Explore, Data, Graph, Scratchpad)

Buttons for adding, editing, exporting, and other actions

### Navigating the Sidebar

The sidebar is your database navigation tool:

Click on a schema (database section) to expand it and see what's inside. Think of schemas as folders organizing related tables.

Under each schema, you'll see a list of tables (where your data lives), views (saved queries), and other database objects.

Click any table name to view its data in the main area.

If you see a lot of tables, use the search box in the sidebar to quickly find what you need.

## Exploring Your First Table

Let's look at your data. We'll start with the "users" table as an example.

### The Explore View

Click on a table name, then click the "Explore" tab to see the table's structure:

![Explore Users Table](/images/07-explore-users-table.png)

**What you're seeing:**

Basic information about the table:
- **Table Name**: The official name in the database
- **Row Count**: How many records are in the table
- **Size**: How much disk space the table uses
- **Created**: When the table was created

Every column (field) in the table with:
- **Name**: What the column is called
- **Type**: What kind of data it stores (text, numbers, dates, etc.)
- **Nullable**: Whether it can be empty (NULL)
- **Default**: What value it gets if you don't specify one

The unique identifier column(s) that ensure each row is distinct.

![Primary Key Column](/images/98-explore-primary-key-column.png)

Usually an "id" column with auto-incrementing numbers.

Connections to other tables.

![Foreign Key Columns](/images/99-explore-foreign-key-columns.png)

For example, an "orders" table might have a "user_id" column linking to the "users" table.

Special database features that speed up searches.

![Table with Indexes](/images/100-explore-table-with-indexes.png)

Think of them like an index in a book—they help find information faster.

The Explore view is like looking at the blueprint of your table. It shows you the structure without showing the actual data.

## Viewing Table Data

Now let's see the actual data inside your table.

### The Data View

Click the "Data" tab to view the table's records:

![Data View Users Table](/images/09-data-view-users-table.png)

**Understanding what you see:**

Your data displays in a spreadsheet-like grid with rows and columns.

![Data View Table Content](/images/10-data-view-table-content.png)

- Each row is one record (one user, one product, etc.)
- Each column is one piece of information
- Scroll horizontally to see more columns
- Scroll vertically to see more rows

The top row shows column names. Click any header to sort by that column (ascending or descending).

![Sorted Column](/images/38-data-view-sorted-column.png)

Click the checkbox at the start of any row to select it. Selected rows can be exported, deleted, or modified together.

![Table Row Selection](/images/44-table-row-selection-single.png)

At the bottom, you'll see page numbers. Large tables are split into pages for faster loading.

Use the page size dropdown to control how many rows appear per page (10, 25, 50, or 100).

![Page Size Dropdown](/images/15-data-view-page-size-dropdown.png)

### Different Data Types

WhoDB displays different types of data appropriately:

**Text and Strings**
![Long Text Content](/images/80-table-long-text-content.png)

Text wraps to fit in cells, making long descriptions readable.

**Numbers**
![Numeric Types](/images/82-table-numeric-types.png)

Numbers align to the right for easy comparison.

**Dates and Timestamps**
![Dates and Timestamps](/images/81-table-dates-timestamps.png)

Dates display in a consistent format with time when relevant.

**NULL Values**
![Table with NULL Values](/images/79-table-with-null-values.png)

Empty cells represent NULL (missing data). These are different from empty strings.

Understanding data types helps you work with data correctly. For example, you can sort numbers by value but text alphabetically.

## Your First Search and Filter

Now that you can view data, let's learn to find specific information.

### Searching Data

Use the search box to find records containing specific text:

Click the search box and type what you're looking for.

As you type, WhoDB searches all visible columns in real-time.

Matching records are highlighted:

![Search Highlight](/images/19-data-view-search-highlight.png)

The search looks through all columns, making it easy to find data even if you don't remember which field it's in.

Click the X in the search box to clear it and show all records again.

### Filtering with WHERE Conditions

For more precise filtering, use WHERE conditions:

Click the filter button to open the WHERE conditions panel.

![Where Conditions Popover](/images/16-data-view-where-conditions-popover.png)

Choose which column you want to filter by:

![Where Field Dropdown](/images/17-data-view-where-field-dropdown.png)

Select how you want to compare:

- **Equals (=)**: Exact match
  ![Operator Equals](/images/59-where-operator-equals.png)

- **Not Equals (!=)**: Everything except this value
  ![Operator Not Equals](/images/62-where-operator-not-equals.png)

- **Greater Than (>)**: Numbers or dates above a value
  ![Operator Greater Than](/images/60-where-operator-greater-than.png)

- **Less Than (\<)**: Numbers or dates below a value
  ![Operator Less Than](/images/61-where-operator-less-than.png)

- **Greater Than or Equal (>=)**: At or above a value
  ![Operator GTE](/images/63-where-operator-gte.png)

- **Less Than or Equal (&lt;=)**: At or below a value
  ![Operator LTE](/images/64-where-operator-lte.png)

Type the value you want to filter by, then click "Add Condition."

The table updates to show only matching records:

![Where Conditions Badge](/images/18-data-view-where-conditions-badge.png)

You'll see a badge showing how many conditions are active.

Add more conditions to narrow results further:

![Multiple Conditions](/images/39-data-view-multiple-conditions.png)

All conditions must be true for a row to appear (AND logic).

WHERE conditions are powerful. You can filter by dates (e.g., "created_at > 2024-01-01"), numbers (e.g., "price &lt; 100"), or text (e.g., "status = 'active'").

## Running Your First Query

Now you're ready to write SQL queries in the Scratchpad.

### What is SQL?

**SQL** (Structured Query Language) is the language used to communicate with databases. Don't worry—it's more readable than most programming languages.

**Basic SQL Query Structure:**

```
SELECT column1, column2
FROM table_name
WHERE condition
ORDER BY column1
LIMIT 10;
```

This reads almost like English: "Select these columns from this table where this condition is true, ordered by this column, limit to 10 results."

### Using the Scratchpad

Click "Scratchpad" in the top navigation to open the query editor.

![Scratchpad Main View](/images/27-scratchpad-main-view.png)

This is your SQL playground where you can write and test queries.

Let's start with the simplest query—getting all data from a table:

![Scratchpad Code Editor](/images/28-scratchpad-code-editor.png)

Type this query:

```
SELECT * FROM users LIMIT 10;
```

**What this means:**
- `SELECT *` = Get all columns
- `FROM users` = From the users table
- `LIMIT 10` = Only show 10 rows

Always use LIMIT when exploring. It prevents accidentally loading millions of rows.

Press the Run button or hit **Cmd/Ctrl + Enter**.

WhoDB executes your query and shows the results below:

![Query Results](/images/29-scratchpad-query-results.png)

You'll see:
- **Results Table**: Your data in a grid format
- **Row Count**: How many rows were returned
- **Execution Time**: How long the query took

If there's an error, you'll see a clear message explaining what went wrong:

![Query Error](/images/30-scratchpad-query-error.png)

### Common Query Examples

Here are some beginner-friendly queries to try:

Instead of all columns (`*`), choose specific ones:

```
SELECT id, username, email
FROM users
LIMIT 10;
```

![Select Query Result](/images/86-scratchpad-select-query-result.png)

This makes results cleaner and loads faster.

Find out how many records are in a table:

```
SELECT COUNT(*) as total_users
FROM users;
```

![Count Query Result](/images/87-scratchpad-count-query-result.png)

The `as total_users` part gives the result column a readable name.

Use WHERE to show only matching records:

```
SELECT username, email
FROM users
WHERE created_at > '2024-01-01'
LIMIT 20;
```

This shows users created after January 1, 2024.

Sort your results:

```
SELECT username, email, created_at
FROM users
ORDER BY created_at DESC
LIMIT 10;
```

`DESC` means descending (newest first). Use `ASC` for ascending (oldest first).

Combine data from related tables:

```
SELECT
  users.username,
  orders.order_date,
  orders.total
FROM users
LEFT JOIN orders ON users.id = orders.user_id
LIMIT 10;
```

![Join Query Result](/images/88-scratchpad-join-query-result.png)

This shows users and their orders together.

Be careful with UPDATE and DELETE queries. They permanently change your data. Always use WHERE conditions to avoid affecting the wrong records.

### Query History

All your queries are automatically saved:

![Query History](/images/33-scratchpad-query-history.png)

Click the History panel to see previous queries.

Click any historical query to load it into the editor.

![History Clone Button](/images/49-scratchpad-history-clone-button.png)

Make changes and run again. This is faster than writing from scratch.

## Exporting Your Data

Once you've found the data you need, you can export it for use in other applications.

### Export Process

From the Data view, click the Export button to open the export dialog.

![Export Dialog](/images/20-data-view-export-dialog.png)

Select your export scope:
- **All Rows**: Export everything in the table
- **Selected Rows**: Only rows you've selected
- **Filtered Rows**: Rows matching your current filters

![Export Selected Rows](/images/46-export-selected-rows-dialog.png)

Choose your export format:

**CSV (Comma-Separated Values)**
![CSV Option](/images/65-export-format-csv-option.png)

Best for Excel, Google Sheets, and data analysis tools.

**Excel (.xlsx)**
![Excel Option](/images/66-export-format-excel-option.png)

Native Excel format with formatting support.

**JSON**
For developers and APIs—structured data format.

**SQL**
Creates INSERT statements you can run on another database.

Set format-specific options:

![Export Delimiter](/images/67-export-delimiter-comma.png)

For CSV:
- **Delimiter**: Comma, semicolon, or pipe
- **Include Headers**: Column names in first row
- **Quote Strings**: Wrap text in quotes

Click "Export" and your file will download to your computer.

You can now open it in Excel, import it into another system, or use it for analysis.

CSV is the most universal format. When in doubt, export as CSV—it works with almost everything.

## Visualizing Relationships

The Graph view helps you understand how your tables connect.

### Understanding the Graph

Switch to the Graph view to see your database schema visually:

![Graph View Schema Topology](/images/24-graph-view-schema-topology.png)

**What you're seeing:**
- **Boxes (Nodes)**: Each box represents one table
- **Lines (Edges)**: Connections show relationships between tables
- **Arrows**: Show the direction of relationships

Use the controls to explore:

![Graph View with Controls](/images/25-graph-view-with-controls.png)

- **Pan**: Click and drag to move around
- **Zoom**: Mouse wheel or zoom buttons
- **Fit View**: Centers all tables in the viewport

![Fit View Control](/images/97-graph-fit-view-control.png)

Click any table to see its details:

![Graph Node Details](/images/26-graph-view-node-details.png)

You'll see:
- Column names and types
- Primary key
- Foreign keys connecting to other tables

Hover over a table node and click "View Data" to jump directly to that table's data:

![Graph Node Data Button](/images/50-graph-node-data-button-hover.png)

### Understanding Relationships

Different relationship types appear differently:

**One-to-Many Relationship**
![One to Many Relationship](/images/92-graph-one-to-many-relationship.png)

Example: One user can have many orders.

**Many-to-One Relationship**
![Many to One Relationship](/images/93-graph-many-to-one-relationship.png)

The reverse: Many orders belong to one user.

**Multiple Foreign Keys**
![Multiple Foreign Keys](/images/94-graph-multiple-foreign-keys.png)

Some tables connect to many others.

**Isolated Tables**
![Isolated Table Node](/images/91-graph-isolated-table-node.png)

Tables without relationships appear alone.

The Graph view is especially helpful when you're new to a database. It shows you how everything fits together at a glance.

## Tips for Beginners

Don't try to learn everything at once. Master viewing data first, then move to filtering, then queries.

Never practice on production (live) databases. Ask for a development copy where mistakes don't matter.

Always add `LIMIT 10` or `LIMIT 100` to queries when exploring. This prevents accidentally loading millions of rows.

When queries fail, read the error message carefully. It usually tells you exactly what's wrong:
- Misspelled table name
- Missing quotation marks
- Wrong column name
- Syntax error

Save useful queries in a note file. Copy-paste and modify them instead of writing from scratch.

Before UPDATE or DELETE operations, export the data first. This gives you a backup if something goes wrong.

If you're confused, ask someone with more experience. Database mistakes can be costly, so it's better to ask than guess.

Database skills build on each other. Master each concept before moving to the next:

1. View data
2. Search and filter
3. Sort and paginate
4. Simple SELECT queries
5. WHERE conditions
6. Joins
7. Updates and deletes

## Common Beginner Mistakes

Watch out for these common pitfalls:

**Wrong:**
```
DELETE FROM users;
```

This deletes ALL users. Always include WHERE:

**Correct:**
```
DELETE FROM users WHERE id = 123;
```

**Wrong:**
```
SELECT * FROM huge_table;
```

This tries to load millions of rows. Always use LIMIT:

**Correct:**
```
SELECT * FROM huge_table LIMIT 100;
```

In many databases:
- Keywords are case-insensitive: `SELECT` = `select`
- Table/column names may be case-sensitive depending on the database

When in doubt, match the exact case shown in the Explore view.

Never practice on live databases. Changes are permanent and can cause serious problems.

`NULL` is not the same as empty string or zero:
- `NULL` = Missing/unknown value
- `''` = Empty text
- `0` = The number zero

Use `IS NULL` or `IS NOT NULL` to check for NULL values.

## Next Steps

Congratulations on completing the beginner's guide. You now understand database basics and can navigate WhoDB confidently.

Explore more WhoDB features with hands-on examples

Learn advanced data viewing, editing, and management techniques

Master SQL queries with auto-completion and best practices

Coming from phpMyAdmin or pgAdmin? Check our migration guides

## Getting Help

Browse the full WhoDB documentation for in-depth guides on every feature.

Check the FAQ section for answers to frequently asked questions.

Join the WhoDB community to ask questions and share tips with other users.

Ask your team or instructor for practice databases where you can experiment safely.

You're now ready to work with databases confidently. Remember: everyone started as a beginner. Take your time, practice regularly, and don't be afraid to ask questions.

Bookmark this guide for reference. As you gain experience, you'll find yourself using WhoDB faster and more confidently each day.

---


# guides/migrating-from-phpmyadmin.mdx
## phpMyAdmin Alternative: Migrate to WhoDB

Switch from phpMyAdmin to WhoDB. Feature comparison, workflow mapping, keyboard shortcuts, and why WhoDB is better for modern database management.

# Migrating from phpMyAdmin to WhoDB

If you've been using phpMyAdmin for years, you're already familiar with web-based database management. WhoDB builds on that foundation with a modern, intuitive interface designed for today's development workflows. This guide will help you transition smoothly and discover what makes WhoDB better.

WhoDB is designed to feel familiar to phpMyAdmin users while being significantly more intuitive and feature-rich. Most developers switch within minutes.

## Why Migrate from phpMyAdmin?

Both tools manage databases through a web interface, but WhoDB offers important advantages:

Clean, intuitive interface without unnecessary clutter or outdated design

Faster load times and responsiveness even with large datasets

Graph visualization, mock data, better querying, and more

Keyboard shortcuts, query history, multi-cell queries, syntax highlighting

PostgreSQL, MySQL, SQLite, MongoDB, Redis from the same interface

Powerful filtering with visual query builder instead of complex SQL

## Feature Comparison: phpMyAdmin vs WhoDB

| Feature | phpMyAdmin | WhoDB |
|---------|-----------|-------|
| Create/Drop Databases | Yes | Yes |
| Create/Drop Tables | Yes | Yes |
| Table Structure View | Yes, text-based | Yes, modern UI |
| Column Management | Complex dialogs | Intuitive forms |
| Storage Engines | Full support | Yes |
| Collation Settings | Yes | Yes |
| Connection Profiles | Limited | Built-in |
| Multi-Database View | Dropdown menu | Sidebar browser |

| Feature | phpMyAdmin | WhoDB |
|---------|-----------|-------|
| Table Data Display | Grid view | Modern data grid |
| Pagination | Yes | Yes, with custom sizes |
| Column Sorting | Click header | Click header |
| Multi-Column Sorting | Complex | Intuitive |
| Search Data | Basic search | Advanced search + highlighting |
| Filter/Where Conditions | SQL-based | Visual query builder |
| Row Count | Shows total | Shows total with pagination |
| Cell Preview | Hover tooltip | Inline preview |
| Copy Cell Value | Right-click | Context menu |

| Feature | phpMyAdmin | WhoDB |
|---------|-----------|-------|
| Inline Editing | Limited | Full support |
| Add Row Dialog | Yes | Modern modal |
| Edit Row Dialog | Complex | Clean interface |
| Bulk Operations | Limited | Yes |
| Delete Confirmation | Yes | Yes |
| Undo Operations | No | Limited |
| Data Validation | Basic | Enhanced |
| Null Value Handling | Confusing | Clear UI |

| Feature | phpMyAdmin | WhoDB |
|---------|-----------|-------|
| SQL Editor | Single query | Multi-cell scratchpad |
| Syntax Highlighting | Yes | Yes, modern |
| Query History | Browser history | Built-in history |
| Save Queries | File download | Built-in storage |
| Query Templates | Limited | Yes |
| Auto-Completion | Basic | Advanced |
| Query Formatting | Manual | Auto-format available |
| Multiple Queries | One at a time | Execute multiple cells |

| Feature | phpMyAdmin | WhoDB |
|---------|-----------|-------|
| Relationships View | Text-based | Interactive graph |
| Foreign Key Display | Table listing | Visual connections |
| Schema Diagram | Requires plugin | Built-in |
| Interactive Exploration | No | Yes |
| Relationship Details | Manual review | Click node for details |
| Export Diagram | No | Yes |

| Feature | phpMyAdmin | WhoDB |
|---------|-----------|-------|
| CSV Export | Yes | Yes |
| Excel Export | Yes, XLS | Yes, XLSX |
| JSON Export | Yes | Yes |
| SQL Export | Yes | Yes |
| XML Export | Yes | Limited |
| Custom Delimiters | Yes | Yes |
| Selected Rows Export | Yes | Yes, easier |
| Filtered Data Export | Yes | Yes, clearer |

| Feature | phpMyAdmin | WhoDB |
|---------|-----------|-------|
| Learning Curve | Steep | Gentle |
| Keyboard Shortcuts | Limited | Extensive |
| Mobile Responsive | Partial | Full |
| Dark Mode | Yes | Yes |
| Customization | Limited | More options |
| Context Menus | Yes | Enhanced |
| Accessibility | Basic | Modern standards |

## Workflow Mapping: Your Familiar Tasks in WhoDB

### Task: Viewing Table Data

**In phpMyAdmin:**
1. Click database in left sidebar
2. Click table name
3. Click "Browse" tab
4. Data appears in table

**In WhoDB:**
1. Database visible in sidebar
2. Click table name directly
3. Data view appears immediately
4. No extra tabs needed

WhoDB's sidebar automatically shows your database structure. Just click any table to view its data instantly. No need for separate "browse" steps.

### Task: Editing a Row

**In phpMyAdmin:**
1. Click the pencil icon on a row
2. Wait for edit page to load
3. Modify values in form
4. Click "Go" to save

**In WhoDB:**
1. Right-click row → "Edit Row" (or double-click)
2. Modal opens with form
3. Modify values
4. Click "Update" to save

![Data View With Edit Option](/images/09-data-view-users-table.png)

### Task: Filtering Data

**In phpMyAdmin:**
1. Click "Search" tab or use WHERE clause
2. Write complex SQL conditions
3. Execute to filter

**In WhoDB:**
1. Click filter icon
2. Select field → operator → value
3. Filter applies instantly

![Where Conditions Popover](/images/16-data-view-where-conditions-popover.png)

WhoDB's visual filter builder is much faster than phpMyAdmin's SQL-based approach, especially for non-developers.

### Task: Exporting Data

**In phpMyAdmin:**
1. Click "Export" tab
2. Select format (CSV, Excel, SQL)
3. Configure options through multiple pages
4. Click "Go"

**In WhoDB:**
1. Click "Export" button
2. Choose format
3. Configure options in clean dialog
4. Click "Export"

The process is nearly identical, just cleaner in WhoDB.

### Task: Writing a Query

**In phpMyAdmin:**
1. Go to "SQL" tab
2. Type your query
3. Click "Go"
4. Results appear below

**In WhoDB:**
1. Go to "Scratchpad" view
2. Type your query
3. Click "Run" or Cmd/Ctrl+Enter
4. Results appear below

**Advantage in WhoDB**: Multiple cells allow you to organize related queries and run them independently.

## Keyboard Shortcuts: Your Quick Reference

### Shortcuts You Already Know

| Action | phpMyAdmin | WhoDB |
|--------|-----------|-------|
| Copy | Cmd/Ctrl+C | Cmd/Ctrl+C |
| Paste | Cmd/Ctrl+V | Cmd/Ctrl+V |
| Find | Cmd/Ctrl+F | Cmd/Ctrl+F |
| Save | Cmd/Ctrl+S | Cmd/Ctrl+S |

### New Shortcuts to Master

| Action | WhoDB Shortcut |
|--------|---------------|
| Run Query | Cmd/Ctrl+Enter |
| New Query Cell | Cmd/Ctrl+N |
| Duplicate Cell | Cmd/Ctrl+D |
| Toggle Sidebar | Cmd/Ctrl+/ |
| Format Query | Cmd/Ctrl+Shift+F |
| Select All Rows | Cmd/Ctrl+A |
| Quick Navigation | Cmd/Ctrl+K |

WhoDB's shortcuts are designed for developers who already use modern code editors. They'll feel immediately familiar.

## First-Time Setup in WhoDB

### Connection Profiles

**In phpMyAdmin**, you typically set up multiple servers in `config.inc.php`.

**In WhoDB**, you can save connection profiles without any configuration files:

On the first login screen, enter your database details (host, port, username, password)

Click "Connect" to establish connection

WhoDB immediately shows your database structure in the sidebar

You can create multiple profiles by reconnecting with different credentials

WhoDB doesn't store credentials by default (more secure), but you can create and save connection profiles for easy reconnection.

### Navigating Your First Database

![Storage Unit List](/images/05-storage-unit-list.png)

When you first connect, you'll see:
- **Sidebar**: Your complete database structure
- **Main Area**: List of tables
- **Top Tabs**: Data, Explore, Graph, Scratchpad

Click any table to start exploring. No separate "browse" step needed.

## Understanding WhoDB's Views

Unlike phpMyAdmin's tab-based system, WhoDB organizes features into distinct views:

Browse, edit, filter, and sort your table data with modern UX

See table structure, columns, indexes, constraints, and metadata

Visualize your database schema and relationships interactively

Write and execute SQL queries with multi-cell support

Each view is focused on a specific task, making the interface cleaner than phpMyAdmin's multiple tabs.

## Common phpMyAdmin Tasks in WhoDB

**In WhoDB:**
1. Go to Explore view
2. Find the table
3. Click "Add Column" button
4. Fill in column details (name, type, constraints)
5. Click "Create"

This is cleaner than phpMyAdmin's complex structure modification dialogs.

**In WhoDB:**
1. Use Scratchpad to write CREATE TABLE statement
2. Execute the query
3. Table appears instantly in sidebar

Alternatively, use the UI to create tables (coming in future updates).

**Tip**: This is one area where phpMyAdmin's UI table builder is faster. We're working on improving this in WhoDB.

**In WhoDB:**
Currently, stored procedures are shown in Explore view but must be edited via queries in Scratchpad.

We're adding visual stored procedure editing in upcoming releases.

**In WhoDB:**
1. Open Scratchpad
2. Run privilege checking queries:
   - PostgreSQL: `SELECT * FROM information_schema.table_privileges;`
   - MySQL: `SELECT * FROM mysql.user;`
3. Results appear in grid

This requires more SQL knowledge than phpMyAdmin's UI, but it's more transparent.

**In WhoDB:**
1. Go to Data view or Scratchpad
2. Export entire table with "Export All" as SQL format
3. Save file as backup

For full database backups, use command-line tools:
```
# PostgreSQL
pg_dump dbname > backup.sql

# MySQL
mysqldump -u user -p database > backup.sql
```

WhoDB's export feature is great for quick data backups, but command-line tools are better for full database administration.

## What's Different (and Why It's Better)

### No "Structure" Tab Confusion

phpMyAdmin separates data browsing from structure viewing. WhoDB has an **"Explore"** view that shows everything in one intuitive place:
- Column names and types
- Primary keys and indexes
- Constraints and relationships
- All without SQL knowledge

### Instant Multi-Database Access

Instead of selecting from a dropdown, WhoDB's **sidebar shows your entire database structure** at once. Expand schemas, click any table, and view data instantly.

![Storage Unit List With Sidebar](/images/06-storage-unit-list-with-sidebar.png)

### Better Query Organization

phpMyAdmin runs one query at a time. WhoDB's **Scratchpad** lets you write multiple related queries in different cells and run them independently. Perfect for complex workflows.

![Scratchpad Main View](/images/27-scratchpad-main-view.png)

### Visual Relationships

phpMyAdmin requires you to mentally parse foreign keys from `information_schema`. WhoDB's **Graph view** visualizes your entire schema:

![Graph View Schema Topology](/images/24-graph-view-schema-topology.png)

### Smart Filtering Without SQL

phpMyAdmin forces you to write WHERE clauses for filtering. WhoDB's visual filter builder lets you:
- Select field
- Choose operator (=, >, \<, like, etc.)
- Enter value
- Filter applies instantly

Perfect for non-SQL developers and quick ad-hoc queries.

## Phasing Out phpMyAdmin

### Week 1: Parallel Usage
- Keep phpMyAdmin running
- Use WhoDB for all new tasks
- Reference phpMyAdmin when you need something familiar

### Week 2: Explore Advanced Features
- Try Graph visualization
- Practice multi-cell Scratchpad queries
- Experiment with filtering

### Week 3: Go All-In
- Fully switch to WhoDB
- Delete phpMyAdmin from your stack
- Enjoy the simpler, cleaner interface

Most users find WhoDB more intuitive within just a few days. Your muscle memory will switch quickly.

## Troubleshooting Common Transition Issues

**Solution**: Look in the **Explore view** rather than a separate "Structure" tab. Click the table in your sidebar, then click the "Explore" tab to see all table details including columns, indexes, and constraints.

**Solution**: WhoDB displays key stats in the Explore view. For detailed statistics, use the Scratchpad to query system tables:
```
-- PostgreSQL
SELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename))
FROM pg_tables WHERE schemaname != 'information_schema';
```

**Solution**: Write queries in the Scratchpad. They're automatically saved to history, which you can access by clicking the "History" button. You can also save query cells and refer back to them.

**Solution**: Click the collapse arrow next to schema names to hide table lists. Use Cmd/Ctrl+/ to toggle the sidebar visibility entirely. You can customize what's visible.

**Solution**: Create separate browser tabs with different connections. WhoDB supports managing multiple connections simultaneously. Each tab maintains its own session.

## Advanced Features You'll Love

These are things phpMyAdmin doesn't have that will improve your workflow:

Generate realistic test data instantly for development and testing

Auto-format your queries for readability with Cmd/Ctrl+Shift+F

All your past queries are automatically saved and searchable

Edit cell values directly in the table without opening a dialog

Real-time search highlighting across your entire data view

Export selected rows, filtered results, or entire tables in seconds

## Quick Migration Checklist

Follow the [Installation Guide](/installation) to get WhoDB running

Use the [First Database Connection Guide](/guides/tutorials/first-database-connection)

Click tables in the sidebar to view familiar data layouts

Test the visual filter builder to replace WHERE clause writing

Click "Graph" to visualize your database structure

Switch to Scratchpad and write your first multi-cell query

Use the export feature to verify it works with your formats

Migrate your entire workflow and retire phpMyAdmin

## Getting Help During Migration

If you get stuck during migration, don't revert to phpMyAdmin immediately. Try exploring the WhoDB interface for just 10 more minutes—you'll usually find what you're looking for.

**Resources:**
- [Complete Beginner's Guide](/guides/complete-beginner) - Start from scratch
- [Quick Start Guide](/quick-start) - 5-minute overview
- [Features Documentation](/features/database-connectivity) - Detailed feature guides
- [FAQ](/resources/faq) - Common questions

## Final Words

The jump from phpMyAdmin to WhoDB is easier than you think. Within a week, you'll be more productive. Within a month, you won't even remember phpMyAdmin's interface. The modern design, powerful features, and developer-friendly workflow are worth the small learning curve.

**Welcome to WhoDB. You've made a great choice.**

Ready to make the switch? Start with the [Installation Guide](/installation) and get WhoDB running in minutes.

---


# guides/migrating-from-pgadmin.mdx
## pgAdmin Alternative: Migrate to WhoDB

Better than pgAdmin. Switch to WhoDB for faster, simpler PostgreSQL management. Feature comparison, workflow mapping, and migration guide.

# Migrating from pgAdmin to WhoDB

pgAdmin is the industry standard for PostgreSQL management, and for good reason. But WhoDB takes the best ideas from pgAdmin and reimagines them for modern workflows. This guide will help you transition smoothly and discover why so many PostgreSQL developers are switching.

WhoDB supports PostgreSQL as a first-class citizen while also supporting MySQL, SQLite, MongoDB, and Redis. Plus, it's significantly faster and more intuitive than pgAdmin.

## Why Switch from pgAdmin to WhoDB?

pgAdmin is powerful but complex. WhoDB provides the features you need with half the complexity:

Lighter interface that loads instantly and remains responsive

Find what you need in seconds instead of navigating nested menus

Modern, single-purpose interfaces instead of overwhelming modal windows

Manage PostgreSQL, MySQL, SQLite, MongoDB, and Redis simultaneously

Handle large datasets smoothly without UI lag

Keyboard shortcuts, query history, syntax highlighting, multi-cell queries

## Feature Comparison: pgAdmin vs WhoDB

| Feature | pgAdmin | WhoDB |
|---------|--------|-------|
| Add/Remove Servers | Complex hierarchy | Simple connection profiles |
| Server Properties | Deep menu diving | Connection form |
| Server Groups | Yes, nested | Yes, simplified |
| Connection Pooling | Yes, complex config | Yes, simpler settings |
| SSL Configuration | Yes, many options | Yes, focused options |
| SSH Tunnels | Yes | Yes |
| Connection Testing | Built-in | Automatic on connect |
| Quick Reconnect | Multiple steps | One click |

| Feature | pgAdmin | WhoDB |
|--------|--------|-------|
| Database List | Tree view (nested) | Sidebar (flat, expandable) |
| Schema Visibility | Drill down through menus | Expand/collapse in sidebar |
| Table Browsing | Multiple nested levels | Flat sidebar structure |
| Quick Search | Limited | Ctrl+K navigation |
| Favorite Bookmarks | Dashboard | Not needed (sidebar always visible) |
| Database Switching | Navigate tree | Click in sidebar |
| Simultaneous Connections | Yes, tab-based | Yes, tab-based |

| Feature | pgAdmin | WhoDB |
|--------|--------|-------|
| Data Grid | Basic | Modern, fast |
| Sorting | Click header | Click header |
| Filtering | SQL-based WHERE | Visual query builder |
| Search Data | Row editing | Search highlighting |
| Pagination | Limited | Flexible sizes |
| Cell Editing | Inline | Inline + dialog |
| Row Selection | Single | Multi-select |
| Copy Values | Manual | Context menu |
| Export Selected | Complex | One click |

| Feature | pgAdmin | WhoDB |
|--------|--------|-------|
| Query Editor | Basic editor | Modern scratchpad |
| Syntax Highlighting | Yes | Yes, better colors |
| Query History | Server logs | Built-in searchable history |
| Saved Queries | File-based | Browser-based history |
| Multiple Queries | One at a time | Multi-cell scratchpad |
| Auto-Completion | Basic | Advanced with schema context |
| Query Formatting | Manual | Auto-format available |
| Execution Plan | EXPLAIN available | Yes, cleaner display |
| Result Set Navigation | Basic | Intuitive pagination |

| Feature | pgAdmin | WhoDB |
|--------|--------|-------|
| View Schema Objects | Deep menu navigation | Sidebar explorer |
| Create Objects | Complex wizards | Simple dialogs |
| Edit Definitions | Property dialogs | SQL-based or forms |
| Dependencies View | Yes, complex | Relationships in graph |
| Object Properties | Many tabs | Organized sections |
| Mass Operations | Limited | Better selection |

| Feature | pgAdmin | WhoDB |
|--------|--------|-------|
| ER Diagrams | Plugin (pgAdmin4) | Built-in interactive graph |
| Relationship Display | Static diagram | Interactive graph |
| Constraint Visualization | Text-based | Visual connections |
| Schema Complexity View | Confusing | Clear interactive layout |
| Export Diagrams | Limited | Built-in |

| Feature | pgAdmin | WhoDB |
|--------|--------|-------|
| Add Rows | Complex dialogs | Simple modal |
| Edit Rows | Inline or dialog | Both options available |
| Delete Rows | Confirmation required | Confirmed deletion |
| Bulk Operations | Limited | Multi-select capabilities |
| Data Validation | Basic | Enhanced validation |
| Foreign Key Handling | Shows constraints | Visual in graph |

| Feature | pgAdmin | WhoDB |
|--------|--------|-------|
| Database Backup | Built-in tool | Export as SQL |
| Restore Data | Restore tool | SQL import (future) |
| Maintenance Tasks | VACUUM, ANALYZE buttons | Query-based |
| Bloat Analysis | Built-in | Via query |

## Workflow Mapping: Your pgAdmin Tasks in WhoDB

### Task: Connecting to a Server

**In pgAdmin:**
1. Click "Servers" in left tree
2. Right-click → "Register Server"
3. Fill complex registration form
4. Navigate to database → schema → table

**In WhoDB:**
1. Enter host, port, username, password
2. Click "Connect"
3. Sidebar shows everything instantly

WhoDB's connection process is 80% faster than pgAdmin's registration and navigation steps.

### Task: Viewing Table Data

**In pgAdmin:**
1. Server → Database → Schema → Table (4 clicks)
2. Right-click table → "View/Edit Data" → "All Rows"
3. Wait for data grid to load

**In WhoDB:**
1. Click table in sidebar (1 click)
2. Data appears immediately

![Storage Unit List With Sidebar](/images/06-storage-unit-list-with-sidebar.png)

### Task: Filtering Data

**In pgAdmin:**
1. Right-click table → "View/Edit Data"
2. Click "Filter" button
3. Write SQL WHERE clause
4. Results update

**In WhoDB:**
1. Click filter icon
2. Select field → operator → value
3. Filter applies instantly

The visual approach is much faster for ad-hoc filtering.

![Where Conditions Popover](/images/16-data-view-where-conditions-popover.png)

### Task: Running a Query

**In pgAdmin:**
1. Tools → Query Tool (or Ctrl+Alt+Q)
2. Type query
3. Execute
4. Results in panel below

**In WhoDB:**
1. Click "Scratchpad" view
2. Type query
3. Run with Cmd/Ctrl+Enter
4. Results appear below

**WhoDB Advantage**: Multiple cells let you organize related queries and execute them independently.

![Scratchpad Main View](/images/27-scratchpad-main-view.png)

### Task: Viewing Relationships

**In pgAdmin:**
1. Requires Graphviz plugin
2. Tools → Debugger → ER Diagram
3. Complex setup for visualization

**In WhoDB:**
1. Click "Graph" view
2. Interactive visualization loads instantly
3. Click any node for details

![Graph View Schema Topology](/images/24-graph-view-schema-topology.png)

WhoDB's graph visualization is built-in and interactive. No plugins required. Click nodes to explore relationships instantly.

### Task: Exploring Table Structure

**In pgAdmin:**
1. Navigate table → Properties tab
2. Explore properties across multiple tabs
3. Check columns, constraints, indexes separately

**In WhoDB:**
1. Click table → "Explore" view
2. All structure info in one clean interface
3. Scroll to see everything

Much simpler without pgAdmin's multi-tab chaos.

![Explore Table Metadata](/images/08-explore-table-metadata.png)

## Interface Differences Explained

### pgAdmin's Tree Navigation vs WhoDB's Sidebar

**pgAdmin** uses a deep tree structure:
```
Servers
  └─ My Server
     └─ Databases
        └─ postgres
           └─ Schemas
              └─ public
                 └─ Tables
                    └─ users
```

**WhoDB** uses a flat, expandable sidebar:
```
Database: postgres
  Schemas
    └─ public
       └─ users (click to view data)
```

The WhoDB approach is faster and less disorienting.

### Modal Dialogs vs Single-Purpose Views

**pgAdmin** opens complex property dialogs with many tabs for every action (creating tables, editing columns, etc.).

**WhoDB** has dedicated views for each task:
- **Data View** for browsing/editing records
- **Explore View** for schema structure
- **Graph View** for relationships
- **Scratchpad View** for queries

Each view is focused and lightweight.

## Keyboard Shortcuts: pgAdmin Habits in WhoDB

### Shortcuts That Transferred

| Action | pgAdmin | WhoDB |
|--------|--------|-------|
| Execute Query | F5 or Cmd/Ctrl+Enter | Cmd/Ctrl+Enter |
| Copy | Cmd/Ctrl+C | Cmd/Ctrl+C |
| Paste | Cmd/Ctrl+V | Cmd/Ctrl+V |
| Find | Cmd/Ctrl+F | Cmd/Ctrl+F |
| Select All | Cmd/Ctrl+A | Cmd/Ctrl+A |

### New Shortcuts to Learn

| Action | WhoDB Shortcut |
|--------|---------------|
| Quick Navigation | Cmd/Ctrl+K |
| Toggle Sidebar | Cmd/Ctrl+/ |
| New Query Cell | Cmd/Ctrl+N |
| Duplicate Cell | Cmd/Ctrl+D |
| Format Query | Cmd/Ctrl+Shift+F |

WhoDB's keyboard shortcuts are fewer and more focused. You'll master them quickly.

## First-Time Setup in WhoDB

### Creating Your Connection

Navigate to WhoDB's login interface

- Host: your_server.example.com
- Port: 5432 (default)
- Username: your_username
- Password: your_password
- Database: postgres (or your target database)

WhoDB connects immediately

Sidebar shows your complete database structure

WhoDB doesn't require complex server registration like pgAdmin. Just fill in the connection details and you're ready.

### Understanding WhoDB's Interface

When you connect, you'll see:

- **Sidebar**: Your complete database structure (expandable)
- **Main Area**: Tables list or specific table data
- **Top Tabs**: Data, Explore, Graph, Scratchpad
- **Action Bar**: Context-specific tools

Click any table in the sidebar to start exploring instantly.

![Explore Users Table](/images/07-explore-users-table.png)

## Common pgAdmin Tasks in WhoDB

**In WhoDB:**
1. Open Scratchpad view
2. Write CREATE TABLE statement:
```
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  email VARCHAR(100) UNIQUE,
  created_at TIMESTAMP DEFAULT NOW()
);
```
3. Execute query
4. Table appears in sidebar

**Note**: Visual table creation UI is coming in a future update. For now, SQL is the most reliable method.

**In WhoDB:**
1. Go to Scratchpad
2. Write ALTER TABLE statement:
```
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
```
3. Execute
4. Refresh Explore view to see new column

Alternatively, use Explore view's "Add Column" button (if available).

**In WhoDB:**
1. Click table name
2. Click "Explore" view tab
3. Scroll to "Indexes" section
4. See all indexes with their columns

Much simpler than pgAdmin's property tabs.

**In WhoDB:**
1. Open Scratchpad
2. Add EXPLAIN ANALYZE before your query:
```
EXPLAIN ANALYZE
SELECT users.id, COUNT(posts.id)
FROM users
LEFT JOIN posts ON users.id = posts.user_id
GROUP BY users.id;
```
3. Execute to see query plan
4. Results show execution time and node costs

WhoDB displays the same information as pgAdmin but in a cleaner format.

**In WhoDB:**
1. Select all rows from a table (Cmd/Ctrl+A)
2. Click "Export"
3. Choose "SQL" format
4. Click "Export" to download SQL INSERT statements

For full database backups, use pg_dump from command line:
```
pg_dump -U username -h hostname database_name > backup.sql
```

WhoDB's export is perfect for table-level backups but can't replace pg_dump for full database administration.

**In WhoDB:**
1. Open Scratchpad
2. Run query:
```
SELECT
  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;
```
3. See table sizes in results

pgAdmin has a built-in tool, but WhoDB's query approach is more flexible and transparent.

**In WhoDB:**
1. Open Scratchpad
2. Run query:
```
SELECT pid, usename, application_name, state, query
FROM pg_stat_activity
WHERE state != 'idle';
```
3. View active queries and connections

WhoDB makes you write the query, which actually teaches you more about PostgreSQL internals.

**In WhoDB:**
1. Open Scratchpad
2. Write GRANT statement:
```
GRANT SELECT, INSERT, UPDATE ON public.users TO app_user;
GRANT USAGE ON SCHEMA public TO app_user;
```
3. Execute to apply permissions

For complex permission management, you might still use pgAdmin, but WhoDB works perfectly for this.

## What's Different (and Why It's Better)

### No Plugin Dependencies

pgAdmin's ER diagram requires Graphviz. WhoDB's graph visualization is built-in and interactive.

**pgAdmin Workflow:**
1. Install Graphviz (complex on some systems)
2. Configure pgAdmin to find Graphviz
3. Generate ER diagram (static image)

**WhoDB Workflow:**
1. Click "Graph" tab
2. Interactive visualization loads instantly
3. Click nodes to explore

### Faster Data Exploration

pgAdmin's nested menus create unnecessary clicking. WhoDB's sidebar puts everything at your fingertips:

| Task | pgAdmin Clicks | WhoDB Clicks |
|------|---------------|------------|
| View table data | 4+ | 1 |
| Filter records | 3 | 2 |
| See table structure | 3 | 2 |
| Run query | 2 | 1 |

### Multi-Tab Connection Management

Both support multiple connections, but WhoDB's approach is simpler:

**pgAdmin**: Manage multiple server registrations with properties and folders
**WhoDB**: Open new browser tabs with different connections

### Better for Multi-Database Workflows

If you use PostgreSQL AND MySQL or SQLite:

**pgAdmin**: Limited to PostgreSQL only (or community versions for others)
**WhoDB**: Manage all databases from one interface

## Advanced Features You'll Appreciate

These capabilities go beyond what pgAdmin offers:

Generate realistic test data instantly for development

Organize related queries in different cells, execute independently

Visual query builder instead of SQL WHERE clauses

Highlight matching values as you type

Edit values directly in the grid without dialogs

Export filtered results, selected rows, or entire tables

## Migration Timeline

### Week 1: Explore WhoDB
- Install and connect to your PostgreSQL database
- Browse a few tables
- Try the Graph view
- Run a simple query in Scratchpad

### Week 2: Start Using Daily
- Use WhoDB for all new data exploration
- Practice filtering and sorting
- Write multi-cell queries in Scratchpad
- Try exporting data

### Week 3: Go All-In
- Fully switch to WhoDB for daily work
- Keep pgAdmin only as backup for complex admin tasks
- Discover WhoDB-specific workflows

### Month 2: Retire pgAdmin
- Realize you haven't opened pgAdmin in weeks
- Remove it from your toolset
- Enjoy the simpler, faster workflow

Most pgAdmin users find WhoDB equally capable within 2 weeks. The learning curve is minimal if you know PostgreSQL already.

## Troubleshooting Common Transition Issues

**Solution**: Click the table → "Explore" view. All properties are displayed in organized sections without overwhelming dialogs.

**Solution**: Use Scratchpad to query pg_catalog tables:
```
SELECT usename, usesuper, usecreatedb, usecanlogin
FROM pg_user;
```

**Solution**: WhoDB doesn't have a scheduler like pgAdmin. Use pgAdmin or cron jobs for VACUUM, ANALYZE, and backups. WhoDB is best for interactive queries.

**Solution**: Use Cmd/Ctrl+K for quick navigation. Search for table names instead of scrolling.

**Solution**: Use EXPLAIN ANALYZE in Scratchpad:
```
EXPLAIN ANALYZE
SELECT ...;
```
Results show the same execution plan as pgAdmin's EXPLAIN tool.

## When to Still Use pgAdmin

WhoDB is excellent for 95% of PostgreSQL management, but pgAdmin is still better for:

- Complex backup and restore operations
- Database server administration (not just data)
- Automated maintenance scheduling
- Advanced permission management UI
- Managing multiple servers across enterprises

For daily data exploration, testing, and queries? WhoDB is superior.

## Quick Migration Checklist

Follow the [Installation Guide](/installation)

Use the [First Database Connection Guide](/guides/tutorials/first-database-connection)

Expand schemas and click tables in the sidebar

Test the visual filter builder

Click "Graph" to see your schema visually

Write your first multi-cell query in Scratchpad

Try editing rows, exporting data, running EXPLAIN

Keep it handy but use WhoDB for daily work

## Getting Help During Transition

If you can't find a feature, try the Scratchpad. Most pgAdmin functions can be replicated with SQL queries in WhoDB.

**Resources:**
- [Complete Beginner's Guide](/guides/complete-beginner) - Learn from scratch
- [Quick Start Guide](/quick-start) - 5-minute overview
- [Data Management Guide](/data/viewing-data) - Explore and edit data
- [Query Guide](/query/scratchpad-intro) - Master the Scratchpad
- [FAQ](/resources/faq) - Common questions

## Final Words

Switching from pgAdmin to WhoDB is one of the best decisions you can make. You'll work faster, enjoy a cleaner interface, and gain access to modern features pgAdmin doesn't have. The PostgreSQL-specific knowledge you have will transfer perfectly—WhoDB just makes it easier to apply.

**Welcome to WhoDB. Your database management just got simpler.**

Ready to switch? Start with the [Installation Guide](/installation) and get up and running in minutes.

---


# guides/team-setup.mdx
## Team Database Management with WhoDB

Set up WhoDB for your team. Deploy with Docker, manage connections, implement read-only access, and enable collaboration on database projects.

# Team Setup Guide for WhoDB

Managing databases across a team requires the right tools and proper workflows. WhoDB makes it simple to give your entire team secure, intuitive database access. This guide walks you through setting up WhoDB for your team, from initial deployment to establishing best practices.

Whether you're managing a 3-person startup or a 300-person organization, WhoDB scales to support your team's database needs.

## Why WhoDB for Teams?

All team members use the same tool regardless of database type

Deploy once, team accesses via browser—no individual setups

Grant read-only, editing, or admin access per user

Save multiple database connections for easy team access

Query history and actions are logged for compliance

SSL encryption, SSH tunnels, and credential isolation

## Deployment Options

### Option 1: Docker Deployment (Recommended for Teams)

Docker is the simplest way to deploy WhoDB for your team. Everyone connects to a single instance that handles all databases.

**Prerequisites:**
- Docker and Docker Compose installed
- Server with at least 2GB RAM
- Network access from your team

**Step 1: Create docker-compose.yml**

```
version: '3.8'

services:
  whodb:
    image: clidey/whodb:latest
    container_name: whodb
    ports:
      - "3000:3000"
    environment:
      # Optional: Configure default database
      DB_HOST: ${DB_HOST:-localhost}
      DB_PORT: ${DB_PORT:-5432}
      DB_USER: ${DB_USER}
      DB_PASSWORD: ${DB_PASSWORD}
      DB_NAME: ${DB_NAME}
    volumes:
      # Optional: Persist query history
      - whodb_data:/app/data
    restart: unless-stopped
    networks:
      - whodb_network

  # Optional: PostgreSQL for testing
  postgres:
    image: postgres:15-alpine
    container_name: postgres_demo
    environment:
      POSTGRES_PASSWORD: demo_password
      POSTGRES_DB: demo_db
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - whodb_network
    # Only expose if needed for local testing
    # ports:
    #   - "5432:5432"

volumes:
  whodb_data:
  postgres_data:

networks:
  whodb_network:
    driver: bridge
```

**Step 2: Deploy**

```
# Copy the above YAML to docker-compose.yml
# Start the services
docker-compose up -d

# Verify it's running
docker-compose logs -f whodb

# Check status
docker ps | grep whodb
```

**Step 3: Access WhoDB**

Navigate to `http://your_server:3000` in your browser. WhoDB is now accessible to your team.

Use `http://localhost:3000` for local testing, or replace with your server's IP/hostname for team access.

### Option 2: Kubernetes Deployment (Enterprise)

For large teams with Kubernetes infrastructure:

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: whodb
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: whodb
  template:
    metadata:
      labels:
        app: whodb
    spec:
      containers:
      - name: whodb
        image: clidey/whodb:latest
        ports:
        - containerPort: 3000
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "1Gi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 30
          periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: whodb-service
spec:
  selector:
    app: whodb
  ports:
  - protocol: TCP
    port: 80
    targetPort: 3000
  type: LoadBalancer
```

Deploy with:
```
kubectl apply -f whodb-deployment.yaml
```

### Option 3: Traditional Server Deployment

For teams without Docker or Kubernetes:

**Download and Install:**
```
# Download the latest WhoDB binary
wget https://github.com/clidey/whodb/releases/latest/download/whodb-server

chmod +x whodb-server

# Run as background service
./whodb-server &

# Or use systemd
sudo systemctl start whodb
```

## Securing Your Team Instance

### Enable HTTPS/SSL

Never expose WhoDB over HTTP in production. Always use HTTPS/SSL encryption.

**With Docker and Nginx Reverse Proxy:**

```
# Use Let's Encrypt for free SSL certificates
sudo apt-get install certbot nginx

# Get certificate (replace yourdomain.com with your domain)
sudo certbot certonly --standalone -d yourdomain.com

# Configure Nginx as reverse proxy
sudo nano /etc/nginx/sites-available/whodb
```

```
server {
    listen 443 ssl http2;
    server_name yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name yourdomain.com;
    return 301 https://$server_name$request_uri;
}
```

Enable and restart:
```
sudo ln -s /etc/nginx/sites-available/whodb /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
```

### Network Security

Only allow trusted IPs to access WhoDB:
```
# UFW example (Ubuntu)
sudo ufw allow 443/tcp
sudo ufw allow 80/tcp (redirects to 443)
sudo ufw enable

# Restrict to specific IPs if possible
sudo ufw allow from 203.0.113.0/24 to any port 443
```

If possible, require team members to connect via VPN before accessing WhoDB. This adds an extra security layer.

Configure WhoDB to log all connections and queries for security auditing.

Change database credentials monthly or after team member departures.

## Managing Database Connections for Your Team

### Creating Shared Connection Profiles

Instead of giving team members raw credentials, create connection profiles:

Create a document with all connection details your team needs:
- Development database
- Staging database
- Production database (if applicable)
- Data warehouse
- Analytics database

Example:
```
Development PostgreSQL
  Host: dev-db.internal
  Port: 5432
  Database: myapp_dev
  Username: dev_user (read-write)

Production PostgreSQL
  Host: prod-db.internal
  Port: 5432
  Database: myapp_prod
  Username: prod_readonly (read-only)
```

For each database, create dedicated read-only users:

```
-- PostgreSQL: Create read-only user
CREATE USER whodb_readonly WITH PASSWORD 'secure_random_password';
GRANT CONNECT ON DATABASE myapp_dev TO whodb_readonly;
GRANT USAGE ON SCHEMA public TO whodb_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO whodb_readonly;

-- Apply to future tables
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO whodb_readonly;
```

```
-- MySQL: Create read-only user
CREATE USER 'whodb_readonly'@'%' IDENTIFIED BY 'secure_random_password';
GRANT SELECT ON myapp_dev.* TO 'whodb_readonly'@'%';
FLUSH PRIVILEGES;
```

Create a reference document for your team:
```
Development Connection (Read-Only)
Host: dev-db.internal
Port: 5432
Username: whodb_readonly
Password: [Store in your password manager]
Database: myapp_dev
```

Distribute credentials via your password manager, not email or Slack.

### Managing Multiple Database Access Levels

**Production Environment:**

```
Level 1: Analysts (Read-Only)
- Can view all data
- Cannot modify or delete
- Cannot access sensitive tables

Level 2: Developers (Read-Write)
- Can view and edit data
- Can run queries
- Cannot drop tables

Level 3: DBAs (Admin)
- Full access
- Can modify schema
- Can backup/restore
```

**Implementation:**

```
-- PostgreSQL example: Create three access levels

-- Level 1: Read-only
CREATE USER analyst_user WITH PASSWORD 'analyst_pass';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO analyst_user;

-- Level 2: Read-write
CREATE USER developer_user WITH PASSWORD 'dev_pass';
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO developer_user;

-- Level 3: Admin (superuser or high privileges)
CREATE USER dba_user WITH SUPERUSER PASSWORD 'dba_pass';
```

## Workflow Templates for Teams

### Template 1: Daily Data Review

**Scenario**: Your team reviews key metrics daily

Developer opens WhoDB, connects to analytics database

Write queries for key metrics:
- User signups yesterday
- Feature usage stats
- Error rate trends
Save in Scratchpad for team reference

Export results as CSV for team meeting or Slack

Share queries in documentation for consistency

### Template 2: QA Data Preparation

**Scenario**: QA team needs consistent test data

Use WhoDB to export production data (anonymized) to CSV

Use WhoDB's mock data feature to fill gaps with realistic data

Use Scratchpad to load data: `INSERT INTO table VALUES ...`

Create database snapshot for all QA team members to use

### Template 3: Debugging Production Issues

**Scenario**: Customer reports a bug, team needs to investigate

QA/Support team connects with read-only credentials to production

Write query in Scratchpad to reproduce the problem

Export results and attach to bug ticket

Engineers get full query for confirmation and fix

## Best Practices for Team Collaboration

### Documentation

Create a WhoDB team guide document covering:
- How to connect to each database
- Common queries your team uses
- What data is production vs. test
- Who to ask for help

Example structure:
```
# WhoDB Team Guide

## Connections
- Production (Read-Only): connection details
- Staging (Read-Write): connection details
- Development (Full Access): connection details

## Common Queries
- Daily active users: [query]
- Recent errors: [query]
- Revenue trends: [query]

## Security
- Never share credentials
- Always use connection profiles
- Log out when done
- Report suspicious activity
```

### Access Control Rules

Establish clear policies to prevent data breaches:
1. Never store production credentials in version control
2. Use strong, unique passwords for each database user
3. Create separate users for different access levels
4. Rotate credentials when team members leave
5. Audit query history regularly

### Team Onboarding Checklist

When a new team member joins:

Create restricted database user with appropriate access level

Give them the team documentation

Help them connect to development database first

Show them how to view data, filter, and run basic queries

Show scratchpad, graph visualization, export options

Add production access only when they're ready

### Team Member Offboarding

When a team member leaves:

Immediately disable their database accounts

Review their query history for security

Assign their saved queries to team lead

Remove from any permission groups

Change passwords they had access to

## Monitoring and Maintenance

### Regular Backups

Ensure your team's work isn't lost:

```
# Daily backup script (cron job)
#!/bin/bash
BACKUP_DIR="/backups/whodb"
DATE=$(date +%Y%m%d_%H%M%S)

# Backup all databases
for db in $DATABASES; do
  pg_dump -U $DB_USER -h $DB_HOST $db > $BACKUP_DIR/$db\_$DATE.sql
done

# Keep only last 30 days
find $BACKUP_DIR -name "*.sql" -mtime +30 -delete
```

Set up in crontab:
```
# Run backup daily at 2 AM
0 2 * * * /usr/local/bin/backup-databases.sh
```

### Performance Monitoring

Monitor WhoDB instance health:

```
# Check system resources
docker stats whodb

# Check error logs
docker logs whodb | grep -i error

# Monitor database connections
# In WhoDB Scratchpad:
SELECT count(*) FROM pg_stat_activity;
```

### Upgrading WhoDB

Keep your instance updated:

```
# Pull latest image
docker pull clidey/whodb:latest

# Stop current instance
docker-compose down

# Update compose file (latest tag)
docker-compose up -d

# Verify it's running
docker ps | grep whodb
```

## Troubleshooting Team Issues

**Diagnosis:**
1. Verify user exists in database
2. Check password is correct
3. Confirm host/port are correct
4. Verify firewall allows connection

**Solution:**
1. Double-check credentials
2. Test connection from command line first
3. Check database server logs
4. Verify user has CONNECT privilege

**Symptoms:** Team members report slow data loading

**Solutions:**
1. Check database server load: `top`, `htop`
2. Analyze slow queries: `EXPLAIN ANALYZE`
3. Verify indexes exist on frequently queried columns
4. Add connection pooling
5. Consider database optimization or scaling

**Diagnosis:**
1. User account doesn't have VIEW permission on schema
2. Tables are in different schema they can't access

**Solution:**
```
-- PostgreSQL: Grant schema access
GRANT USAGE ON SCHEMA public TO username;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO username;
```

```
-- MySQL: Grant database access
GRANT SELECT ON myapp.* TO 'username'@'%';
```

**Process:**
1. Locate backup file
2. Restore using database tools:

```
# PostgreSQL
psql -U postgres -d myapp_prod < backup_20240115.sql

# MySQL
mysql -u root -p myapp_prod < backup_20240115.sql
```

3. Verify data integrity
4. Notify team of restoration

**Immediate Actions:**
1. Review query history: which user ran it, what did it access
2. Check recent connections: who accessed production
3. Disable suspicious user account
4. Rotate production database credentials
5. Audit all recent queries

## Advanced: Single Sign-On (SSO) Integration

For larger teams, integrate with your identity provider:

SSO integration is a future feature. For now, use strong, managed passwords with your team's password manager.

**Coming Soon:** WhoDB will support:
- LDAP/Active Directory
- OAuth2 (Google, GitHub, etc.)
- SAML for enterprise
- API key authentication

## Security Checklist for Production Teams

All team connections encrypted with SSL/TLS

Restrict WhoDB access to team networks/VPN

Enforce complex passwords for database users

Give read-only where possible, full access only when needed

Log and review all queries regularly

Change database passwords monthly

Automated daily backups with offsite storage

Quarterly review of who has access to what

## Scaling as Your Team Grows

### Team of 3-10

- Single WhoDB instance
- Shared development database
- Read-only production access
- Basic documentation

### Team of 10-50

- Dedicated WhoDB instance (Docker)
- Multiple databases (dev, staging, prod)
- Role-based access control
- Regular backups
- Query auditing

### Team of 50+

- High-availability setup (multiple instances)
- Kubernetes deployment
- Database connection pooling
- Advanced monitoring and alerting
- Compliance and audit logging
- Dedicated database admin support

## Cost Considerations

| Component | Cost | Notes |
|-----------|------|-------|
| WhoDB | Free (Open Source) | No licensing costs |
| Server/VPS | $10-100/month | Depends on team size |
| SSL Certificate | Free (Let's Encrypt) | Annual renewal |
| Backup Storage | $5-50/month | Varies by data size |
| Database Licenses | $0-$500+/month | Many databases free (PostgreSQL, MySQL) |
| **Total** | **$15-650/month** | **Way cheaper than traditional tools** |

## Getting Help for Team Setup

Setting up WhoDB for your team is straightforward, but if you need guidance:
- Check the [Installation Guide](/installation)
- Review [Database Connectivity](/features/database-connectivity)
- Read [Security Best Practices](/best-practices/security)

## Summary: Your Team is Ready

After completing this guide, your team has:

✓ WhoDB deployed securely
✓ Read-only and read-write access configured
✓ Documentation and best practices established
✓ Onboarding process ready
✓ Backup and monitoring in place

**Your team can now safely and efficiently manage your databases together.**

Your team is ready to start using WhoDB. Onboard team members using the checklist above, and they'll be productive immediately.

---


# guides/tutorials/first-database-connection.mdx
## Your First Database Connection

Step-by-step tutorial for connecting to your first database with WhoDB

# Your First Database Connection

Connecting to a database for the first time can feel daunting, but WhoDB makes it straightforward. This tutorial will walk you through every step of establishing your first successful connection, from understanding the login screen to troubleshooting common issues.

## What You'll Learn

By the end of this tutorial, you'll be able to:
- Navigate the WhoDB login interface confidently
- Select the appropriate database type for your needs
- Configure connection settings correctly
- Troubleshoot common connection problems
- Verify a successful connection

This tutorial assumes you have WhoDB installed and running. If you haven't installed it yet, check out the [Installation Guide](/installation).

## Prerequisites

Before starting, make sure you have:
- WhoDB installed and accessible in your browser
- Database credentials (username, password, host, port)
- Network access to your database server
- Basic understanding of your database type (PostgreSQL, MySQL, etc.)

## Step 1: Understanding the Login Page

When you first open WhoDB, you'll see a clean, focused login page:

![WhoDB Login Page](/images/01-login-page.png)

The login page is designed with simplicity in mind. You'll notice several key elements:

**Database Type Selector**: The dropdown at the top allows you to choose which type of database you're connecting to. This is the first and most important decision.

**Connection Fields**: Below the database type, you'll see fields for entering connection details. These fields change based on the database type you select.

**Advanced Options**: An expandable section for SSL certificates, SSH tunnels, and connection pooling settings.

**Connect Button**: Once your details are entered, this button establishes the connection.

WhoDB doesn't store your credentials by default. You'll need to enter them each time you connect, ensuring better security for sensitive environments.

## Step 2: Selecting Your Database Type

Click the database type dropdown to see all available options:

![Database Type Dropdown](/images/02-login-database-type-dropdown.png)

WhoDB supports several database types:

The world's most advanced open source relational database

Popular open source relational database management system

Self-contained, serverless SQL database engine

Document-oriented NoSQL database

In-memory data structure store

Enterprise Edition includes support for Oracle, SQL Server, and more

For this tutorial, we'll use PostgreSQL as our example, but the process is similar for other database types.

Choose the database type that matches your actual database. Selecting the wrong type will result in connection errors.

## Step 3: Entering Connection Details

After selecting PostgreSQL, the form updates to show the required fields. Let's fill them in:

![Login Form Filled](/images/03-login-form-filled.png)

Here's what each field means and what to enter:

**Host**: The address where your database server is running. Common values:
- `localhost` - For databases on your local machine
- `127.0.0.1` - Alternative localhost address
- `host.docker.internal` - When WhoDB runs in Docker connecting to host machine
- `postgres` or other service names - When using Docker Compose
- `db.example.com` - For remote servers
- IP addresses like `192.168.1.100` - For local network servers

**Port**: The port your database listens on. Default ports:
- PostgreSQL: `5432`
- MySQL: `3306`
- MongoDB: `27017`
- Redis: `6379`
- SQLite: No port needed (file-based)

**Username**: Your database user account. Common usernames:
- PostgreSQL: `postgres` (default superuser)
- MySQL: `root` (default administrator)
- Custom users: Your organization's specific username

**Password**: The password for the database user. Enter this carefully as passwords are case-sensitive.

**Database**: The specific database you want to connect to. This is the database name within your server:
- PostgreSQL: `postgres` (default database), or your application database like `myapp_production`
- MySQL: Your database name like `wordpress` or `ecommerce`
- MongoDB: Your database name or leave blank to see all databases

Never share your database credentials through insecure channels. Always use strong, unique passwords for production databases.

### Example Connection: Local Development

For a local PostgreSQL development database:
```
Host: localhost
Port: 5432
Username: postgres
Password: dev_password
Database: myapp_dev
```

### Example Connection: Docker Compose

When connecting from WhoDB to a PostgreSQL container:
```
Host: postgres
Port: 5432
Username: postgres
Password: password
Database: app_db
```

### Example Connection: Remote Server

For a production PostgreSQL server:
```
Host: db.example.com
Port: 5432
Username: app_readonly
Password: secure_random_password_123
Database: production_db
```

## Step 4: Configuring Advanced Options (Optional)

For secure connections or special networking requirements, click the "Advanced Options" section:

![Advanced Options](/images/04-login-advanced-options.png)

Enable encrypted connections for production databases:

**When to use**: Always for production databases, especially over the internet

**SSL Mode Options**:
- `disable`: No SSL (only for local development)
- `require`: Use SSL, but don't verify certificates
- `verify-ca`: Use SSL and verify the certificate authority
- `verify-full`: Use SSL and verify the entire certificate chain (most secure)

**Certificate Files**: If your database requires client certificates, provide:
- SSL Certificate: Your client certificate file
- SSL Key: Your private key file
- SSL Root Certificate: The CA certificate to verify the server

Many cloud database providers (AWS RDS, Google Cloud SQL, Azure) require or recommend SSL connections. Check your provider's documentation for certificate files.

Connect through an SSH bastion host for added security:

**When to use**: When your database isn't directly accessible but you can SSH to a jump host

**Required Fields**:
- SSH Host: The bastion/jump server address
- SSH Port: Usually `22`
- SSH Username: Your SSH username
- SSH Private Key: Path to your SSH private key file

This creates an encrypted tunnel through the SSH server to reach your database.

Optimize performance for high-traffic applications:

- **Max Connections**: Maximum simultaneous connections (default: 10)
- **Max Idle**: Maximum idle connections to keep open (default: 5)
- **Connection Lifetime**: How long connections stay open (default: 1 hour)

Most users can leave these at default values. Adjust only if you're experiencing connection issues or have specific performance requirements.

## Step 5: Establishing the Connection

Once all fields are filled correctly, click the "Connect" button.

WhoDB will attempt to:
1. Validate your input fields
2. Establish a network connection to the host and port
3. Authenticate using your username and password
4. Connect to the specified database
5. Verify permissions and access

This process usually takes 1-3 seconds. You'll see a loading indicator while the connection is being established.

Success! If the connection succeeds, you'll be redirected to the main WhoDB interface showing your database structure and tables.

## Troubleshooting Connection Issues

If your connection fails, don't worry. Here are the most common issues and their solutions:

**Symptom**: "Connection refused" or "Cannot connect to server"

**Possible Causes & Solutions**:

1. **Database server isn't running**
   - Verify the database service is started
   - Check with: `systemctl status postgresql` or `brew services list`

2. **Wrong host or port**
   - Double-check the host address and port number
   - Try `localhost` vs `127.0.0.1` vs `host.docker.internal`
   - Verify the port with: `netstat -an | grep 5432`

3. **Firewall blocking connection**
   - Check firewall rules allow the port
   - For cloud databases, verify security group settings
   - Test connectivity: `telnet hostname 5432`

4. **Docker networking issues**
   - If WhoDB is in Docker, use `host.docker.internal` for host machine databases
   - Ensure containers are on the same Docker network for container-to-container connections
   - Check with: `docker network inspect bridge`

For Docker users connecting to the host machine: Use `host.docker.internal` on Mac/Windows or `172.17.0.1` on Linux instead of `localhost`.

**Symptom**: "Password authentication failed" or "Access denied"

**Possible Causes & Solutions**:

1. **Incorrect username or password**
   - Verify credentials are correct (passwords are case-sensitive)
   - Try connecting with a different tool to verify credentials work
   - Reset password if needed: `ALTER USER postgres PASSWORD 'newpassword';`

2. **User doesn't have permission**
   - Grant necessary permissions: `GRANT ALL ON DATABASE mydb TO username;`
   - Verify user exists: `SELECT * FROM pg_user;` (PostgreSQL)
   - Check user hosts: `SELECT user, host FROM mysql.user;` (MySQL)

3. **Host-based authentication restrictions**
   - Check `pg_hba.conf` (PostgreSQL) allows connections from WhoDB's IP
   - For MySQL, verify user is allowed from WhoDB's host: `'user'@'%'` vs `'user'@'localhost'`

Be cautious when modifying authentication configuration files. Always backup before making changes.

**Symptom**: "Database does not exist" or "Unknown database"

**Possible Causes & Solutions**:

1. **Database name is incorrect**
   - Verify the database name (case-sensitive in some systems)
   - List databases: `\l` (PostgreSQL) or `SHOW DATABASES;` (MySQL)
   - Check for typos or extra spaces

2. **Database hasn't been created**
   - Create the database: `CREATE DATABASE mydb;`
   - Ensure you have permission to see the database

3. **Connected to wrong server**
   - Verify you're connecting to the intended server
   - Check the host address matches your expectation

**Symptom**: "SSL connection error" or "Certificate verification failed"

**Possible Causes & Solutions**:

1. **Server requires SSL but it's disabled**
   - Enable SSL in Advanced Options
   - Set SSL Mode to at least "require"

2. **Certificate files are incorrect**
   - Verify certificate paths are correct and files exist
   - Ensure WhoDB has read permissions for certificate files
   - Check certificates aren't expired: `openssl x509 -in cert.pem -noout -dates`

3. **SSL mode is too strict**
   - Try "require" instead of "verify-full" for development
   - Ensure you have the correct CA certificate for "verify-ca" or "verify-full"

Cloud database providers often provide downloadable certificate bundles. Check your provider's documentation for SSL setup instructions.

**Symptom**: "Connection timeout" or "Operation timed out"

**Possible Causes & Solutions**:

1. **Network connectivity issues**
   - Test network: `ping hostname`
   - Check VPN is connected if required
   - Verify DNS resolution: `nslookup hostname`

2. **Firewall blocking traffic**
   - Check both local and remote firewalls
   - For cloud databases, verify security groups/firewall rules
   - Ensure your IP is whitelisted if required

3. **Server is overloaded**
   - Database server might be unresponsive
   - Check server resources and performance
   - Try again in a few minutes

4. **Wrong region or endpoint**
   - For cloud databases, verify you're using the correct regional endpoint
   - Check for typos in the hostname

## Verifying Your Connection

Once connected successfully, you should see the main WhoDB interface with:

- **Sidebar**: Showing your database structure with schemas and tables
- **Main area**: Displaying a list of available tables or storage units
- **Top navigation**: With tabs for Data, Explore, Graph, and Scratchpad views

Take a moment to explore:
- Expand schemas in the sidebar
- Click on a table to view its data
- Check that the database name appears correctly in the interface

If you see your tables and can browse the sidebar, your connection is working perfectly. You're ready to start exploring your data!

## Security Best Practices

Now that you're connected, keep these security practices in mind:

Create specific users for WhoDB with only necessary permissions:
```
-- PostgreSQL example
CREATE USER whodb_viewer WITH PASSWORD 'secure_password';
GRANT CONNECT ON DATABASE mydb TO whodb_viewer;
GRANT USAGE ON SCHEMA public TO whodb_viewer;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO whodb_viewer;
```

Always use SSL/TLS connections for production databases to encrypt data in transit.

Use firewall rules to allow connections only from trusted IP addresses or networks.

For viewing data only, use database accounts with SELECT-only permissions.

Change database passwords periodically and after team member departures.

## Next Steps

Congratulations on establishing your first database connection! Now you're ready to:

Learn how to browse tables, view data, and filter results

Start writing SQL queries in the Scratchpad

Understand table relationships with the Graph view

Get a comprehensive overview of WhoDB features

## Summary

In this tutorial, you learned how to:
- Navigate the WhoDB login interface
- Select the appropriate database type
- Enter connection details correctly
- Configure advanced options for SSL and SSH
- Troubleshoot common connection issues
- Verify a successful connection
- Follow security best practices

You're now connected and ready to explore! The next tutorial will show you how to navigate your database, view data, and use filtering and search features effectively.

---


# guides/tutorials/data-exploration-workflow.mdx
## Data Exploration Workflow

Master the complete workflow for exploring, viewing, filtering, and analyzing data in WhoDB

# Data Exploration Workflow

Data exploration is at the heart of database management. Whether you're debugging an issue, analyzing trends, or understanding your data structure, WhoDB provides a comprehensive set of tools to make exploration efficient and intuitive. This tutorial guides you through a complete workflow from initial connection to advanced data analysis.

## What You'll Learn

This tutorial covers the complete data exploration workflow:
- Navigating the database structure efficiently
- Understanding the Explore view for table metadata
- Viewing and browsing table data
- Using pagination to handle large datasets
- Filtering data with WHERE conditions
- Searching across all columns
- Sorting data for better insights
- Combining multiple techniques for complex analysis

This tutorial assumes you've already connected to a database. If you haven't, complete the [First Database Connection](/guides/tutorials/first-database-connection) tutorial first.

## Understanding the WhoDB Interface

After connecting, you'll see the main interface with several key areas:

![Storage Unit List](/images/05-storage-unit-list.png)

The interface is divided into functional zones:

**Left Sidebar**: Your navigation hub showing databases, schemas, tables, views, and other database objects in a hierarchical tree structure.

**Main Content Area**: Where you interact with your data—viewing tables, examining metadata, or analyzing results.

**Top Tabs**: Switch between different views:
- **Data**: View and edit table contents
- **Explore**: Examine table structure and metadata
- **Graph**: Visualize schema relationships
- **Scratchpad**: Write and execute custom queries

**Action Bar**: Context-sensitive buttons for common operations like adding rows, exporting data, or refreshing views.

## Step 1: Navigating Your Database Structure

Let's start by exploring the sidebar navigation:

![Sidebar Navigation](/images/06-storage-unit-list-with-sidebar.png)

The sidebar organizes your database in a logical hierarchy:

At the top, you'll see your connected database name. For systems that support multiple databases (like PostgreSQL or MySQL), you can switch between databases using the dropdown selector.

![Database Selector](/images/34-sidebar-database-selector.png)

Expand the database to see schemas. Schemas are logical groupings of tables and other objects:
- PostgreSQL: Usually has `public` schema and possibly others
- MySQL: Schemas are equivalent to databases
- SQLite: No schema concept (all tables in one namespace)

![Schema Selector](/images/35-sidebar-schema-selector.png)

In PostgreSQL, the `public` schema is the default. Application tables often live here, while system tables are in `pg_catalog` and `information_schema`.

Within each schema, objects are grouped by type:
- **Tables**: Your data storage units
- **Views**: Virtual tables based on queries
- **Materialized Views**: Cached query results (PostgreSQL)
- **Functions**: Stored procedures and functions
- **Sequences**: Auto-increment generators

Click the arrow next to any group to expand and see individual objects.

Click on any table name to open it in the main content area. The table name becomes highlighted, indicating it's currently selected.

![Navigation Menu](/images/36-sidebar-navigation-menu.png)

The sidebar supports keyboard navigation. Use arrow keys to move between items and Enter to open a table.

## Step 2: Exploring Table Structure

Before diving into data, it's valuable to understand the table structure. Click on a table, then switch to the **Explore** tab:

![Explore Users Table](/images/07-explore-users-table.png)

The Explore view provides comprehensive metadata about your table:

### Table Information

At the top, you'll see:
- **Table Name**: The full qualified name (schema.table)
- **Row Count**: Approximate number of rows
- **Size**: Disk space used by the table
- **Created/Modified**: Timestamps for table creation and last modification

### Column Details

The main section shows all columns with detailed information:

![Explore Table Metadata](/images/08-explore-table-metadata.png)

For each column, you can see:

**Column Name**: The field name as it appears in the database

**Data Type**: The column's data type (INTEGER, VARCHAR, TEXT, TIMESTAMP, etc.)
- Numeric types: INT, BIGINT, DECIMAL, NUMERIC, REAL, DOUBLE
- String types: CHAR, VARCHAR, TEXT
- Date/Time types: DATE, TIME, TIMESTAMP, TIMESTAMPTZ
- Boolean: BOOLEAN, BOOL
- Binary: BYTEA, BLOB
- JSON: JSON, JSONB (PostgreSQL)

**Nullable**: Whether the column can contain NULL values
- ✓ (Yes): Column accepts NULL values
- ✗ (No): Column requires a value (NOT NULL constraint)

**Default Value**: The default value assigned when no value is provided
- `NULL`: Defaults to NULL
- `nextval('sequence_name')`: Auto-increment from sequence
- Static values: '0', 'CURRENT_TIMESTAMP', etc.

**Primary Key**: Indicated with a key icon if the column is part of the primary key

![Primary Key Column](/images/98-explore-primary-key-column.png)

**Foreign Key**: Shows relationships to other tables with an arrow icon

![Foreign Key Columns](/images/99-explore-foreign-key-columns.png)

Understanding column types is important for filtering and querying. A TIMESTAMP column requires date-time values, while an INTEGER column accepts only numbers.

### Indexes

Below the columns, you'll find index information:

![Table with Indexes](/images/100-explore-table-with-indexes.png)

Indexes speed up queries by creating optimized lookup structures. Common index types:
- **Primary Key Index**: Automatically created for primary keys
- **Unique Index**: Ensures column values are unique
- **Regular Index**: Improves query performance
- **Composite Index**: Covers multiple columns

Indexes are important for query performance. If you're filtering frequently on a column without an index, queries may be slow on large tables.

### Constraints

Constraints enforce data integrity rules:
- **Primary Key**: Uniquely identifies each row
- **Foreign Key**: Links to another table
- **Unique**: Ensures column values are unique
- **Check**: Validates data against a condition
- **Not Null**: Requires a value

Understanding constraints helps you understand how data relates and what values are valid.

## Step 3: Viewing Table Data

Now let's look at the actual data. Click the **Data** tab to see the table contents:

![Data View Users Table](/images/09-data-view-users-table.png)

The Data view displays records in a spreadsheet-like grid:

### Understanding the Data Grid

![Data View Table Content](/images/10-data-view-table-content.png)

**Column Headers**: Show column names and types. Click to sort or access column options.

**Row Numbers**: On the left, showing the row position in the current view (not the database ID).

**Cells**: Individual data values. Different types are formatted appropriately:
- Numbers: Right-aligned
- Text: Left-aligned
- Dates: Formatted as YYYY-MM-DD or with time
- Booleans: Shown as true/false or ✓/✗
- NULL values: Displayed in gray as "NULL"

![Table with NULL Values](/images/79-table-with-null-values.png)

**Long Text**: Text too long for the cell is truncated with "..." Click the cell to see the full content.

![Table Long Text Content](/images/80-table-long-text-content.png)

### Data Type Formatting

Different data types are displayed with appropriate formatting:

**Date and Timestamp Fields**:
![Table Dates and Timestamps](/images/81-table-dates-timestamps.png)

**Numeric Types**:
![Table Numeric Types](/images/82-table-numeric-types.png)

Hover over a cell to see a tooltip with the full value, especially useful for truncated text or detailed timestamps.

## Step 4: Pagination for Large Datasets

Large tables can contain millions of rows. WhoDB uses pagination to load data efficiently:

![Page Size Dropdown](/images/15-data-view-page-size-dropdown.png)

### Configuring Page Size

Click the page size dropdown to choose how many rows to display per page:

**Available Options**:
- 10 rows: For quick browsing and minimal scrolling
- 25 rows: Balance between data visibility and performance
- 50 rows: More data per page for larger screens
- 100 rows: Maximum rows per page for comprehensive views

![Page Size 10 Selected](/images/56-page-size-10-selected.png)
![Page Size 25 Selected](/images/57-page-size-25-selected.png)
![Page Size 50 Selected](/images/58-page-size-50-selected.png)

**Choosing the Right Page Size**:
- Use smaller sizes (10-25) for tables with many columns
- Use larger sizes (50-100) for tables with few columns
- Consider your screen size and resolution
- Smaller sizes load faster on slow connections

### Navigating Pages

At the bottom of the data grid, you'll find pagination controls:
- **First Page**: Jump to the beginning
- **Previous Page**: Go back one page
- **Page Numbers**: Click specific page numbers
- **Next Page**: Advance one page
- **Last Page**: Jump to the end

The current page and total pages are displayed: "Page 1 of 42"

The total row count is approximate for large tables to avoid expensive COUNT(*) queries. The actual number of pages may vary slightly.

## Step 5: Filtering Data with WHERE Conditions

Often you don't need all data—just specific records matching certain criteria. Use WHERE conditions to filter:

![Where Conditions Popover](/images/16-data-view-where-conditions-popover.png)

Click the filter icon in the action bar to open the WHERE conditions panel.

Click "Select field" to see all available columns:

![Where Field Dropdown](/images/17-data-view-where-field-dropdown.png)

Choose the column you want to filter on. The available operators will adjust based on the column's data type.

Select the comparison operator:

**For all types**:
- `=` (Equals): Exact match
- `!=` (Not Equals): Exclude matching values

![Where Operator Equals](/images/59-where-operator-equals.png)
![Where Operator Not Equals](/images/62-where-operator-not-equals.png)

**For numbers and dates**:
- `>` (Greater Than): Values larger than the specified value
- `<` (Less Than): Values smaller than the specified value
- `>=` (Greater Than or Equal): Values larger than or equal to
- `<=` (Less Than or Equal): Values smaller than or equal to

![Where Operator Greater Than](/images/60-where-operator-greater-than.png)
![Where Operator Less Than](/images/61-where-operator-less-than.png)
![Where Operator GTE](/images/63-where-operator-gte.png)
![Where Operator LTE](/images/64-where-operator-lte.png)

**For text**:
- `LIKE`: Pattern matching with % wildcards
- `NOT LIKE`: Exclude patterns
- `IN`: Match any value in a list
- `NOT IN`: Exclude values in a list
- `IS NULL`: Find NULL values
- `IS NOT NULL`: Find non-NULL values

Type the value to filter by. The input field adapts to the column type:
- **Text**: Free text input
- **Numbers**: Numeric input with validation
- **Dates**: Date picker or manual entry
- **Booleans**: True/false dropdown

Click "Add Condition" to combine multiple filters. You can use AND/OR logic to create complex filters.

![Multiple Conditions](/images/39-data-view-multiple-conditions.png)

Click "Apply" to execute the filter. The data grid updates to show only matching rows.

![Where Conditions Badge](/images/18-data-view-where-conditions-badge.png)

A badge appears showing the number of active filters. Click it to view or modify conditions.

### Real-World Filtering Examples

**Find active users**:
```
Field: status
Operator: =
Value: active
```

**Find recent records**:
```
Field: created_at
Operator: >
Value: 2024-01-01
```

**Find users by email domain**:
```
Field: email
Operator: LIKE
Value: %@example.com
```

**Find rows with missing data**:
```
Field: phone_number
Operator: IS NULL
```

**Complex filter (recent active users)**:
```
Condition 1: status = 'active'
AND
Condition 2: created_at > '2024-01-01'
```

Filters are translated to SQL WHERE clauses. The query runs on the database server, making it efficient even for large tables.

## Step 6: Searching Across All Columns

For quick searches without setting up filters, use the search box:

![Search Highlight](/images/19-data-view-search-highlight.png)

The search feature:
- Searches across all visible columns simultaneously
- Highlights matching text in yellow
- Updates in real-time as you type
- Is case-insensitive by default
- Works with the current page only (combine with filters for broader searches)

**When to use Search vs. Filters**:
- **Search**: Quick lookups when you're not sure which column contains the value
- **Filters**: Precise queries on specific columns with operators

Search works on the current page only. For comprehensive searches across all rows, use WHERE conditions with the LIKE operator.

## Step 7: Sorting Data

Sorting helps you find extremes (highest, lowest, most recent) or organize data logically:

![Sorted Column](/images/38-data-view-sorted-column.png)

Click any column header to sort by that column in ascending order (A-Z, 0-9, oldest to newest).

Click the same header again to reverse the sort order (descending: Z-A, 9-0, newest to oldest).

Hold Shift and click multiple column headers to sort by multiple columns. The sort order follows your click sequence.

Right-click a column header to access additional options:

![Table Header Context Menu](/images/37-table-header-context-menu.png)

Options include:
- Sort Ascending
- Sort Descending
- Clear Sort
- Hide Column
- Filter by this column

### Sorting Examples

**Find newest records**:
- Click `created_at` column header twice (descending)

**Find highest values**:
- Click `amount` column header twice (descending)

**Alphabetical listing**:
- Click `name` column header once (ascending)

**Complex sort (by status, then by date)**:
- Click `status` header (ascending)
- Hold Shift, click `created_at` header twice (descending)

Sorting is performed by the database, not in the browser, making it efficient even for millions of rows.

## Step 8: Combining Techniques for Advanced Analysis

The real power comes from combining filtering, sorting, and pagination:

### Example Workflow: Finding Top Customers

**Goal**: Find the top 10 customers by total purchases in the last quarter.

Filter `purchase_date >= '2024-01-01'`

Click `total_amount` column twice for descending sort

Choose 10 rows per page

The first page now shows your top 10 customers

### Example Workflow: Data Quality Check

**Goal**: Find incomplete user records.

Add condition: `email IS NULL`

Add condition: `phone IS NULL` with OR logic

Sort `created_at` descending to see most recent incomplete records

Identify patterns in incomplete records

### Example Workflow: Activity Analysis

**Goal**: Analyze user activity patterns.

Add condition: `status = 'active'`

Use search box: "@enterprise.com" to find enterprise users

Sort `last_login_at` descending

Set to 50 rows for better overview

## Handling Different Data Scenarios

### Empty Result Sets

If your filters return no rows:

![Empty State No Results](/images/76-table-empty-state-no-results.png)

**Troubleshooting**:
- Check filter values for typos
- Verify date formats match the database format
- Use broader conditions (LIKE with % wildcards)
- Clear filters and try again with simpler conditions

### Single Row Results

![Single Row Result](/images/77-table-single-row-result.png)

Perfect for finding specific records by unique identifiers or primary keys.

### Wide Tables

For tables with many columns:

![Many Columns Wide](/images/78-table-many-columns-wide.png)

**Tips**:
- Scroll horizontally to see all columns
- Hide unnecessary columns via the column header menu
- Use a smaller page size to reduce vertical scrolling
- Consider the Explore view to see column list

## Best Practices for Data Exploration

Begin by viewing data without filters to understand the overall dataset. Then apply filters progressively to focus on what matters.

1. View table structure in Explore tab
2. Browse sample data without filters
3. Identify interesting patterns
4. Apply filters to investigate further

Choose page sizes based on your task:
- **10 rows**: Quick checks and narrow filters
- **25 rows**: General exploration
- **50 rows**: Detailed analysis
- **100 rows**: Comprehensive reviews

Remember: Larger page sizes take longer to load.

Use search for quick exploration, then create precise filters for the patterns you discover:

1. Search to find examples
2. Note which columns contain the pattern
3. Create WHERE conditions for those columns
4. Refine with additional criteria

Check the Explore tab before filtering:
- Timestamp fields need proper date formats
- Numeric fields require numbers, not strings
- Boolean fields use true/false or 1/0
- Text fields are case-sensitive in some databases

This prevents filter errors and unexpected results.

Sorting reveals data patterns:
- Sort dates descending to see recent activity
- Sort names ascending for alphabetical lists
- Sort numbers descending to find extremes
- Multi-sort to organize by category then value

NULL values indicate missing data:
- Use `IS NULL` to find empty fields
- Use `IS NOT NULL` to exclude empty fields
- Consider data quality implications
- Plan data cleanup or validation

## Performance Considerations

Keep these performance tips in mind when working with large datasets

**Indexed Columns**: Filtering and sorting on indexed columns is much faster. Check the Explore tab to see which columns have indexes.

**Avoid Large Page Sizes on Slow Connections**: Loading 100 rows over a slow connection takes significantly longer than 10 rows.

**Filter Before Sorting**: Apply WHERE conditions to reduce the dataset before sorting. Sorting millions of rows is slower than sorting thousands.

**Use Appropriate Operators**: Some operators are faster than others:
- Fast: `=`, `!=`, `>`, `<`, `>=`, `<=` on indexed columns
- Medium: `LIKE` with leading characters (`name LIKE 'John%'`)
- Slow: `LIKE` with leading wildcards (`name LIKE '%smith'`)

**Limit Complex Filters**: Each additional filter condition adds processing time. Use the minimum necessary conditions.

## Next Steps

You've mastered the data exploration workflow! Continue your learning:

Write advanced SQL queries with JOINs and aggregations

Learn to export filtered data for further analysis

Explore table relationships with the Graph view

Learn about adding, editing, and deleting records

## Summary

In this tutorial, you learned:
- How to navigate the WhoDB interface and sidebar
- Understanding table structure with the Explore view
- Viewing and browsing table data efficiently
- Using pagination for large datasets
- Filtering data with WHERE conditions and operators
- Searching across all columns quickly
- Sorting data for better insights
- Combining techniques for advanced analysis
- Best practices for performance and data quality

You now have a complete workflow for exploring and analyzing data in WhoDB. Practice these techniques to become more efficient in your daily database work.

---


# guides/tutorials/building-complex-queries.mdx
## Building Complex SQL Queries

Master SQL JOINs, aggregations, and subqueries with step-by-step examples and WhoDB's Scratchpad editor

# Building Complex SQL Queries

Once you're comfortable viewing data, the next skill is writing SQL queries that answer real questions about your data. This tutorial guides you through building increasingly complex queries, from simple SELECT statements to advanced JOINs, aggregations, and subqueries. You'll learn to think analytically about data and translate business questions into SQL.

## What You'll Learn

By the end of this tutorial, you'll be able to:
- Write SELECT statements to retrieve specific columns
- Use WHERE clauses to filter data precisely
- Join data from multiple tables
- Aggregate data with GROUP BY and aggregate functions
- Filter aggregated results with HAVING
- Use subqueries to solve complex problems
- Optimize query performance
- Debug queries when something goes wrong

This tutorial assumes you've completed the [Data Exploration Workflow](/guides/tutorials/data-exploration-workflow) tutorial and are familiar with table structure and basic filtering.

## Prerequisites

Before starting, ensure you:
- Have WhoDB connected to a database with multiple related tables
- Understand the data schema and relationships between tables
- Are familiar with the Scratchpad editor and query results
- Have basic SQL knowledge (SELECT, WHERE, JOIN concepts)

## The Scratchpad Query Editor

Let's start with an overview of the Scratchpad, WhoDB's powerful query editor:

![Scratchpad Main View](/images/27-scratchpad-main-view.png)

The Scratchpad provides:

**Code Editor**: On the left, a full-featured SQL editor with:
- Syntax highlighting for SQL keywords
- Line numbers for easy reference
- Automatic indentation
- Multi-cell support for organizing complex analyses

![Scratchpad Code Editor](/images/28-scratchpad-code-editor.png)

**Results Panel**: On the right, displaying query results:
- Results shown in a paginated grid
- Column headers and data types
- Row counts and pagination controls
- Error messages for debugging

![Scratchpad Query Results](/images/29-scratchpad-query-results.png)

**Action Buttons**: Execute, save, and manage queries:
- Run Query: Execute the selected SQL
- Save Query: Store frequently-used queries
- Clear: Reset the editor
- Multiple cell support for batch operations

## Step 1: Simple SELECT Statements

Start with the foundation—retrieving data from a single table:

### Example 1: Select All Columns

```
SELECT * FROM users;
```

This returns every column and row from the users table. Useful for quick exploration but generally not recommended for production as it can be slow on large tables.

### Example 2: Select Specific Columns

```
SELECT id, email, created_at FROM users;
```

Only return the columns you need. This is faster and cleaner. Notice:
- Column names are separated by commas
- Order matters—results appear in the order you specify
- You can rename columns with AS: `email AS user_email`

### Example 3: Alias Column Names

```
SELECT
  id AS user_id,
  email AS user_email,
  created_at AS signup_date
FROM users;
```

Aliases make results clearer and easier to read. Use descriptive names that explain what the data represents.

### Example 4: Remove Duplicates

```
SELECT DISTINCT status FROM users;
```

The DISTINCT keyword removes duplicate values. Result: one row for each unique status value.

Queries become more readable when formatted with proper line breaks. Press Shift+Enter to create new lines in the Scratchpad without executing the query.

## Step 2: Filtering with WHERE Clauses

SELECT without WHERE returns all rows. Add WHERE to filter:

### Example 5: Simple Equality Filter

```
SELECT id, name, email FROM users WHERE status = 'active';
```

Returns only rows where status equals 'active'. Notice the string value is in single quotes.

### Example 6: Multiple Conditions with AND

```
SELECT id, name, email, created_at
FROM users
WHERE status = 'active' AND created_at >= '2024-01-01';
```

AND requires all conditions to be true. This finds active users who signed up in 2024.

### Example 7: Multiple Conditions with OR

```
SELECT id, name, email
FROM users
WHERE status = 'active' OR status = 'pending';
```

OR requires at least one condition to be true. This finds users who are either active or pending.

### Example 8: Combining AND and OR

```
SELECT id, name, email
FROM users
WHERE (status = 'active' OR status = 'trial')
  AND created_at >= '2024-01-01';
```

Parentheses control evaluation order. Without them, the logic can be ambiguous. Always use parentheses for clarity.

### Example 9: Text Pattern Matching

```
SELECT id, name, email FROM users WHERE email LIKE '%@company.com';
```

LIKE enables pattern matching:
- `%` matches any characters
- `_` matches a single character
- `'%@company.com'` finds emails ending with @company.com
- `'john%'` finds names starting with john
- `'%smith%'` finds names containing smith

### Example 10: Numeric Comparisons

```
SELECT id, name, age FROM users WHERE age >= 18 AND age < 65;
```

Numeric operators: `=`, `!=`, `<`, `>`, `<=`, `>=`

Date comparisons require proper date format. Use 'YYYY-MM-DD' format for compatibility across databases. Some databases also support comparison of timestamps.

## Step 3: Sorting and Limiting Results

### Example 11: Sort Results

```
SELECT id, name, created_at
FROM users
ORDER BY created_at DESC;
```

ORDER BY sorts results:
- `ASC`: Ascending order (default, A-Z, 0-9, earliest date)
- `DESC`: Descending order (Z-A, 9-0, latest date)

### Example 12: Multi-Column Sorting

```
SELECT id, name, status, created_at
FROM users
ORDER BY status ASC, created_at DESC;
```

Sort by status first (active users, then inactive), then by date within each group. Order matters—queries sort by the first column, then the second, etc.

### Example 13: Limit Result Count

```
SELECT id, name, created_at
FROM users
ORDER BY created_at DESC
LIMIT 10;
```

LIMIT restricts the number of rows returned. Useful for:
- Getting a sample of data
- Finding top N items
- Preventing huge result sets

### Example 14: Pagination with OFFSET

```
SELECT id, name, created_at
FROM users
ORDER BY created_at DESC
LIMIT 10 OFFSET 20;
```

OFFSET skips the first N rows. Combined with LIMIT, it enables pagination:
- Page 1: `LIMIT 10 OFFSET 0`
- Page 2: `LIMIT 10 OFFSET 10`
- Page 3: `LIMIT 10 OFFSET 20`

## Step 4: Aggregate Functions

Aggregate functions combine multiple rows into single summary values:

### Example 15: Count Records

```
SELECT COUNT(*) FROM users;
```

COUNT(*) returns the total number of rows. Alternative: `COUNT(id)` counts non-NULL values in the id column.

### Example 16: Sum and Average

```
SELECT
  SUM(amount) AS total_revenue,
  AVG(amount) AS average_order,
  COUNT(*) AS order_count
FROM orders;
```

Common aggregate functions:
- `COUNT()`: Number of rows
- `SUM()`: Total of numeric values
- `AVG()`: Average of numeric values
- `MIN()`: Smallest value
- `MAX()`: Largest value

### Example 17: Multiple Aggregates

```
SELECT
  COUNT(*) AS total_orders,
  SUM(amount) AS total_amount,
  AVG(amount) AS average_amount,
  MIN(amount) AS smallest_order,
  MAX(amount) AS largest_order
FROM orders;
```

Combine multiple aggregates to get comprehensive statistics about your data.

### Example 18: Find Minimum and Maximum

```
SELECT
  MIN(created_at) AS earliest_user,
  MAX(created_at) AS latest_user
FROM users;
```

Useful for understanding data ranges—when did your data start and end?

Always use aliases (AS) with aggregate functions to make column names meaningful. `SUM(amount)` is unclear; `SUM(amount) AS total_revenue` is self-documenting.

## Step 5: GROUP BY for Grouped Aggregates

GROUP BY lets you aggregate by categories:

### Example 19: Count by Status

```
SELECT
  status,
  COUNT(*) AS user_count
FROM users
GROUP BY status;
```

Result shows each unique status with the count of users in that status. Key point: When using GROUP BY, SELECT can only include:
- Grouped columns (status)
- Aggregate functions (COUNT)

### Example 20: Revenue by User

```
SELECT
  user_id,
  COUNT(*) AS order_count,
  SUM(amount) AS total_spent,
  AVG(amount) AS average_order
FROM orders
GROUP BY user_id
ORDER BY total_spent DESC;
```

Find each user's purchasing statistics. Sorted by total spending to see top customers first.

### Example 21: Multiple GROUP BY Columns

```
SELECT
  status,
  EXTRACT(YEAR FROM created_at) AS year,
  COUNT(*) AS user_count
FROM users
GROUP BY status, EXTRACT(YEAR FROM created_at)
ORDER BY year DESC, status;
```

Group by multiple columns for deeper analysis. This shows user counts by status and year.

### Example 22: Filter Groups with HAVING

```
SELECT
  status,
  COUNT(*) AS user_count
FROM users
GROUP BY status
HAVING COUNT(*) > 100;
```

HAVING filters grouped results (like WHERE for aggregates). This shows only statuses with more than 100 users.

Difference:
- `WHERE`: Filters rows before grouping
- `HAVING`: Filters groups after aggregation

### Example 23: Complex HAVING Clause

```
SELECT
  user_id,
  COUNT(*) AS order_count,
  SUM(amount) AS total_spent
FROM orders
GROUP BY user_id
HAVING COUNT(*) >= 5 AND SUM(amount) > 500
ORDER BY total_spent DESC;
```

Find power users: those with 5+ orders AND over $500 spent. The WHERE/HAVING combination is crucial:

```
SELECT
  user_id,
  COUNT(*) AS order_count,
  SUM(amount) AS total_spent
FROM orders
WHERE created_at >= '2024-01-01'  -- Filter rows before grouping
GROUP BY user_id
HAVING SUM(amount) > 500          -- Filter groups after aggregation
ORDER BY total_spent DESC;
```

## Step 6: INNER JOIN - Combining Data

JOINs combine data from multiple tables:

### Example 24: Simple INNER JOIN

```
SELECT
  users.name,
  orders.id,
  orders.amount,
  orders.created_at
FROM users
INNER JOIN orders ON users.id = orders.user_id;
```

INNER JOIN returns rows that exist in both tables. This combines user names with their orders.

Structure:
- `FROM users`: Start with the users table
- `INNER JOIN orders`: Add data from orders table
- `ON users.id = orders.user_id`: Match rows where user IDs match

### Example 25: JOIN with Aliases

```
SELECT
  u.name,
  o.id AS order_id,
  o.amount,
  o.created_at
FROM users u
INNER JOIN orders o ON u.id = o.user_id
ORDER BY u.name, o.created_at;
```

Table aliases (`u` for users, `o` for orders) make queries shorter and clearer. Aliases become essential with multiple JOINs.

### Example 26: Three-Table JOIN

```
SELECT
  u.name,
  o.id AS order_id,
  oi.product_id,
  oi.quantity,
  oi.price
FROM users u
INNER JOIN orders o ON u.id = o.user_id
INNER JOIN order_items oi ON o.id = oi.order_id
ORDER BY u.name, o.id;
```

Chain multiple JOINs to combine data from many tables. This query flows through users → orders → order_items.

### Example 27: JOIN with Filtering

```
SELECT
  u.name,
  COUNT(o.id) AS order_count,
  SUM(o.amount) AS total_spent
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE o.created_at >= '2024-01-01'
GROUP BY u.id, u.name
HAVING COUNT(o.id) > 0
ORDER BY total_spent DESC;
```

Combine JOINs with WHERE, GROUP BY, and HAVING for powerful analysis.

## Step 7: LEFT JOIN - Keeping All Rows

LEFT JOIN keeps all rows from the left table, even if no match exists in the right table:

### Example 28: LEFT JOIN for Completeness

```
SELECT
  u.name,
  COUNT(o.id) AS order_count,
  COALESCE(SUM(o.amount), 0) AS total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name
ORDER BY total_spent DESC;
```

This finds all users, including those with no orders. Result shows 0 for users with no spending.

Key differences from INNER JOIN:
- INNER: Only users with orders
- LEFT: All users, with 0/NULL for those without orders

The `COALESCE()` function converts NULL to 0 for cleaner display.

### Example 29: Identifying Missing Data

```
SELECT
  u.name,
  u.email,
  o.id
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.id IS NULL;
```

Find users with no orders by checking for NULL values. This is a powerful data quality technique.

## Step 8: Subqueries - Queries Within Queries

Subqueries solve complex problems by breaking them into smaller steps:

### Example 30: Subquery in WHERE

```
SELECT
  name,
  email,
  created_at
FROM users
WHERE id IN (
  SELECT user_id
  FROM orders
  WHERE amount > 1000
);
```

Find users who've placed orders over $1000. The subquery finds qualifying user IDs, then the outer query retrieves user details.

### Example 31: Subquery with Aggregation

```
SELECT
  name,
  email
FROM users
WHERE id IN (
  SELECT user_id
  FROM orders
  GROUP BY user_id
  HAVING SUM(amount) > 5000
);
```

Find users whose total spending exceeds $5000. Subqueries with GROUP BY handle complex aggregations.

### Example 32: Correlated Subquery

```
SELECT
  u.name,
  u.created_at,
  (SELECT COUNT(*) FROM orders WHERE user_id = u.id) AS order_count
FROM users u
ORDER BY order_count DESC;
```

A correlated subquery references the outer query. Here, for each user, we count their orders. More readable than a GROUP BY with coalesce.

### Example 33: Subquery in FROM Clause

```
SELECT
  status,
  AVG(order_count) AS avg_orders
FROM (
  SELECT
    status,
    COUNT(o.id) AS order_count
  FROM users u
  LEFT JOIN orders o ON u.id = o.user_id
  GROUP BY u.id, u.status
) user_stats
GROUP BY status;
```

Use a subquery result as a table. This calculates average orders per user, grouped by status.

## Executing Queries in the Scratchpad

Let's walk through executing a complete query:

### Step 1: Enter Your Query

Type or paste your SQL in the editor:

![Scratchpad Code Editor](/images/28-scratchpad-code-editor.png)

### Step 2: Execute the Query

Click the "Run" button or press Ctrl+Enter (Cmd+Enter on Mac):

![Scratchpad Query Results](/images/29-scratchpad-query-results.png)

Results appear in the right panel within 1-3 seconds. The status bar shows:
- Query execution time
- Number of rows returned
- Column count

### Step 3: Review Results

Examine the results grid:
- Each row represents one record
- Columns are sortable by clicking headers
- Numbers are right-aligned, text is left-aligned
- NULL values appear in gray

### Step 4: Handle Errors

If your query has an error, you'll see:

![Scratchpad Query Error](/images/30-scratchpad-query-error.png)

The error message explains what went wrong:
- Syntax errors (missing commas, quotes, keywords)
- Table/column doesn't exist
- Type mismatches
- Permission denied

Read the error carefully—it usually points to the exact problem line.

## Query Optimization Tips

### Use EXPLAIN to Understand Performance

```
EXPLAIN SELECT * FROM users WHERE status = 'active';
```

EXPLAIN shows how the database will execute your query. Look for:
- Full table scans (slow on large tables)
- Index usage (fast)
- Join strategies

### Index Awareness

```
-- Fast: Filtering on indexed column
SELECT * FROM users WHERE id = 123;

-- Slow: Filtering on non-indexed column
SELECT * FROM users WHERE phone_number = '555-1234';
```

Check which columns have indexes in the Explore view. Filter on indexed columns for better performance.

### Avoid SELECT *

```
-- Slow
SELECT * FROM large_table;

-- Fast
SELECT id, name, email FROM large_table;
```

Selecting specific columns reduces data transfer and is clearer about your intent.

### Aggregate Before Joining

```
-- Inefficient: Join large tables, then aggregate
SELECT u.id, COUNT(o.id)
FROM users u
JOIN orders o ON u.id = o.user_id
GROUP BY u.id;

-- Better: Aggregate first, then join
SELECT u.id, orders_stats.order_count
FROM users u
LEFT JOIN (
  SELECT user_id, COUNT(*) AS order_count
  FROM orders
  GROUP BY user_id
) orders_stats ON u.id = orders_stats.user_id;
```

Pre-aggregating reduces intermediate result sizes.

## Multi-Cell Queries

Use the Scratchpad's multi-cell feature to organize complex analyses:

![Scratchpad Multiple Pages](/images/31-scratchpad-multiple-pages.png)

Create multiple cells for:
- Data exploration queries
- Intermediate result calculations
- Final analysis queries

Organize your analysis logically, making it reproducible and understandable.

## Saving Frequently Used Queries

The cell options menu lets you save queries:

![Scratchpad Cell Options Menu](/images/32-scratchpad-cell-options-menu.png)

Save queries you run repeatedly:
- Common analysis queries
- Troubleshooting queries
- Performance monitoring queries

Access saved queries from the history panel:

![Scratchpad Query History](/images/33-scratchpad-query-history.png)

## Common Query Patterns

### Find Top N Items

```
SELECT category, SUM(revenue) AS total
FROM sales
GROUP BY category
ORDER BY total DESC
LIMIT 10;
```

### Customers This Month vs. Last Month

```
SELECT
  DATE_TRUNC('month', created_at) AS month,
  COUNT(*) AS new_customers
FROM users
WHERE created_at >= DATE_TRUNC('month', CURRENT_DATE - INTERVAL '2 months')
GROUP BY DATE_TRUNC('month', created_at)
ORDER BY month DESC;
```

### Products Without Recent Sales

```
SELECT p.id, p.name
FROM products p
LEFT JOIN order_items oi ON p.id = oi.product_id
WHERE oi.id IS NULL
OR oi.created_at < CURRENT_DATE - INTERVAL '90 days';
```

## Query Debugging Checklist

- Column names spelled correctly and exist in the table
- Single quotes around text values: `WHERE status = 'active'`
- Proper join conditions with table aliases
- GROUP BY includes all non-aggregated columns
- HAVING used for post-aggregation filtering
- Table names and aliases match throughout
- Date formats consistent ('YYYY-MM-DD')

## Next Steps

You've learned to write sophisticated queries. Continue your learning:

Run queries, then export results for further analysis

Understand table relationships that power your queries

Master WHERE clause techniques for complex filters

Export query results in multiple formats

## Summary

In this tutorial, you learned:
- Simple SELECT statements to retrieve specific columns
- WHERE clauses for precise data filtering
- ORDER BY and LIMIT for result control
- Aggregate functions (SUM, COUNT, AVG) for calculations
- GROUP BY for grouped analysis
- INNER and LEFT JOINs for combining tables
- Subqueries for complex problem-solving
- How to execute queries in the Scratchpad
- Query optimization and debugging techniques
- Multi-cell queries for organized analysis

You now have the skills to write complex SQL queries that answer real business questions. Practice these patterns and adapt them to your specific data and analytical needs.

---


# guides/tutorials/data-export-analysis.mdx
## Data Export and Analysis Workflow

Complete workflow for filtering data, exporting to Excel, and conducting analysis outside WhoDB

# Data Export and Analysis Workflow

Database exploration is just the first step. The real value comes from exporting your data into formats you can share, analyze, and present. This tutorial guides you through the complete workflow: filtering data, exporting in various formats, and analyzing results. By the end, you'll have a comprehensive dataset ready for presentations, reports, or deep analysis.

## What You'll Learn

By the end of this tutorial, you'll be able to:
- Apply precise filters to isolate exactly the data you need
- Understand different export formats and when to use each
- Export data to Excel, CSV, JSON, and SQL formats
- Configure export options like delimiters and column selection
- Export selected rows vs. entire result sets
- Use exported data in analysis tools like Excel and Google Sheets
- Create reproducible export workflows

This tutorial assumes you've completed the [Data Exploration Workflow](/guides/tutorials/data-exploration-workflow) tutorial and understand filtering and pagination.

## Prerequisites

Before starting, ensure you:
- Have WhoDB connected to a database with data
- Know how to filter data with WHERE conditions
- Have Excel, Google Sheets, or similar tools installed (optional but recommended)
- Are familiar with basic data analysis concepts

## The Complete Export Workflow

Here's the workflow we'll follow:

1. **Identify your data needs** - What question are you answering?
2. **Filter the dataset** - Apply WHERE conditions to get exactly what you need
3. **Preview results** - Verify you have the right data before exporting
4. **Choose export format** - Select the best format for your use case
5. **Configure export options** - Customize delimiter, columns, and filename
6. **Execute export** - Download your file
7. **Analyze and share** - Use the exported data for further work

## Step 1: Identifying Your Export Needs

Before filtering, clarify what you're exporting and why:

**Business Questions to Answer**:
- Quarterly sales performance report?
- Customer analysis for a marketing campaign?
- Data for compliance auditing?
- Product performance metrics?
- User engagement statistics?

**Who needs the data?**
- Internal team (technical analysis)?
- Management (presentation-ready)?
- External partners (standardized format)?
- Customers (privacy-sensitive)?

**When do you need it?**
- One-time export?
- Regular recurring export?
- Ad-hoc analysis?

This context guides your filtering and export choices. For example:
- Internal analysis: SQL format with all data
- Management report: Excel with summary statistics
- Partner sharing: CSV with sanitized sensitive fields

## Step 2: Applying Precise Filters

Let's start with a complete example. Suppose you need to analyze Q4 2024 sales:

Open the Data view and click the filter button:

![Where Conditions Popover](/images/16-data-view-where-conditions-popover.png)

### Example Filter Workflow 1: Date Range Analysis

**Goal**: Export all sales orders from Q4 2024 (October 1 - December 31)

Click the filter icon in the action bar to open the WHERE conditions panel.

Click "Select field" and choose `order_date`:

![Where Field Dropdown](/images/17-data-view-where-field-dropdown.png)

- Operator: `>=` (greater than or equal)
- Value: `2024-10-01`

![Where Operator GTE](/images/63-where-operator-gte.png)

Click "Add Condition" to add another filter. Set it to AND logic:
- Field: `order_date`
- Operator: `<=` (less than or equal)
- Value: `2024-12-31`

Add a third condition (AND):
- Field: `status`
- Operator: `=`
- Value: `completed`

This ensures we only export completed orders, excluding pending or cancelled ones.

Click "Apply". The data grid updates to show only Q4 completed orders:

![Multiple Conditions](/images/39-data-view-multiple-conditions.png)

Notice the filter badge showing active filters.

### Example Filter Workflow 2: Customer Segment Analysis

**Goal**: Export high-value customers who've placed 5+ orders

```
Condition 1: order_count >= 5 (AND)
Condition 2: total_spent >= 5000 (AND)
Condition 3: status = 'active'
```

This requires creating a custom view or running a query, then filtering results.

### Example Filter Workflow 3: Data Quality Check

**Goal**: Export incomplete records (missing email or phone)

```
Condition 1: email IS NULL (OR)
Condition 2: phone_number IS NULL
```

These incomplete records can then be reviewed, corrected, or deleted.

Always verify your filter results before exporting. Make sure the data grid shows exactly what you expect—the right number of rows and relevant columns.

## Step 3: Previewing Your Data

Before exporting, take time to review what you'll be exporting:

**Check the basics**:
- Page indicator shows "Page 1 of X" — do you have the expected number of pages?
- Visible columns are what you need — or should you hide some?
- Data appears correct — spot-check a few cells
- Formatting looks good — dates in proper format, numbers aligned

**Look at column order**:

![Table Header Context Menu](/images/37-table-header-context-menu.png)

Right-click column headers to hide unnecessary columns. This reduces file size and improves clarity for recipients.

**Check row count**:
The approximate row count helps you understand export size. A million-row export might take longer than a thousand-row export. For large datasets, consider:
- Adding more filter conditions
- Using LIMIT in queries for samples
- Scheduling exports during off-peak hours

## Step 4: Understanding Export Formats

Click the export button to see available formats:

![Export Dialog](/images/20-data-view-export-dialog.png)

Each format serves different purposes:

### CSV (Comma-Separated Values)

![Export Format CSV Option](/images/65-export-format-csv-option.png)

**When to use**:
- Maximum compatibility with all tools
- Text-based, small file size
- Universal import format for databases
- GitHub-friendly (versioning support)

**Characteristics**:
- Plain text, no formatting
- Column names in first row
- One row per line
- Configurable delimiter

**Example**:
```
id,name,email,created_at,status
1,Alice Johnson,alice@example.com,2023-01-15,active
2,Bob Smith,bob@example.com,2023-01-16,active
3,Charlie Brown,charlie@example.com,2023-01-17,inactive
```

### Excel

![Export Format Excel Option](/images/66-export-format-excel-option.png)

**When to use**:
- Business reports and presentations
- Data analysis in Excel or Google Sheets
- Sharing with non-technical stakeholders
- Need for formatting and formulas

**Characteristics**:
- Binary format (.xlsx)
- Supports formatting, colors, fonts
- Can include multiple sheets
- Larger file size than CSV

**Advantages**:
- Professional appearance
- Easy further analysis
- Column autofit for readability
- Date formatting preserved

### JSON

**When to use**:
- Web applications and APIs
- NoSQL databases
- JavaScript/Node.js projects
- Nested data structures

**Characteristics**:
- Structured, nested format
- Supports complex data types
- Self-documenting schema
- Larger file size than CSV

**Example**:
```
[
  {
    "id": 1,
    "name": "Alice Johnson",
    "email": "alice@example.com",
    "created_at": "2023-01-15",
    "status": "active"
  },
  {
    "id": 2,
    "name": "Bob Smith",
    "email": "bob@example.com",
    "created_at": "2023-01-16",
    "status": "active"
  }
]
```

### SQL

**When to use**:
- Database backups and migrations
- Sharing structured data with developers
- Creating reproducible datasets
- INSERT statements for bulk data

**Characteristics**:
- Database-ready format
- Can be executed directly
- Preserves data types
- Good for documentation

**Example**:
```
INSERT INTO users (id, name, email, created_at, status)
VALUES (1, 'Alice Johnson', 'alice@example.com', '2023-01-15', 'active');
INSERT INTO users (id, name, email, created_at, status)
VALUES (2, 'Bob Smith', 'bob@example.com', '2023-01-16', 'active');
```

## Step 5: Configuring Export Options

Different formats have different configuration options:

### CSV Options

When exporting to CSV, you'll see:

- **Delimiter**: Character separating columns
  - Comma (,): Standard, compatible with most tools
  - Tab: Better for data with commas
  - Semicolon (;): Common in European systems

![Export Delimiter Comma](/images/67-export-delimiter-comma.png)

- **Include Headers**: Whether to include column names in first row
  - Usually: Yes (recommended)
  - Sometimes: No (if importing into existing structure)

- **Selected Rows vs. All**: Export only selected rows or all filtered results

### Excel Options

- **Include Headers**: Column names in first row
- **Auto-fit Columns**: Automatically size columns to content
- **Formatting**: Preserve date/number formatting
- **Sheet Name**: Name of the Excel sheet (default: Sheet1)

### Column Selection

In the export dialog, you can often select which columns to include:

- Deselect unnecessary columns to reduce file size
- Reorder columns by dragging
- Hide sensitive columns like passwords or API keys

## Step 6: Exporting Data

Let's walk through a complete export workflow:

### Workflow: Export Q4 Sales Report

**Step 1**: Apply filters as shown earlier (Q4 2024, completed status)

**Step 2**: Click the Export button:

![Export Dialog](/images/20-data-view-export-dialog.png)

**Step 3**: Review the export preview showing:
- Number of rows to export
- Selected columns
- Estimated file size

**Step 4**: Choose format. For a report to management: Excel

**Step 5**: Configure options:
- Format: Excel (.xlsx)
- Include headers: Yes
- Auto-fit columns: Yes

**Step 6**: Review column selection. Hide any sensitive data (passwords, API keys)

**Step 7**: Click "Export" button

**Step 8**: Browser downloads the file with timestamp:
- Default name: `export_[table_name]_[timestamp].xlsx`
- Example: `export_orders_2024-10-31_143022.xlsx`

Your file is now downloaded and ready for analysis!

## Step 7: Using Exported Data

### Opening in Excel

1. **Open Excel** and use File → Open to select your downloaded CSV or Excel file
2. **Data Import Dialog** appears for CSV files
   - Confirm delimiter matches export settings
   - Preview shows data looks correct
   - Click OK to import
3. **Format and Analyze**
   - Apply conditional formatting to highlight important values
   - Create pivot tables for summaries
   - Make charts for visualization

### Using in Google Sheets

1. **Create new spreadsheet** in Google Sheets
2. **File → Import** your CSV file
   - Choose "Replace current sheet" or "Insert new sheet"
   - Adjust import settings if needed
3. **Analyze and Share**
   - Use built-in functions for calculations
   - Share link with team members
   - Collaborate in real-time

### Analysis Examples

**Example 1: Revenue Summary**

After importing Q4 sales to Excel:
```
Create a pivot table:
Rows: Product Category
Values: Sum of Amount
Result: Total revenue by category for Q4
```

**Example 2: Customer Analysis**

After importing customers:
```
Create formulas:
- Average customer value: =AVERAGE(total_spent)
- Customers this year: =COUNTIF(signup_year, 2024)
- Churn rate: =COUNTIF(status, "inactive") / COUNTA(status)
```

**Example 3: Data Validation**

After importing user data:
```
Check for data quality:
- Blank cells: =COUNTBLANK(E:E)
- Invalid emails: =SUMPRODUCT(--(NOT(ISNUMBER(SEARCH("@",E:E)))))
- Duplicate IDs: Use Data → Remove Duplicates
```

## Step 8: Regular Scheduled Exports

For recurring reports, consider:

1. **Query Approach**: Create a saved query in Scratchpad that produces your export data
2. **Document the Process**:
   - Save the query
   - Document filter criteria
   - Store export instructions
3. **Reproduce**: Run the same query monthly/quarterly to get updated data
4. **Automation** (Advanced): Use WhoDB's API to automate exports programmatically

## Selected Rows Export

For smaller exports, you can select specific rows:

Click the checkbox at the left of each row you want to export, or use "Select All" for the current page:

![Table Row Selection Single](/images/44-table-row-selection-single.png)

Right-click or use the action menu:

![Context Menu Select Row](/images/45-context-menu-select-row.png)

Click "Export Selected Rows":

![Export Selected Rows Dialog](/images/46-export-selected-rows-dialog.png)

Proceed with format selection and configuration as before.

**When to use selected rows export**:
- Exporting a small sample
- Removing specific records before export
- Exact control over which rows to include
- Testing export process with minimal data

## Best Practices for Data Export

Never export entire tables if possible. Filtering ensures:
- Smaller, more manageable files
- Only relevant data for your analysis
- Reduced risk of sharing sensitive data
- Faster export and analysis

Start with WHERE conditions to narrow your dataset.

Before exporting, hide columns containing:
- Passwords or hashes
- API keys or tokens
- Full credit card numbers
- Personally identifiable information (PII) if not needed
- Internal notes or comments

Use column selection in the export dialog to deselect sensitive fields.

Save exports with meaningful names:
- Good: `Q4-2024-Sales-Report.xlsx`
- Good: `Active-Customers-October-2024.csv`
- Bad: `export.xlsx`
- Bad: `data.csv`

Include dates and purpose in filenames for easy identification.

Keep notes about exports:
- What filters were applied
- Why data was excluded
- Who received the export
- What analysis was performed

This helps with reproducibility and audit trails.

Before sending exported data to others:
- Check row counts match expectations
- Spot-check data for accuracy
- Verify formatting is correct
- Ensure no sensitive data is included
- Test with a colleague first if possible

Different audiences need different formats:
- **Excel**: Business stakeholders, presentations
- **CSV**: Technical teams, data import
- **JSON**: Developers, web applications
- **SQL**: Database administrators, backup/restore

Match the format to your audience's tools and comfort level.

## Common Export Scenarios

### Scenario 1: Monthly Sales Report

**Process**:
1. Filter orders: `created_at >= '2024-10-01' AND created_at < '2024-11-01'`
2. Include columns: order_date, customer_name, product, quantity, amount
3. Sort by: customer_name, order_date
4. Export format: Excel with formatting
5. Create pivot table in Excel by product category

### Scenario 2: Customer Segmentation Export

**Process**:
1. Filter customers: `status = 'active' AND last_purchase >= DATE_SUB(NOW(), INTERVAL 90 DAY)`
2. Calculate in query: total_spent, purchase_count, avg_order_value
3. Sort by: total_spent DESC
4. Export format: CSV for importing to marketing tool
5. Use in email campaign targeting

### Scenario 3: Data Backup Export

**Process**:
1. Export all tables from database
2. Format: SQL (INSERT statements)
3. Filename: `database_backup_2024-10-31.sql`
4. Store in backup location
5. Document the backup in your records

### Scenario 4: Privacy Compliance Export

**Process**:
1. Filter: `email = 'user@example.com'` (user requesting their data)
2. Include columns: All non-sensitive columns
3. Exclude columns: password, ip_address, internal_notes
4. Format: CSV or Excel, easy to read
5. Deliver: Secure transfer to user

## Troubleshooting Export Issues

**Problem**: Downloaded file can't be opened in Excel

**Solutions**:
- Verify file extension (.xlsx for Excel, .csv for CSV)
- For CSV: Try opening with different delimiter (comma, semicolon, or pipe)
- File might be corrupted: Try exporting again
- Check file isn't huge (>100MB may cause issues)

**Problem**: Numbers, dates, or formatting incorrect in exported file

**Solutions**:
- Check column data types (Explore view)
- Verify filters applied correctly before exporting
- For dates: Check date format matches expected
- For numbers: Ensure numeric columns exported as numbers, not text

**Problem**: File is too big to work with or send

**Solutions**:
- Add more filter conditions to reduce rows
- Use CSV instead of Excel (smaller file size)
- Split export into smaller time periods or categories
- Export specific columns only, hiding unnecessary ones

**Problem**: Exported data needs special formatting for your needs

**Solutions**:
- Export to Excel, then apply formatting there
- Use a query in Scratchpad to format data as SQL
- Export multiple times with different configurations
- Consider importing into analysis tool for custom formatting

## Next Steps

You've mastered data export and analysis. Continue learning:

Write advanced queries before exporting filtered results

Understand relationships between exported tables

Advanced export configurations and options

Learn to edit and manage data in WhoDB

## Summary

In this tutorial, you learned:
- How to identify your export needs and choose appropriate filters
- Applying precise WHERE conditions for targeted exports
- Understanding different export formats (CSV, Excel, JSON, SQL)
- Configuring export options for your use case
- Exporting data to files for external analysis
- Using exported data in Excel and Google Sheets
- Best practices for security, naming, and documentation
- Handling common export scenarios and troubleshooting issues
- Selecting specific rows for targeted exports

You now have the complete workflow for extracting data from WhoDB, analyzing it externally, and sharing it with colleagues and stakeholders.

---


# guides/tutorials/schema-visualization.mdx
## Schema Visualization with Graph View

Master WhoDB's Graph view to visualize database relationships, understand ERD diagrams, and explore table connections

# Schema Visualization with Graph View

Understanding how tables relate to each other is fundamental to working effectively with databases. While the Explore view shows individual table details, the Graph view reveals the complete picture—how your entire database fits together. This tutorial teaches you to harness the Graph view's power for schema visualization, relationship exploration, and database comprehension.

## What You'll Learn

By the end of this tutorial, you'll be able to:
- Open and navigate the Graph view
- Understand different relationship types (one-to-many, many-to-one)
- Explore table nodes and their connections
- Use zoom and pan controls for effective navigation
- Identify foreign keys and understand their meaning
- Handle complex multi-table relationships
- Use graph visualization for query planning
- Troubleshoot missing or incorrect relationships

This tutorial assumes you've completed the [Data Exploration Workflow](/guides/tutorials/data-exploration-workflow) tutorial and understand table structures and basic relationships.

## Prerequisites

Before starting, ensure you:
- Have WhoDB connected to a database with related tables
- Understand primary and foreign key concepts
- Are familiar with the sidebar navigation
- Have some familiarity with Entity-Relationship Diagrams (ERDs)

## The Graph View Interface

Let's start by exploring the Graph view interface:

![Graph View Schema Topology](/images/24-graph-view-schema-topology.png)

The Graph view displays your database schema as an interactive diagram with:

**Nodes**: Represented as rectangles, each node is a table. Node size relates to how many relationships it has.

**Edges**: Lines connecting nodes represent foreign key relationships. The direction and style indicate the relationship type.

**Canvas**: The background area where you pan and explore your schema. Use your mouse to navigate.

**Controls**: Toolbar at the top with navigation and view options.

Let's examine the control bar:

![Graph View with Controls](/images/25-graph-view-with-controls.png)

**Available Controls**:
- **Zoom In**: Magnify a section of your schema
- **Zoom Out**: See more of your schema at once
- **Fit View**: Auto-fit all nodes to the visible area
- **Reset**: Return to the default view
- **Settings**: Configure graph display options
- **Export**: Save the schema visualization as an image

## Understanding Relationship Types

Different relationships between tables tell different stories about your data. Let's explore them:

### One-to-Many Relationships

The most common relationship type:

![Graph One to Many Relationship](/images/92-graph-one-to-many-relationship.png)

**Structure**: One row in the parent table connects to many rows in the child table.

**Example**:
- One user can have many orders
- One category can have many products
- One company can have many employees

**Visual**: Line from parent (users) to child (orders) table

**Real-world meaning**: When you see a foreign key column in a table (like `user_id` in orders), it creates a one-to-many relationship. Many orders can reference the same user.

### Many-to-One Relationships

Viewed from the opposite direction:

![Graph Many to One Relationship](/images/93-graph-many-to-one-relationship.png)

**Structure**: Many rows in one table reference a single row in another.

**Example**:
- Many orders belong to one user
- Many products belong to one category
- Many employees work at one company

**Visual**: Appears as a line from child to parent

**Note**: Many-to-one is the same relationship as one-to-many, just viewed from the opposite direction.

### Multiple Foreign Keys

Complex tables can have multiple relationships:

![Graph Multiple Foreign Keys](/images/94-graph-multiple-foreign-keys.png)

**Structure**: A single table has multiple foreign keys to different tables.

**Example**: Order_items table with:
- Foreign key to orders (which order?)
- Foreign key to products (which product?)
- Possibly to inventory, pricing, etc.

**Significance**: These relationships tell a complete story. An order_item exists at the intersection of an order and a product, linking them together.

### Isolated Tables

Tables with no relationships:

![Graph Isolated Table Node](/images/91-graph-isolated-table-node.png)

**Why they exist**:
- Lookup tables (colors, statuses, countries)
- Standalone data collections
- Legacy tables no longer in use
- Temporary or archive tables

**Implications for queries**: Isolated tables can't be joined with other tables—you might use them for reference only.

## Step 1: Opening Graph View

In the main interface, click the "Graph" tab at the top to switch from Data or Explore view to Graph view.

WhoDB fetches relationship data from your database and renders the graph. This typically takes 2-5 seconds depending on schema complexity.

The graph displays with all tables visible. If your schema is complex, it might look crowded. Don't worry—we'll learn navigation techniques next.

## Step 2: Navigating the Graph

### Zooming

Use zoom controls to focus on specific areas:

**Zoom In**: Click the + button or scroll up with your mouse wheel
- Magnifies a section of your schema
- Shows table and column details more clearly
- Use when: Examining specific relationships or table details

**Zoom Out**: Click the - button or scroll down
- Shows more of your schema at once
- Reveals overall structure and organization
- Use when: Understanding complete database architecture

![Graph Zoomed In View](/images/95-graph-zoomed-in-view.png)

Shows zoomed-in view with relationships clearly visible.

![Graph Zoomed Out View](/images/96-graph-zoomed-out-view.png)

Shows zoomed-out view revealing overall structure.

### Panning

Click and drag the canvas to move around:
- Move your mouse to an empty area
- Click and hold, then drag to pan
- Explore different sections of your schema
- Release to stop

**Tip**: After zooming in, use panning to explore different areas without constantly zooming out.

### Fit View

![Graph Fit View Control](/images/97-graph-fit-view-control.png)

**Fit View** automatically adjusts zoom and pan to show all tables:
- Click the fit button
- All nodes become visible
- Optimal for getting an overview
- Use when: You want to see the complete schema at once

## Step 3: Examining Individual Tables

Click on any table node to see its details:

![Graph View Node Details](/images/26-graph-view-node-details.png)

**Node Details Panel** shows:

**Table Name**: The full name of the table (schema.table if applicable)

**Columns**: List of all columns in the table:
- Column name
- Data type (VARCHAR, INT, TIMESTAMP, etc.)
- Nullable indicator (YES/NO)
- Key type indicators (PK for Primary Key, FK for Foreign Key)

**Relationships**:
- Foreign keys pointing to other tables
- Primary keys being referenced by other tables

**Metadata**:
- Row count (approximate for large tables)
- Last modified time
- Table size

## Step 4: Understanding Relationship Flow

Trace the data flow through your relationships:

### Example 1: E-Commerce Database Flow

**Scenario**: You want to understand how orders relate to products.

Click the "orders" node. You see it has foreign keys to:
- users (who placed the order?)
- order_status (what's the status?)

Orders connects to order_items (one order has many line items).

Order_items connects to products (which product in the order?).

Products connects to inventory (stock levels).

You now understand the complete flow: User → Order → Order Items → Product → Inventory

This flow reveals the relationships you'd need to join in a query to answer questions like "How many items did user X order from category Y?"

### Example 2: Blog Database

**Structure**:
- users (authors)
- posts (content)
- comments (user feedback)
- tags (categorization)
- post_tags (many-to-many relationship)

**Relationships**:
- users → posts (one user writes many posts)
- users → comments (one user writes many comments)
- posts → comments (one post has many comments)
- posts → post_tags (one post has many tags)
- tags → post_tags (one tag appears on many posts)

**Analysis**: The post_tags table is crucial—it's a junction table enabling many-to-many relationships between posts and tags. This is a common pattern for flexible categorization.

## Step 5: Identifying Key Patterns

### Primary Keys

Look for tables with **PK** indicators. Primary keys:
- Uniquely identify each row
- Are indexed for fast lookup
- Often form foreign keys in other tables
- Are typically the small numeric IDs you see

**Pattern recognition**: Tables with PK columns that appear as FK in many other tables are often central hub tables. For example, `users.id` is referenced in orders, comments, transactions, etc. This makes users a central entity in your database.

### Foreign Keys

Look for tables with **FK** indicators. Foreign keys:
- Reference primary keys in other tables
- Enable relationships and data integrity
- Are used for joining in queries
- Should be indexed for performance

**Pattern**: Child tables have more FKs. For example, order_items might have FKs to orders, products, and warehouses—it exists at the intersection of multiple entities.

### Junction Tables

Identify many-to-many relationships through junction tables:

**Pattern recognition**:
- Table has 2+ foreign keys to other tables
- Few or no non-key columns
- Names often like "post_tags", "user_roles", "product_categories"
- Enables flexible relationships without duplication

**Example**: A post_tags table with just `post_id` and `tag_id`:
```
post_id | tag_id
1       | 5
1       | 7
1       | 12
2       | 5
2       | 9
```

This allows:
- One post tagged with multiple tags
- One tag applied to multiple posts
- Easy addition/removal of tags without modifying posts or tags tables

## Step 6: Using Relationships for Query Planning

Understanding relationships helps you plan queries:

### Planning a JOIN Query

**Question**: "Get all orders and the customer email for each order"

Start with orders table (source of data you want)

In the graph, find the connection from orders to users

You need: `SELECT orders.*, users.email FROM orders JOIN users ON orders.user_id = users.id`

The relationship tells you exactly the JOIN condition to use.

### Planning Multi-Table Analysis

**Question**: "Get product names and the number of times they've been ordered, with average order quantity"

- products (for product names)
- order_items (connects orders to products, has quantity)
- orders (to filter by date if needed)

- products → order_items (products has many order_items)
- order_items → orders (items belong to orders)

```
SELECT
  p.name,
  COUNT(oi.id) AS times_ordered,
  AVG(oi.quantity) AS avg_quantity
FROM products p
LEFT JOIN order_items oi ON p.id = oi.product_id
LEFT JOIN orders o ON oi.order_id = o.id
GROUP BY p.id, p.name
ORDER BY times_ordered DESC;
```

The graph relationships directly translate to the JOINs in your query.

## Step 7: Exporting and Sharing Your Schema

The Graph view helps you document your database structure:

### Taking Screenshots

1. **Arrange your graph** to show the most important relationships
2. **Zoom and pan** to focus on specific areas
3. **Use the export option** to save as an image
4. **Include in documentation** for onboarding new team members

### Creating Documentation

Use the graph visualization to document your database:

**For developers**:
- Shows what tables can be joined
- Clarifies primary/foreign key relationships
- Helps with query writing

**For stakeholders**:
- Visual understanding of data structure
- Relationship complexity overview
- Data flow from users to analytics

**For data teams**:
- Data lineage understanding
- Quality check reference
- Architecture documentation

## Step 8: Troubleshooting Missing Relationships

Sometimes expected relationships don't appear:

**Problem**: Expected relationship between tables doesn't show in graph

**Possible Causes**:
1. Foreign key constraints not defined in database
2. Relationship implemented via stored procedures
3. Database uses a different naming convention
4. Table just doesn't reference the other table

**Solutions**:
- Check in Explore view if foreign keys are defined
- Run query to verify referential integrity: `SELECT DISTINCT user_id FROM orders WHERE user_id NOT IN (SELECT id FROM users);`
- Consult database documentation
- Define foreign key constraints if they're missing

**Problem**: Multiple paths between same tables create confusion

**Causes**:
- Table has multiple foreign keys to same table
- Complex many-to-many relationships
- Bridge or junction tables creating multiple connection paths

**Solutions**:
- Zoom in on specific relationships
- Click nodes to read relationship details
- Trace one relationship at a time
- Use in your query planning carefully to avoid accidental cross-joins

**Problem**: Graph takes a long time to render or becomes unresponsive

**Causes**:
- Very large schema (100+ tables)
- Complex relationships create dense graph
- Browser performance limitations

**Solutions**:
- Zoom in on specific areas
- Use the filter or search to focus on relevant tables
- Reload the graph if it becomes unresponsive
- Consider using the Explore view for detailed table information

**Problem**: Graph view shows blank or incomplete

**Causes**:
- Initial load still in progress
- Browser JavaScript issue
- Database connection issue

**Solutions**:
- Wait a few seconds for initial load
- Refresh the browser
- Try fitting the view with the fit button
- Switch to another tab and back to Graph view
- Verify database connection is active

## Advanced Graph Navigation

### Searching for Tables

In complex schemas with many tables:
1. Use the search feature (if available) to locate a specific table
2. Click the table in results
3. Graph zooms and pans to show the selected table

### Filtering by Relationship Type

Some Graph implementations allow filtering:
- Show only tables related to users table
- Hide isolated tables
- Show only many-to-many relationships
- Display only tables with specific naming patterns

### Graph Configuration

Access graph settings to customize display:
- **Node size**: Based on relationships or custom
- **Node color**: By table type or category
- **Edge thickness**: By relationship strength
- **Layout**: Force-directed, hierarchical, circular
- **Labels**: Show full table names, column details

## Common Schema Patterns

Recognizing patterns helps you understand database design:

### Hierarchical Data

**Pattern**: Table with self-referencing foreign key

**Example**: `categories` table with `parent_category_id` column
```
id | name              | parent_category_id
1  | Electronics       | NULL
2  | Computers         | 1
3  | Laptops           | 2
4  | Gaming Laptops    | 3
```

**Visual in graph**: Node with a self-loop

**Query pattern**: Recursive queries or adjacency list traversal

### Time-Series Data

**Pattern**: Tables with timestamp columns and filtering on dates

**Example**: Daily metrics table with created_at

**Graph insight**: Temporal tables often have limited relationships—they reference core entities but aren't referenced much

**Query pattern**: Filter by date range, aggregate by time periods

### Slowly Changing Dimensions

**Pattern**: Audit or history tables tracking changes

**Example**: `user_snapshots` table capturing user state at different times

**Visual**: Relationship from users to snapshots table

**Implication**: Ability to track how data changes over time

### Dimensional Modeling (Data Warehouse)

**Pattern**: Central fact table with many dimension tables

**Example**:
- fact_sales (central): Sales transactions
- dim_customer (dimension): Customer information
- dim_product (dimension): Product details
- dim_time (dimension): Date/time information
- dim_location (dimension): Geographic data

**Visual in graph**: Central hub (fact table) with many connections

**Query pattern**: Star queries joining fact to multiple dimensions

## Case Study: Real-World Schema

Let's analyze a complete e-commerce database schema:

**Core entities**:
- users (customers)
- products
- orders
- order_items (junction between orders and products)

**Supporting tables**:
- categories (products organized by category)
- product_reviews (user feedback)
- inventory (stock levels)
- shipping_addresses (for delivery)
- payments (transaction records)
- coupons (discount codes)
- user_coupons (applied discounts)

**Relationship flow**:
1. User places Order
2. Order contains Order_items
3. Order_items reference Products
4. Products belong to Categories
5. Products have Inventory levels
6. Order uses Shipping_address
7. Order has Payment records
8. User applies Coupons

**Graph visualization reveals**:
- users is a central hub (orders, reviews, coupons)
- products is also central (orders via order_items, categories, inventory)
- Junction tables: order_items, user_coupons connect related entities
- Isolation: Categories, shipping_addresses have limited relationships

**Query implications**:
- Most queries start with users or products
- order_items is the key table for connecting orders to products
- Complex queries might combine user, order, and product information

## Best Practices for Schema Visualization

Periodically view your schema graph to:
- Spot new relationships added by migrations
- Identify orphaned or unused tables
- Recognize emerging patterns
- Plan future schema changes

Use graph visualization to document:
- Main data entities and their relationships
- Critical JOINs for common queries
- Data flow for reporting
- Onboarding material for new developers

When queries aren't working as expected:
- Check the graph for expected relationships
- Verify foreign key columns are correctly named
- Trace the data flow you expect from the query
- Find unexpected relationships that might be causing issues

Use graph images for team communication:
- Data team discussions
- Documentation
- Pull request reviews of schema changes
- Presentations to stakeholders

## Next Steps

You've mastered schema visualization. Continue learning:

Use your schema understanding to write advanced queries

Export analyzed data across related tables

Apply schema knowledge to efficient data exploration

Explore advanced graph view features

## Summary

In this tutorial, you learned:
- How to open and navigate the Graph view
- Understanding different relationship types (one-to-many, many-to-one)
- Examining individual table details within the graph
- Tracing data flow through relationships
- Identifying key database patterns (primary keys, foreign keys, junction tables)
- Using relationships for query planning
- Exporting and documenting your schema
- Troubleshooting missing or complex relationships
- Recognizing common schema design patterns
- Best practices for schema visualization

You now understand your database architecture at a deeper level and can use that knowledge for better queries, data exploration, and team communication.

---


# guides/tutorials/ai-first-query.mdx
## Your First AI-Powered Query

A beginner's guide to making your first query using WhoDB's AI Chat Assistant

# Your First AI-Powered Query

The AI Chat Assistant makes database exploration accessible to everyone, regardless of SQL knowledge. This tutorial walks you through your first AI-powered query, from opening the Chat page to understanding results. By the end, you'll be comfortable asking questions in natural language and interpreting what the AI tells you about your data.

## What You'll Learn

By the end of this tutorial, you'll be able to:
- Navigate to the Chat interface
- Understand the Chat page layout
- Configure an AI provider (simplified approach)
- Ask your first simple question
- Interpret AI responses
- Request data from specific tables
- View the SQL code behind AI responses
- Handle and learn from errors
- Ask follow-up questions to refine results

This tutorial assumes you've already connected to a database. If you haven't, complete the [First Database Connection](/guides/tutorials/first-database-connection) tutorial first.

## Prerequisites

Before starting, make sure you have:
- WhoDB connected to a database with data
- An AI provider configured (OpenAI, Anthropic, or Ollama)
- Basic familiarity with your database structure (what tables exist)

If you haven't set up an AI provider yet, see the [Setting Up AI Providers](/ai/setup-providers) guide. For this tutorial, any provider will work fine.

## Step 1: Opening the Chat Page

After connecting to your database, navigate to the Chat page:

![Chat Initial Page](/images/101-chat-initial-page.png)

The Chat page is your gateway to natural language database interaction. You'll notice several elements:

**AI Provider Dropdown**: At the top left, shows your current AI provider (OpenAI, Anthropic, or Ollama).

**AI Model Dropdown**: Next to the provider, displays the specific model being used (GPT-4, Claude 3.5 Sonnet, Llama 3.1, etc.).

**Chat Interface**: The main area where your conversation with the AI will appear.

**Input Box**: At the bottom, where you type your questions.

**Example Prompts**: Helpful suggestions to get you started with common questions.

The Chat page remembers your conversation history. You can refer back to previous questions and answers throughout your session.

## Step 2: Understanding Your AI Configuration

Before asking questions, let's verify your AI configuration. Click the AI Provider dropdown to see available providers:

![AI Provider Dropdown](/images/102-chat-ai-provider-dropdown.png)

You should see at least one configured provider:
- **OpenAI**: Cloud-based, fast and accurate
- **Anthropic**: Cloud-based, excellent for complex queries
- **Ollama**: Local, privacy-focused

Select your preferred provider if it's not already active.

Next, click the AI Model dropdown to see available models:

![AI Model Dropdown](/images/103-chat-ai-model-dropdown.png)

Different models offer different trade-offs:
- **More capable models** (GPT-4, Claude 3.5 Sonnet): Better accuracy, slightly slower
- **Faster models** (GPT-3.5 Turbo, Claude Haiku): Quick responses, good for simple queries
- **Local models** (Llama 3.1, Mistral): Complete privacy, speed varies by hardware

For your first query, any model will work well. Don't overthink this choice—you can always change it later.

## Step 3: Using Example Prompts

WhoDB provides example prompts to help you understand what kinds of questions you can ask:

![Example Prompts](/images/104-chat-example-prompts.png)

These examples demonstrate different query types:
- **List queries**: "Show me all users"
- **Filtered queries**: "Find products with price greater than 100"
- **Aggregation queries**: "Count orders by status"
- **Date-based queries**: "Show orders from last month"

Click any example prompt to see how it works. For this tutorial, let's start with something even simpler.

## Step 4: Asking Your First Question

Let's start with the most basic question possible. In the input box at the bottom, type:

```
What tables exist in this database?
```

Press Enter or click the send button.

Within a few seconds, you'll receive a response:

![Simple Text Response](/images/105-chat-simple-text-response.png)

The AI will list all tables in your database, providing a clear overview of your data structure. This simple question demonstrates the AI's awareness of your database schema.

**What just happened?**
1. Your question was sent to the AI provider
2. The AI analyzed your database structure
3. It generated an appropriate query (in this case, a schema query)
4. The results were formatted into a readable response

Questions about database structure ("What tables exist?", "What columns are in the users table?", "How are these tables related?") are great for learning and exploration.

## Step 5: Retrieving Actual Data

Now let's ask for actual data. Type this question:

```
Show me all users
```

Press Enter and watch what happens:

![SQL Query Results](/images/106-chat-sql-query-results.png)

The AI understands you want data from the users table and presents results in an interactive table. You'll see:

**Column Headers**: The fields from your users table (id, name, email, etc.)

**Data Rows**: Actual user records from your database

**Pagination Controls**: If there are many users, results are paginated for easy browsing

**Interactive Features**: You can scroll, sort columns, and explore the data just like in the Data view

You just retrieved data without writing any SQL. The AI understood your intent and generated the appropriate query automatically.

## Step 6: Viewing the SQL Code

Want to see what SQL query the AI generated? Click the "View Code" or code toggle button above the results:

![SQL Code View](/images/107-chat-sql-code-view.png)

You'll see the actual SQL query that was executed:

```
SELECT * FROM users;
```

This is valuable for several reasons:
- **Learning SQL**: See how natural language translates to SQL syntax
- **Verification**: Confirm the AI understood your question correctly
- **Refinement**: Copy and modify queries in the Scratchpad if needed
- **Debugging**: Understand what went wrong if results aren't what you expected

Toggle between the code view and results view to learn SQL patterns. Over time, you'll develop intuition about how questions map to queries.

## Step 7: Handling Errors Gracefully

Not all questions succeed on the first try. Let's see what happens when something goes wrong. Try asking:

```
Show me all customers from the products table
```

This question has a logical inconsistency—products tables don't typically have customer data:

![Error Message](/images/108-chat-error-message.png)

When errors occur, the AI provides helpful feedback:
- **What went wrong**: Clear explanation of the issue
- **Why it failed**: Context about the error
- **How to fix it**: Suggestions for rephrasing or correcting the query

Common error scenarios:
- **Ambiguous questions**: "Show me the data" (which table?)
- **Invalid operations**: Asking for columns that don't exist
- **Logic errors**: Combining incompatible filters
- **Permission issues**: Requesting operations your database user can't perform

Errors are learning opportunities. Read the error message carefully—it often tells you exactly how to fix your question.

## Step 8: Asking Follow-Up Questions

The real power of the AI assistant comes from conversation. The AI remembers your previous questions, enabling natural follow-ups.

After asking "Show me all users", try this follow-up:

```
How many are there?
```

The AI understands "there" refers to users from your previous question:

![Aggregation Query](/images/109-chat-aggregation-query.png)

The response shows a count of total users. Notice you didn't need to repeat "users"—the AI maintained context from your conversation.

**More follow-up examples:**

After viewing users:
- "Show only active ones" (filters to active users)
- "Sort by creation date" (reorders results)
- "What about users from last month?" (adds date filter)

Each question builds on the previous context, creating a natural exploration flow.

Think of the AI as a knowledgeable colleague sitting next to you. Have a conversation rather than asking isolated questions.

## Step 9: Filtering and Refining Results

Let's practice refining queries with specific criteria. Ask:

```
Show me users created in the last 7 days
```

The AI generates a query with date filtering and displays recent users. The results are automatically filtered to match your timeframe criteria.

Try more refined queries:
- "Show users with gmail addresses"
- "Find users who haven't logged in for 30 days"
- "Show the 10 most recent signups"

Each question demonstrates different filtering capabilities:
- **Pattern matching**: Email domain filtering
- **Date calculations**: Relative time periods
- **Sorting and limiting**: Top N results

You don't need to know date functions, pattern matching syntax, or sorting keywords. Just describe what you want in plain language.

## Step 10: Understanding Confirmations for Changes

The AI assistant is safe by design. When you ask to modify data, it requires explicit confirmation.

Try asking:

```
Delete the oldest user account
```

Instead of immediately deleting data, you'll see:

![Action Confirmation](/images/110-chat-action-confirmation.png)

The AI shows:
- **What will happen**: Clear description of the action
- **Which data will be affected**: Preview of rows to be changed
- **Confirmation required**: Explicit prompt to proceed or cancel

This safety feature prevents accidental data loss. You must respond with explicit confirmation:
- "Yes, proceed"
- "Confirm"
- "Do it"

Or cancel the operation:
- "No"
- "Cancel"
- "Never mind"

Always review confirmation prompts carefully before confirming deletions or updates. These operations cannot be undone.

After confirming:

![Action Executed](/images/111-chat-action-executed.png)

The AI confirms the action was executed and shows how many rows were affected.

## Step 11: Building a Multi-Message Conversation

Let's see how a real exploration session might flow with multiple related questions:

![Multiple Messages](/images/112-chat-multiple-messages.png)

A typical conversation might look like:

**You**: "What tables are related to orders?"

**AI**: Lists tables with foreign key relationships to orders

**You**: "Show me the most recent 5 orders"

**AI**: Displays last 5 orders with all columns

**You**: "What's the total value of these orders?"

**AI**: Calculates and shows the sum

**You**: "Show me the customer details for these orders"

**AI**: Joins orders with customers table, showing combined data

This conversation demonstrates:
- **Context preservation**: Each question builds on previous ones
- **Progressive refinement**: Starting broad, then getting specific
- **Natural flow**: Questions you'd naturally ask when exploring data

The AI maintains conversation context throughout your session, enabling natural, iterative exploration.

## Step 12: Moving Queries to Scratchpad

When you find a useful query, you might want to save it or modify it further. Click the "Move to Scratchpad" option on any AI response:

![Move to Scratchpad Dialog](/images/113-chat-move-to-scratchpad-dialog.png)

A dialog appears showing:
- The SQL query that will be moved
- Option to add notes or description
- Confirmation button

After moving to Scratchpad, you can:
- Edit and refine the SQL manually
- Save the query for future use
- Combine it with other queries
- Execute it repeatedly with modifications

This bridges AI exploration with traditional SQL workflow, giving you the best of both worlds.

## Step 13: Starting a New Chat

Each conversation session maintains its own context. When you want to start fresh, click the "New Chat" button:

![New Chat Button](/images/114-chat-new-chat-button.png)

This clears the conversation history and starts a new session. Use this when:
- Switching to a completely different topic
- Context from previous questions is confusing the AI
- You want a clean slate for a new analysis

Previous chat history is not saved. If you need to reference earlier queries, move important ones to Scratchpad before starting a new chat.

## Best Practices for AI Queries

**Good**: "Show me users who signed up in January 2024"
**Too vague**: "Show me some users"
**Too technical**: "SELECT * FROM users WHERE EXTRACT(MONTH FROM created_at) = 1"

Find the middle ground—specific intent in natural language.

Don't try to ask perfect questions immediately:

1. "Show me orders"
2. "Only completed ones"
3. "From the last month"
4. "With total value over 100"

Build complexity gradually through conversation.

If your database has ambiguous naming:

**Better**: "Show me user orders" (not just "orders")
**Better**: "Count active subscriptions" (not just "count subscriptions")

Clarify which table or status you mean.

Toggle to code view regularly to see how your questions translate to SQL. Over time, you'll:
- Understand SQL patterns
- Learn your database structure
- Write better natural language queries
- Develop SQL skills organically

The AI remembers context, so leverage it:

Instead of:
- "Show me all users"
- "Show me all active users"
- "Show me all active users from 2024"

Do this:
- "Show me all users"
- "Only active ones"
- "From 2024"

Each question refines the previous result.

For INSERT, UPDATE, or DELETE operations:
1. Read the confirmation message carefully
2. Check which rows will be affected
3. Verify it matches your intent
4. Consider testing on a small dataset first

Data modifications are permanent.

## Common First-Time Questions

When starting with the AI assistant, these questions are particularly useful:

- "What tables exist?"
- "Describe the users table"
- "How are orders and customers related?"
- "Show me all column names in products"

- "Show me 10 sample rows from users"
- "What does the data in products look like?"
- "Show me a few examples from orders"

- "Are there any NULL values in the email column?"
- "How many users have incomplete profiles?"
- "Find duplicate email addresses"

- "How many total users are there?"
- "What's the average order value?"
- "Count products by category"

## Troubleshooting Common Issues

**Possible causes**:
- Large database schema being analyzed
- Complex query generated
- Slow AI provider or network
- Local model (Ollama) processing on limited hardware

**Solutions**:
- Try a faster model (GPT-3.5 Turbo instead of GPT-4)
- Be more specific to reduce query complexity
- Switch to a cloud provider for faster response
- Simplify the question

**Possible causes**:
- Question was ambiguous
- AI misunderstood context
- Wrong table was queried

**Solutions**:
- View the generated SQL code
- Rephrase your question more specifically
- Provide table name explicitly: "from the users table"
- Ask a follow-up to clarify: "I meant the products table"

**Cause**: Asked for a column that doesn't exist in the table

**Solution**:
- Ask: "What columns are in the users table?"
- Check the Explore view for actual column names
- Use exact column names from your schema

**Possible causes**:
- Request is outside database operations
- Asking for external data not in your database
- Question is too ambiguous

**Solution**:
- Rephrase to focus on data in your database
- Be more specific about what you want
- Start with a simpler related question

## What You Learned

In this tutorial, you successfully:
- Navigated to the Chat interface
- Understood the Chat page layout and controls
- Verified your AI provider configuration
- Asked questions in natural language
- Retrieved data without writing SQL
- Viewed and understood generated SQL code
- Handled errors and learned from them
- Used conversation context for follow-up questions
- Applied filters and refinements naturally
- Understood safety confirmations for data changes
- Built multi-turn conversations
- Moved useful queries to Scratchpad

You've completed your first AI-powered database exploration. These fundamentals apply to all future interactions with the AI assistant.

## Next Steps

Now that you're comfortable with basic AI queries, explore more advanced capabilities:

Learn advanced querying techniques with joins and aggregations

Safely update, insert, and delete records using natural language

Master context management and multi-turn dialogues

Combine AI queries with traditional data views

## Congratulations

You've taken your first steps into AI-powered database interaction. The skills you learned here—asking clear questions, interpreting responses, refining through conversation—will serve you well as you explore more complex queries and analysis tasks.

Remember: the AI assistant is a tool to augment your capabilities, not replace your judgment. Use it to explore faster, learn SQL patterns, and focus on insights rather than syntax. With practice, you'll develop intuition about how to phrase questions effectively and when to leverage AI assistance versus traditional SQL.

The best way to improve is practice. Try asking different types of questions about your data. Each interaction teaches you more about both your database and how to communicate with the AI assistant.

---


# guides/roles/developers.mdx
## WhoDB for Developers

Master WhoDB workflows for local development, debugging, API development, and testing

# WhoDB for Developers

As a developer, you need fast, reliable tools to inspect database state, debug issues, and generate test data. WhoDB provides a streamlined workflow that integrates seamlessly into your development process, whether you're building features, debugging production issues, or writing tests.

This guide covers development-specific workflows, keyboard shortcuts, debugging techniques, and integration patterns that accelerate your coding velocity.

## Setting Up Your Development Environment

### Local Database Connection

For development work, connect WhoDB to your local or development database instance:

Create a dedicated development database separate from production:

```
# PostgreSQL example
createdb myapp_dev

# MySQL example
mysql -e "CREATE DATABASE myapp_dev;"

# SQLite example
touch dev.db
```

Use this database exclusively for development and testing.

Open WhoDB and connect using your development credentials:

![Login Form](/images/03-login-form-filled.png)

Store connection profiles for different environments:
- **Local**: Your laptop's database
- **Development**: Shared team development server
- **Staging**: Pre-production environment
- **Read-Only Production**: For investigation only

Never connect WhoDB with write access to production databases during active development.

After connecting, navigate to your tables and verify the schema matches your application code:

![Explore Table](/images/07-explore-users-table.png)

Check:
- Column names and types match your ORM models
- Indexes exist for frequently queried columns
- Foreign key relationships are correct
- Constraints align with application logic

### Integration with Development Tools

WhoDB works alongside your existing development stack:

**IDE Integration**
- Keep WhoDB open in a separate window while coding
- Use split-screen to see code and database side-by-side
- Reference table structure while writing queries in your code

**Terminal Workflow**
- Run your application in one terminal
- Keep database logs in another
- Use WhoDB for quick data verification without context switching

**Docker Development**
```
# docker-compose.yml example
services:
  db:
    image: postgres:15
    ports:
      - "5432:5432"
    environment:
      POSTGRES_DB: myapp_dev
      POSTGRES_USER: dev
      POSTGRES_PASSWORD: dev123
```

Connect WhoDB to `localhost:5432` to inspect your containerized database.

## Development Workflows

### Workflow 1: Feature Development with Live Data Inspection

When building new features, use WhoDB to verify database state in real-time:

Begin writing your feature code:

```
// Creating a new order
async function createOrder(userId, items) {
  const order = await db.orders.create({
    user_id: userId,
    status: 'pending',
    total: calculateTotal(items),
    created_at: new Date()
  });

  for (const item of items) {
    await db.order_items.create({
      order_id: order.id,
      product_id: item.product_id,
      quantity: item.quantity,
      price: item.price
    });
  }

  return order;
}
```

Run your application or test:

```
npm run dev
# or
npm test
```

Your code creates database records.

Without leaving your IDE, switch to WhoDB and refresh the table view:

![Data View](/images/10-data-view-table-content.png)

Check:
- Was the order record created?
- Are all order_items present?
- Do the totals match your calculations?
- Are timestamps correct?

**Keyboard Shortcut**: `Cmd/Ctrl + R` to refresh the current view.

If something doesn't look right, use filtering to isolate the problem:

![Where Conditions](/images/16-data-view-where-conditions-popover.png)

```
-- Quick debugging query
SELECT * FROM orders
WHERE user_id = 123
ORDER BY created_at DESC
LIMIT 5;
```

Identify exactly which records were affected by your code.

Make changes to your code and repeat the process:
- Update your code
- Run the application
- Verify in WhoDB
- Continue until correct

This tight feedback loop accelerates development significantly.

### Workflow 2: Debugging Production Issues Locally

When investigating production bugs, reproduce them locally:

If you have read-only production access, export a sample dataset:

![Export Dialog](/images/20-data-view-export-dialog.png)

Export as SQL INSERT statements to recreate the exact state locally:

```
-- Export filters to match the problematic records
SELECT * FROM orders
WHERE status = 'failed'
AND created_at > '2024-10-01'
LIMIT 100;
```

**Export as SQL** to get INSERT statements you can run locally.

Run the exported SQL on your local database:

```
psql myapp_dev < production_sample.sql
```

Now you have realistic production data to debug against.

Run your application code against this data:

```
// Test with production-like data
const problematicOrder = await getOrder(12345);
await processOrder(problematicOrder); // Does this fail?
```

Use WhoDB to check database state at each step:

![Scratchpad Query](/images/28-scratchpad-code-editor.png)

Write queries to understand what went wrong:

```
-- Check order state before processing
SELECT * FROM orders WHERE id = 12345;

-- Check related records
SELECT * FROM order_items WHERE order_id = 12345;

-- Look for anomalies
SELECT * FROM order_items
WHERE order_id = 12345
AND (quantity <= 0 OR price < 0);
```

After fixing the bug, verify the fix works with the production sample data. Then test with fresh mock data to ensure it works generally.

### Workflow 3: API Development and Testing

When building APIs, use WhoDB to verify data operations:

Before building endpoints, create realistic test data:

![Mock Data Dialog](/images/22-data-view-mock-data-dialog.png)

Generate:
- 50 users
- 100 products
- 200 orders
- Related order_items

This gives you a realistic dataset for API development.

Implement your API endpoint:

```
// GET /api/orders/:id
app.get('/api/orders/:id', async (req, res) => {
  const order = await db.orders.findOne({
    where: { id: req.params.id },
    include: ['user', 'items']
  });

  res.json(order);
});
```

Make API requests:

```
curl http://localhost:3000/api/orders/1
```

Check what queries your API generated by looking at recent data:

```
-- Did the endpoint access the right tables?
SELECT * FROM orders WHERE id = 1;

-- Were relationships loaded correctly?
SELECT * FROM order_items WHERE order_id = 1;
```

This helps optimize N+1 query problems and verify your ORM is generating efficient SQL.

Modify test data to create edge cases:

![Edit Row](/images/14-data-view-edit-row-dialog.png)

- Set `user_id` to NULL (if allowed) and test API response
- Set `total` to 0 and verify handling
- Delete related `order_items` and check cascade behavior

Your API should handle these gracefully.

### Workflow 4: Schema Migration Testing

Before deploying schema changes, test migrations locally:

Export your current development data:

```
-- Export all tables
pg_dump myapp_dev > backup_before_migration.sql
```

Execute your migration:

```
# Django example
python manage.py migrate

# Rails example
rails db:migrate

# Prisma example
npx prisma migrate dev
```

In WhoDB, navigate to the modified tables and check the Explore view:

![Explore Table Metadata](/images/08-explore-table-metadata.png)

Verify:
- New columns appear with correct types
- Old columns are removed (if intended)
- Indexes were created
- Foreign keys are correct
- Default values are set

WhoDB automatically refreshes schema information when you navigate between tables.

Check that existing data survived the migration:

```
-- Verify data integrity
SELECT COUNT(*) FROM orders; -- Should match pre-migration count

-- Check new columns have expected values
SELECT id, new_column FROM orders LIMIT 10;

-- Verify relationships still work
SELECT o.*, u.username
FROM orders o
JOIN users u ON o.user_id = u.id
LIMIT 5;
```

Run your application against the migrated schema:

```
npm test
```

If tests fail, use WhoDB to investigate which database operations are breaking.

## Debugging Techniques

### Using Scratchpad for Investigation

The Scratchpad is your primary debugging tool:

![Scratchpad with Multiple Cells](/images/31-scratchpad-multiple-pages.png)

**Technique 1: Step-Through Query Building**

When debugging complex queries, build them incrementally:

```
-- Cell 1: Start simple
SELECT * FROM orders LIMIT 5;

-- Cell 2: Add filtering
SELECT * FROM orders WHERE status = 'pending' LIMIT 5;

-- Cell 3: Add joins
SELECT o.*, u.username
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.status = 'pending'
LIMIT 5;

-- Cell 4: Add aggregation
SELECT u.username, COUNT(o.id) as pending_orders
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.status = 'pending'
GROUP BY u.username;
```

Execute each cell independently to see where issues arise.

**Technique 2: Data Validation Queries**

Create cells for common validation checks:

```
-- Check for orphaned records
SELECT o.id, o.user_id
FROM orders o
LEFT JOIN users u ON o.user_id = u.id
WHERE u.id IS NULL;

-- Check for duplicate emails
SELECT email, COUNT(*) as count
FROM users
GROUP BY email
HAVING COUNT(*) > 1;

-- Check for invalid states
SELECT * FROM orders
WHERE status NOT IN ('pending', 'processing', 'completed', 'cancelled');

-- Check for date anomalies
SELECT * FROM orders
WHERE created_at > NOW()
OR updated_at < created_at;
```

Save these cells and re-run them after code changes to verify data integrity.

**Technique 3: Performance Profiling**

Use EXPLAIN to understand query performance:

```
-- Check query plan
EXPLAIN ANALYZE
SELECT o.*, u.username, COUNT(oi.id) as item_count
FROM orders o
JOIN users u ON o.user_id = u.id
LEFT JOIN order_items oi ON o.id = oi.order_id
WHERE o.created_at > '2024-01-01'
GROUP BY o.id, u.username
ORDER BY o.created_at DESC;
```

Look for:
- Sequential scans on large tables (add indexes)
- High cost estimates (optimize query)
- Suboptimal join order (rewrite query)

### Query History for Debugging

All queries are saved automatically:

![Query History](/images/33-scratchpad-query-history.png)

Use history to:
- Recall debugging queries from previous sessions
- Compare query results before and after code changes
- Share queries with teammates investigating the same issue
- Clone and modify previous queries

**Keyboard Shortcut**: `Cmd/Ctrl + H` to open query history.

### Visual Schema Exploration

Use Graph view to understand relationships when debugging join queries:

![Graph View](/images/24-graph-view-schema-topology.png)

Benefits:
- See which tables are related
- Understand foreign key directions
- Identify missing relationships
- Plan complex joins visually

Click on table nodes to see column details:

![Graph Node Details](/images/26-graph-view-node-details.png)

This is especially useful when working with unfamiliar codebases or inherited databases.

## Mock Data Generation for Testing

### Generating Realistic Test Data

WhoDB's mock data generator creates realistic datasets for testing:

Navigate to any table and click "Generate Mock Data":

![Mock Data Dialog](/images/22-data-view-mock-data-dialog.png)

Choose settings based on your testing needs:

**Development Testing**: 10-50 rows
- Fast generation
- Easy to inspect manually
- Good for feature development

**Integration Testing**: 100-200 rows
- Realistic data volume
- Tests pagination
- Exercises joins with reasonable data

**Performance Testing**: Generate multiple batches
- 200 rows per batch (UI limit)
- Run multiple times for larger datasets
- Test query performance at scale

During active development, use append mode:

![Append Mode](/images/71-mock-data-append-mode.png)

This preserves any manually created test records while adding more data.

After generation, verify the data looks realistic:

```
-- Check data distribution
SELECT status, COUNT(*) as count
FROM orders
GROUP BY status;

-- Verify relationships
SELECT o.id, u.id
FROM orders o
LEFT JOIN users u ON o.user_id = u.id
WHERE u.id IS NULL;
-- Should return no rows

-- Check value ranges
SELECT
  MIN(created_at) as earliest,
  MAX(created_at) as latest,
  MIN(total) as min_total,
  MAX(total) as max_total
FROM orders;
```

### Integration Testing with Mock Data

Use generated data for automated testing:

```
// test/integration/orders.test.js
describe('Order Processing', () => {
  beforeAll(async () => {
    // Generate test data
    await generateMockData('users', 20);
    await generateMockData('products', 50);
    await generateMockData('orders', 100);
  });

  afterAll(async () => {
    // Clean up
    await db.orders.destroy({ where: {} });
    await db.products.destroy({ where: {} });
    await db.users.destroy({ where: {} });
  });

  test('processes pending orders correctly', async () => {
    const pending = await db.orders.findAll({
      where: { status: 'pending' }
    });

    for (const order of pending) {
      await processOrder(order.id);
    }

    // Verify in database
    const stillPending = await db.orders.count({
      where: { status: 'pending' }
    });
    expect(stillPending).toBe(0);
  });
});
```

**Tip**: Use WhoDB during test development to verify that your test setup creates the expected state.

## Productivity Tips and Shortcuts

### Keyboard Shortcuts

Master these shortcuts to work faster:

**Global Navigation**
- `Cmd/Ctrl + K`: Quick navigation menu
- `Cmd/Ctrl + /`: Toggle sidebar
- `Cmd/Ctrl + 1-9`: Switch between tabs
- `Esc`: Close current dialog/modal

**Data Grid**
- `Cmd/Ctrl + F`: Search current table
- `Cmd/Ctrl + R`: Refresh data
- `Arrow Keys`: Navigate cells
- `Enter`: Edit selected row
- `Cmd/Ctrl + A`: Select all rows
- `Tab` / `Shift + Tab`: Move between cells

**Scratchpad**
- `Cmd/Ctrl + Enter`: Execute query
- `Cmd/Ctrl + S`: Save query
- `Cmd/Ctrl + N`: New cell
- `Cmd/Ctrl + D`: Duplicate cell
- `Cmd/Ctrl + Shift + F`: Format query
- `Cmd/Ctrl + H`: Query history
- `Cmd/Ctrl + L`: Clear output

**Pro Tip**: Use `Cmd/Ctrl + Shift + F` to auto-format messy SQL queries.

### Multi-Cell Query Organization

Organize your debugging queries using multiple cells:

![Multiple Cells](/images/48-scratchpad-multiple-cells-results.png)

**Organization Pattern 1: By Feature**
```
Cell 1: User queries
Cell 2: Order queries
Cell 3: Payment queries
Cell 4: Analytics queries
```

**Organization Pattern 2: By Purpose**
```
Cell 1: Setup/Insert test data
Cell 2: Main query being developed
Cell 3: Validation checks
Cell 4: Cleanup/Delete test data
```

**Organization Pattern 3: By Version**
```
Cell 1: Original query (broken)
Cell 2: First fix attempt
Cell 3: Second fix attempt
Cell 4: Final working version
```

Execute cells independently with `Cmd/Ctrl + Enter` while focused in that cell.

### Filter Presets for Common Queries

Create reusable filter combinations for frequent checks:

![Where Conditions](/images/39-data-view-multiple-conditions.png)

**Development Filters**:
- Recent records: `created_at > NOW() - INTERVAL '1 hour'`
- Test users: `email LIKE '%@test.com'`
- Failed operations: `status = 'failed'`
- Your test data: `created_by = 'dev_user_123'`

Save these as Scratchpad cells for easy re-use:

```
-- Recent test user activity
SELECT * FROM users
WHERE email LIKE '%@test.com'
AND created_at > NOW() - INTERVAL '1 day'
ORDER BY created_at DESC;
```

### Export for Documentation

Export query results to document API responses or expected data formats:

```
-- Example: Document API response format
SELECT
  id,
  username,
  email,
  created_at,
  status
FROM users
WHERE id = 1;
```

Export as JSON and include in API documentation:

```
{
  "id": 1,
  "username": "john_doe",
  "email": "john@example.com",
  "created_at": "2024-01-15T10:30:00Z",
  "status": "active"
}
```

This ensures documentation matches actual database structure.

## Integration Patterns

### CI/CD Integration

Use WhoDB during local development, then automate database verification in CI:

```
# .github/workflows/test.yml
name: Test
on: [push]
jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_DB: test_db
          POSTGRES_USER: test
          POSTGRES_PASSWORD: test
        ports:
          - 5432:5432
    steps:
      - uses: actions/checkout@v2
      - name: Run migrations
        run: npm run migrate
      - name: Generate test data
        run: npm run seed
      - name: Run tests
        run: npm test
```

During development, use WhoDB to:
1. Verify migrations work locally
2. Check seed data looks correct
3. Debug test failures by inspecting database state

### Docker Compose Development Stack

Integrate WhoDB into your development stack:

```
# docker-compose.yml
version: '3.8'
services:
  db:
    image: postgres:15
    environment:
      POSTGRES_DB: myapp_dev
      POSTGRES_USER: dev
      POSTGRES_PASSWORD: dev123
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data

  app:
    build: .
    ports:
      - "3000:3000"
    depends_on:
      - db
    environment:
      DATABASE_URL: postgresql://dev:dev123@db:5432/myapp_dev

volumes:
  pgdata:
```

Run your stack:
```
docker-compose up
```

Connect WhoDB to `localhost:5432` to inspect your containerized database while your app runs.

### ORM Integration

WhoDB complements ORMs by providing visibility into generated queries:

**Prisma**
```
// After running Prisma queries
const users = await prisma.user.findMany({
  include: { orders: true }
});

// Verify in WhoDB what queries Prisma generated
// Check if it's using efficient joins or N+1 queries
```

**Sequelize**
```
// Enable query logging
const sequelize = new Sequelize({
  logging: console.log
});

// After operations, check actual database state in WhoDB
// Verify Sequelize's output matches expectations
```

**TypeORM**
```
// After complex queries
const result = await getRepository(User)
  .createQueryBuilder("user")
  .leftJoinAndSelect("user.orders", "order")
  .where("user.status = :status", { status: "active" })
  .getMany();

// Use WhoDB to verify the join worked correctly
// Check if the WHERE clause filtered as expected
```

## Debugging Common Development Issues

### Issue: ORM Generated Wrong Query

**Symptoms**: Results don't match expectations.

**Debug with WhoDB**:
1. Enable query logging in your ORM
2. Copy the generated SQL
3. Paste into WhoDB Scratchpad
4. Execute and examine results
5. Modify query to work correctly
6. Update ORM code to generate the correct query

```
-- ORM generated this (inefficient):
SELECT * FROM users;
-- Then for each user:
SELECT * FROM orders WHERE user_id = ?;

-- Should be (efficient):
SELECT u.*, o.*
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
```

### Issue: Migration Failed Partway Through

**Symptoms**: Database in inconsistent state.

**Debug with WhoDB**:
1. Check which tables were modified:
   ```
   SELECT table_name FROM information_schema.tables
   WHERE table_schema = 'public'
   ORDER BY table_name;
   ```

2. Check which columns exist:
   ```
   SELECT column_name, data_type
   FROM information_schema.columns
   WHERE table_name = 'problematic_table';
   ```

3. Manually complete or rollback the migration
4. Re-run the fixed migration

### Issue: Foreign Key Constraint Violation

**Symptoms**: Insert/update fails with constraint error.

**Debug with WhoDB**:
1. Check if referenced record exists:
   ```
   SELECT * FROM users WHERE id = 123;
   -- Returns nothing? That's your problem
   ```

2. Find orphaned records:
   ```
   SELECT o.*
   FROM orders o
   LEFT JOIN users u ON o.user_id = u.id
   WHERE u.id IS NULL;
   ```

3. Fix data or adjust application code

### Issue: Slow Query in Development

**Symptoms**: Operation takes too long.

**Debug with WhoDB**:
1. Run the slow query with EXPLAIN:
   ```
   EXPLAIN ANALYZE
   SELECT * FROM orders
   WHERE user_id = 123
   AND status = 'pending';
   ```

2. Look for "Seq Scan" on large tables
3. Add missing indexes:
   ```
   CREATE INDEX idx_orders_user_status
   ON orders(user_id, status);
   ```

4. Re-run EXPLAIN to verify improvement

## Best Practices

Never mix development and production data. Maintain strict separation:
- `myapp_dev` for local development
- `myapp_test` for running tests
- `myapp_staging` for pre-production testing
- `myapp_prod` for production (read-only access only)

Keep useful debugging and validation queries in version control:

```
project/
  sql/
    debug/
      check_orphaned_records.sql
      validate_user_emails.sql
      find_duplicate_orders.sql
    performance/
      slow_queries.sql
      missing_indexes.sql
```

Team members can use these when debugging similar issues.

When you discover data issues, document them:

```
-- BUG: Some orders have NULL user_id
-- Should not happen after 2024-01-15 migration
-- If found, investigate order creation code
SELECT * FROM orders WHERE user_id IS NULL;
```

Include these checks in your test suite.

Prevent development database bloat:

```
-- Weekly cleanup
DELETE FROM orders WHERE created_at < NOW() - INTERVAL '7 days';
DELETE FROM users WHERE email LIKE '%@test.com';
```

Or regenerate from scratch periodically.

If you must connect to production, use read-only credentials:

```
-- PostgreSQL
CREATE ROLE readonly;
GRANT CONNECT ON DATABASE myapp_prod TO readonly;
GRANT USAGE ON SCHEMA public TO readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;
```

This prevents accidental data modification.

## Summary

WhoDB accelerates development by providing:
- Real-time database state inspection during coding
- Powerful query debugging with Scratchpad
- Realistic mock data generation for testing
- Visual schema exploration for understanding relationships
- Fast keyboard-driven workflows

The tight feedback loop between code changes and database verification catches bugs early and helps you understand exactly how your code interacts with data.

You now have a comprehensive developer toolkit. From feature development through debugging and testing, WhoDB provides the visibility and control you need to build database-backed applications with confidence.

## Next Steps

Learn testing-specific workflows and techniques

Deep dive into test data generation patterns

Master advanced SQL debugging techniques

Learn all mock data options and patterns

---


# guides/roles/data-analysts.mdx
## Database Tool for Data Analysts - WhoDB

Advanced analytics tool for data analysts. Write complex SQL queries, filter large datasets, create analytics workflows, and export results for business intelligence with WhoDB.

# WhoDB for Data Analysts

As a data analyst, you need a powerful tool that bridges the gap between raw databases and actionable insights. WhoDB provides everything you need: interactive filtering, advanced SQL capabilities, complex query execution, and flexible data export—all without leaving your browser.

This guide covers analytics workflows, complex query patterns, performance optimization, and best practices for turning database queries into business insights.

## Analytics Workflow Overview

The typical analytics workflow in WhoDB follows four phases: explore, analyze, validate, and export.

Understand table structure, identify relevant data, apply initial filters

Write complex SQL, aggregate data, find patterns and trends

Verify queries, cross-check results, ensure data accuracy

Export in multiple formats, prepare for stakeholders and dashboards

## Phase 1: Data Exploration

Before writing complex queries, understand what data you're working with.

### Interactive Table Exploration

Select the table containing your analysis data.

![Data View](/images/09-data-view-users-table.png)

The data grid displays your records in a familiar spreadsheet format, making it easy to scan and understand the data structure.

Review each column:
- **Data types**: Are dates formatted correctly? Are numbers in the expected range?
- **Value distributions**: What values appear most frequently? Are there nulls?
- **Data quality**: Do you see obvious errors or anomalies?

This preliminary scan informs your analysis approach.

Start with simple filtering to narrow your focus:

![Filter UI](/images/16-data-view-where-conditions-popover.png)

Example filters:
- `status = 'active'` to focus on current records
- `created_at >= '2024-01-01'` to focus on recent periods
- `amount > 0` to exclude zero-value records
- `is_deleted = false` to remove archived data

Basic filters help you understand your dataset's composition.

Use search to locate specific values across columns:

![Search Highlight](/images/19-data-view-search-highlight.png)

Search is useful for:
- Finding specific user accounts or transactions
- Locating error messages or anomalous data
- Verifying whether specific data exists
- Quick spot-checks during analysis

### Understanding Relationships

Navigate to related tables to understand your data model:

Identify which columns link to other tables. This tells you:
- What related data is available
- How to write effective joins
- What aggregations make sense

Example: An `orders` table with a `user_id` foreign key tells you that order data can be linked to user data.

Understanding relationships helps you:
- Write joins efficiently
- Validate aggregation logic
- Identify data consistency issues

Example: If a customer table has 5,000 records but orders table shows 6,000 unique customer IDs, you have orphaned orders.

## Phase 2: Complex Analysis Queries

Once you understand your data, write sophisticated queries to extract insights.

### Opening the Query Interface

Open the Scratchpad view for full SQL development capabilities:

![Scratchpad Code Editor](/images/28-scratchpad-code-editor.png)

The Scratchpad provides:
- Full SQL syntax highlighting
- Query history and saved queries
- Result pagination and sorting
- Multiple query cells for iterative analysis

Start simple and build complexity:

```
SELECT
  COUNT(*) as total_records,
  COUNT(DISTINCT user_id) as unique_users,
  AVG(amount) as average_amount
FROM orders
WHERE created_at >= '2024-01-01';
```

This baseline query tells you the scale and scope of your dataset.

### Essential Analytics Queries

#### Aggregation & Summarization

Group data to find patterns and trends:

```
-- Sales by product category (most common analytics pattern)
SELECT
  category,
  COUNT(*) as transaction_count,
  SUM(amount) as total_revenue,
  AVG(amount) as avg_transaction_size,
  MAX(amount) as max_transaction,
  MIN(amount) as min_transaction,
  STDDEV(amount) as revenue_volatility
FROM orders
WHERE created_at >= '2024-01-01'
  AND status = 'completed'
GROUP BY category
ORDER BY total_revenue DESC;
```

GROUP BY queries are the foundation of analytics. Use them constantly to break data into meaningful segments.

#### Time Series Analysis

Track metrics over time to identify trends:

```
-- Monthly revenue trend with growth rate
WITH monthly_sales AS (
  SELECT
    DATE_TRUNC('month', created_at)::date as month,
    SUM(amount) as revenue,
    COUNT(*) as transaction_count
  FROM orders
  WHERE status = 'completed'
    AND created_at >= '2023-01-01'
  GROUP BY DATE_TRUNC('month', created_at)
)
SELECT
  month,
  revenue,
  transaction_count,
  LAG(revenue) OVER (ORDER BY month) as previous_month_revenue,
  ROUND(((revenue - LAG(revenue) OVER (ORDER BY month)) /
         LAG(revenue) OVER (ORDER BY month) * 100)::numeric, 2) as growth_percent
FROM monthly_sales
ORDER BY month DESC;
```

This reveals whether your metrics are growing, stable, or declining.

#### Cohort Analysis

Track how different user groups behave over time:

```
-- User retention by signup cohort
WITH user_cohorts AS (
  SELECT
    user_id,
    DATE_TRUNC('month', created_at)::date as signup_month
  FROM users
),
user_activity AS (
  SELECT DISTINCT
    user_id,
    DATE_TRUNC('month', activity_date)::date as activity_month
  FROM user_events
  WHERE event_type = 'purchase'
)
SELECT
  c.signup_month,
  EXTRACT(YEAR FROM AGE(a.activity_month, c.signup_month)) * 12 +
  EXTRACT(MONTH FROM AGE(a.activity_month, c.signup_month)) as months_since_signup,
  COUNT(DISTINCT c.user_id) as active_users
FROM user_cohorts c
LEFT JOIN user_activity a ON c.user_id = a.user_id
GROUP BY c.signup_month, months_since_signup
ORDER BY c.signup_month DESC, months_since_signup;
```

This shows which signup cohorts have the best retention and engagement.

#### Join-Based Analysis

Combine data from multiple tables:

```
-- Customer lifetime value analysis
SELECT
  c.customer_id,
  c.customer_name,
  c.segment,
  COUNT(o.order_id) as lifetime_orders,
  SUM(o.amount) as lifetime_value,
  AVG(o.amount) as avg_order_value,
  MAX(o.created_at) as last_purchase_date,
  MIN(o.created_at) as first_purchase_date,
  DATEDIFF(MAX(o.created_at), MIN(o.created_at)) as customer_tenure_days
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.customer_name, c.segment
ORDER BY lifetime_value DESC;
```

This powerful pattern combines customer attributes with their transaction history.

#### Window Functions for Context

Calculate metrics while preserving row-level detail:

```
-- Orders ranked by amount, with percentile context
SELECT
  order_id,
  customer_id,
  amount,
  ROW_NUMBER() OVER (ORDER BY amount DESC) as rank,
  PERCENT_RANK() OVER (ORDER BY amount) * 100 as percentile,
  SUM(amount) OVER (ORDER BY amount DESC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as running_total
FROM orders
WHERE created_at >= '2024-01-01'
ORDER BY amount DESC
LIMIT 100;
```

Window functions let you see individual records alongside aggregate context.

#### Anomaly Detection

Find unusual values that deviate from normal patterns:

```
-- Detect transactions that are statistical outliers
WITH transaction_stats AS (
  SELECT
    AVG(amount) as avg_amount,
    STDDEV(amount) as std_dev
  FROM orders
  WHERE created_at >= NOW() - INTERVAL '90 days'
)
SELECT
  o.order_id,
  o.customer_id,
  o.amount,
  ts.avg_amount,
  ROUND((o.amount - ts.avg_amount) / ts.std_dev, 2) as std_deviations_from_mean,
  CASE
    WHEN ABS(o.amount - ts.avg_amount) > (3 * ts.std_dev) THEN 'ANOMALY'
    WHEN ABS(o.amount - ts.avg_amount) > (2 * ts.std_dev) THEN 'UNUSUAL'
    ELSE 'NORMAL'
  END as classification
FROM orders o, transaction_stats ts
WHERE ABS(o.amount - ts.avg_amount) > (2 * ts.std_dev)
ORDER BY ABS(o.amount - ts.avg_amount) DESC;
```

This finds outliers that might indicate fraud, data errors, or significant business events.

### Query Execution & Results

Click the Execute button or press Ctrl+Enter:

![Query Results](/images/86-scratchpad-select-query-result.png)

The query runs and results appear below your query text.

Examine the result set:
- **Row count**: Does the number of rows make sense?
- **Value ranges**: Are values within expected ranges?
- **Null values**: Are nulls appearing where expected?
- **Data types**: Do columns display correct types?

Click column headers to sort:

```
Sales by Category (sorted descending by revenue):
Electronics | 5,400 | $850,000
Furniture   | 3,200 | $620,000
Clothing    | 8,100 | $580,000
```

Sorting helps identify top performers and outliers.

### Multi-Cell Queries

For complex analysis, break your work into multiple cells:

Build analysis step-by-step:

![Multiple Cells](/images/48-scratchpad-multiple-cells-results.png)

**Cell 1**: Clean and filter raw data
```
-- Get valid orders from the past year
SELECT *
FROM orders
WHERE status = 'completed'
  AND created_at >= '2023-01-01'
  AND amount > 0;
```

**Cell 2**: Aggregate into meaningful metrics
```
-- Calculate metrics by category
SELECT category, COUNT(*) as count, SUM(amount) as revenue
FROM valid_orders_cte
GROUP BY category;
```

**Cell 3**: Format for presentation
```
-- Add rankings and percentages
SELECT *,
  ROW_NUMBER() OVER (ORDER BY revenue DESC) as rank,
  ROUND(revenue / SUM(revenue) OVER () * 100, 1) as revenue_percent
FROM category_metrics;
```

This step-by-step approach is easier to debug and modify.

Access previous queries instantly:

![Query History](/images/33-scratchpad-query-history.png)

Your query history lets you:
- Find previously successful queries
- Clone and modify queries
- Track your analysis progression
- Recreate analysis later

## Phase 3: Data Validation

Before sharing analysis results, validate your queries.

### Validation Checklist

After any major operation, verify row counts make sense:
- Total rows: Does COUNT(*) match expectations?
- Filtered rows: Did your WHERE clause exclude as expected?
- Grouped rows: Is GROUP BY producing the right number of groups?

Suspicious row counts often indicate logic errors.

Check that values fall within expected ranges:
- Amounts: Are they positive? Below maximum limits?
- Dates: Are they between expected periods? Any future dates?
- Percentages: Do they sum to 100% or 1.0 as expected?
- IDs: Are they sequential or as expected?

Out-of-range values often indicate data quality issues.

Verify NULL behavior:
- COUNT(column) vs COUNT(*): Do they match? If not, you have NULLs.
- COALESCE usage: Are you handling NULLs appropriately?
- JOIN logic: Did LEFT JINs produce NULLs as expected?

NULLs are a common source of analysis mistakes.

Verify join results are correct:
- Compare JOIN results: Does INNER JOIN produce fewer rows than LEFT JOIN? (It should)
- Check for duplicates: Did your join multiply rows unexpectedly?
- Verify keys: Do the join keys match your expectations?

Incorrect joins are a frequent source of errors.

Cross-check aggregates:
- SUM vs COUNT with SUM: Do they align?
- Manual spot-checks: Pick a few groups and verify calculations manually
- Compare methods: Calculate the same metric different ways and verify results match

For time-based analysis, verify dates:
- Timezone handling: Are dates correct for your timezone?
- Date ranges: Do you include the expected periods?
- Seasonality: Do seasonal patterns make business sense?

## Phase 4: Exporting & Sharing

Once validated, export your analysis results.

### Export Workflow

Choose what to export:

![Export Dialog](/images/20-data-view-export-dialog.png)

Options:
- **All**: Every row in your result set
- **Selected**: Only rows you've highlighted
- **Filtered**: Rows matching your current filters

Select the format that matches your audience and downstream use:

![Export Format](/images/21-data-view-export-format-dropdown.png)

Best for:
- Excel or Google Sheets analysis
- Data science workflows (Python, R, pandas)
- Generic data exchange between systems
- Sharing with non-technical stakeholders

CSV is universal and widely supported.

Best for:
- Business stakeholders and executives
- Complex spreadsheet formatting
- Pivot tables and charts
- When you want to include formulas or formatting

More polished than CSV for business contexts.

Best for:
- NoSQL workflows
- JavaScript/web application integration
- Preserving nested data structures
- API-based data workflows

Best for:
- Database backups and restore
- Sharing with other databases
- Version control of specific datasets
- Reproducibility and documentation

SQL INSERT statements are human-readable and executable.

Customize the export:

**For CSV:**
- **Delimiter**: Comma (standard), semicolon (European), pipe (data with commas)
- **Encoding**: UTF-8 (universal), ISO-8859-1 (legacy)

**Column Selection:**
- Include/exclude specific columns
- Reorder columns to match report structure
- Exclude sensitive data like passwords or API keys

Download your file and do a quick sanity check:
- Row count matches query results
- Column order is correct
- Data formatting is readable
- No encoding issues with special characters

## Real-World Analytics Scenarios

### Scenario 1: Customer Segmentation

Identify different customer groups for targeted marketing:

```
-- Segment customers by behavior
SELECT
  c.customer_id,
  c.customer_name,
  c.segment_category,
  COUNT(o.order_id) as lifetime_orders,
  SUM(o.amount) as lifetime_value,
  MAX(o.created_at) as last_purchase,
  DATEDIFF(NOW(), MAX(o.created_at)) as days_since_purchase,
  CASE
    WHEN SUM(o.amount) > 5000 AND DATEDIFF(NOW(), MAX(o.created_at)) < 30 THEN 'High Value Active'
    WHEN SUM(o.amount) > 5000 AND DATEDIFF(NOW(), MAX(o.created_at)) < 90 THEN 'High Value At Risk'
    WHEN SUM(o.amount) > 1000 AND DATEDIFF(NOW(), MAX(o.created_at)) < 30 THEN 'Medium Value Active'
    WHEN SUM(o.amount) <= 1000 AND COUNT(o.order_id) >= 5 THEN 'Small Value Loyal'
    WHEN COUNT(o.order_id) = 1 THEN 'One-Time Buyer'
    ELSE 'Dormant'
  END as customer_segment
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.customer_name, c.segment_category
ORDER BY lifetime_value DESC;
```

Export this for marketing teams to enable targeted campaigns.

### Scenario 2: Churn Prediction

Identify customers at risk of leaving:

```
-- Find customers showing churn signals
WITH customer_activity AS (
  SELECT
    c.customer_id,
    c.customer_name,
    COUNT(o.order_id) as total_orders,
    MAX(o.created_at) as last_order_date,
    DATEDIFF(NOW(), MAX(o.created_at)) as days_inactive,
    AVG(o.amount) as avg_order_value,
    STDDEV(o.amount) as order_volatility
  FROM customers c
  LEFT JOIN orders o ON c.customer_id = o.customer_id
  GROUP BY c.customer_id, c.customer_name
)
SELECT
  *,
  CASE
    WHEN days_inactive > 180 AND total_orders < 5 THEN 'HIGH RISK'
    WHEN days_inactive > 90 AND total_orders < 10 THEN 'MEDIUM RISK'
    WHEN days_inactive > 60 THEN 'LOW RISK'
    ELSE 'STABLE'
  END as churn_risk
FROM customer_activity
WHERE days_inactive > 60
ORDER BY days_inactive DESC;
```

### Scenario 3: Product Performance Analysis

Identify your best and worst performing products:

```
-- Detailed product performance metrics
SELECT
  p.product_id,
  p.product_name,
  p.category,
  COUNT(DISTINCT oi.order_id) as times_sold,
  SUM(oi.quantity) as total_units_sold,
  SUM(oi.quantity * oi.price) as total_revenue,
  AVG(oi.price) as avg_price,
  MIN(oi.price) as min_price,
  MAX(oi.price) as max_price,
  ROUND(SUM(oi.quantity * oi.price) / NULLIF(COUNT(DISTINCT oi.order_id), 0), 2) as revenue_per_sale,
  ROW_NUMBER() OVER (PARTITION BY p.category ORDER BY SUM(oi.quantity * oi.price) DESC) as rank_in_category
FROM products p
LEFT JOIN order_items oi ON p.product_id = oi.product_id
GROUP BY p.product_id, p.product_name, p.category
ORDER BY total_revenue DESC;
```

## Performance Optimization for Large Datasets

### Query Optimization

Use indexes on heavily-filtered columns:
```
-- Check if your query uses indexes (PostgreSQL)
EXPLAIN ANALYZE
SELECT * FROM orders WHERE customer_id = 123;
```

If the plan shows "Seq Scan" on large tables, the column should be indexed.

Always filter to smaller date ranges:
```
-- SLOW: No date filter
SELECT * FROM transactions WHERE status = 'pending';

-- FAST: Date filtered
SELECT * FROM transactions
WHERE status = 'pending'
  AND created_at >= NOW() - INTERVAL '7 days';
```

Unnecessary joins slow queries significantly:
```
-- Only join tables you actually need
SELECT o.*, c.customer_name
FROM orders o
INNER JOIN customers c ON o.customer_id = c.customer_id
-- Don't add: JOIN order_items oi ON o.order_id = oi.order_id
-- if you don't use oi columns
```

Complex queries are easier to debug as multiple steps:
```
WITH step1 AS (
  SELECT * FROM raw_data WHERE filter
),
step2 AS (
  SELECT * FROM step1 WHERE another_filter
)
SELECT * FROM step2;
```

### Troubleshooting Query Issues

- Add time range filters to reduce rows
- Check indexes with EXPLAIN
- Simplify joins or split into multiple queries
- Pre-aggregate data in intermediate steps

- Check NULL handling in aggregates
- Verify JOIN logic (INNER vs LEFT JOIN)
- Validate WHERE conditions
- Check for timezone issues with dates

This happens with many-to-many joins:
```
-- PROBLEM: Orders have multiple items, customer table is duplicate
SELECT c.*, o.*, oi.*
FROM customers c
JOIN orders o ON c.id = o.customer_id
JOIN order_items oi ON o.id = oi.order_id;
-- Result: 3 order items means customer row appears 3 times

-- SOLUTION: Aggregate first
SELECT c.*, SUM(oi.price * oi.quantity) as order_total
FROM customers c
JOIN orders o ON c.id = o.customer_id
JOIN order_items oi ON o.id = oi.order_id
GROUP BY c.id;
```

## Best Practices for Analytics

Begin with a specific business question, not just exploration. This focuses your analysis.

Cross-check results different ways. Verify row counts and value ranges.

When exporting, document date ranges, filters applied, and what NULLs mean.

When possible, connect to read-only replicas to avoid accidental data modification.

Bookmark important queries for quick reuse and analysis reproducibility.

Build complex queries step-by-step. Test each piece before adding more complexity.

## Next Steps

Continue mastering WhoDB analytics:

Learn how to explore and understand new datasets

Master complex SQL patterns and optimization

Learn export customization for different audiences

Master filtering techniques for focused analysis

You now have a complete toolkit for data analysis in WhoDB. From exploring raw data through complex SQL queries to exporting polished results, you can answer virtually any question your database holds. The combination of interactive exploration, powerful query execution, and flexible export makes WhoDB an essential analytics companion.

---


# guides/roles/database-administrators.mdx
## Database Administration Tool - WhoDB for DBAs

Professional DBA tools for database administration. Manage schemas, monitor multiple databases, handle maintenance tasks, and administer database connectivity with WhoDB.

# WhoDB for Database Administrators

As a database administrator, you manage complex database infrastructure supporting multiple applications and teams. WhoDB provides DBA-specific capabilities for schema exploration, multi-database management, data integrity verification, and administrative maintenance tasks.

This guide covers schema management, multi-database workflows, data integrity checks, and monitoring patterns essential for effective database administration.

## DBA Administration Capabilities

Explore, understand, and document database schemas across your infrastructure

Manage multiple database instances, environments, and replica servers

Verify data consistency, check constraints, and identify orphaned records

Execute administrative queries and maintenance operations

## Multi-Database Management

### Managing Multiple Connection Profiles

Set up separate connection profiles for your database infrastructure:

![Login Form](/images/03-login-form-filled.png)

Typical DBA environment structure:
- **Production**: Primary production database (read-only access recommended)
- **Production Replica**: Read-only replica for analysis and backups
- **Staging**: Pre-production environment mirroring production
- **Development**: Shared team development database
- **Local**: Local development database for quick testing

Configure production connections with read-only credentials to prevent accidental modifications.

Quickly switch connection context using the database selector:

![Sidebar Database Selector](/images/34-sidebar-database-selector.png)

This lets you compare schemas or data across environments without reconnecting.

Use consistent naming for profiles:
- `prod-primary`, `prod-replica-1`, `prod-replica-2`
- `staging-main`, `staging-mirror`
- `dev-main`, `dev-local`

Clear naming prevents dangerous mistakes when connecting to production.

### Cross-Database Schema Comparison

Open your primary database and navigate to a table:

![Explore Table](/images/07-explore-users-table.png)

Review the table's structure and data.

Use the environment selector to switch to your second database without disconnecting from the first:

![Sidebar Schema Selector](/images/35-sidebar-schema-selector.png)

Navigate to the same table in the second database.

Document differences:
- Column names and types
- Constraints and indexes
- Default values
- Data distribution and counts

Use this to verify schema consistency across your database infrastructure.

## Schema Exploration & Documentation

### Understanding Your Database Structure

Open any table and review its structure:

![Explore Table Metadata](/images/08-explore-table-metadata.png)

Review:
- Column names, types, and nullability
- Primary keys and their data types
- Constraints (UNIQUE, NOT NULL, CHECK)
- Indexes and their coverage

Verify that each table has appropriate primary key definition:

![Primary Key Column](/images/98-explore-primary-key-column.png)

For effective DBA work, document:
- What is the primary key (usually ID or UUID)?
- Is it auto-incrementing or application-generated?
- Are there any surrogate keys vs natural keys?
- How are distributed systems handling ID generation?

Check how tables relate to each other:

![Foreign Key Columns](/images/99-explore-foreign-key-columns.png)

Document:
- Which tables reference which
- Referential integrity constraints
- Foreign key index presence
- Cascading behavior on deletes/updates

Review indexes to ensure optimal query performance:

![Explore Table With Indexes](/images/100-explore-table-with-indexes.png)

Check:
- Are heavily-queried columns indexed?
- Are all foreign keys indexed?
- Are there redundant or unused indexes?
- Is index maintenance scheduled appropriately?

### Visualizing Schema Topology

Access the schema graph to visualize your entire database structure:

![Graph View Schema Topology](/images/24-graph-view-schema-topology.png)

The graph displays:
- All tables and their relationships
- Foreign key connections
- Data flow between entities

Use the graph to understand your data model:

![Graph View With Controls](/images/25-graph-view-with-controls.png)

Identify:
- Central tables that many others depend on
- Orphaned or isolated tables
- Highly interconnected subsystems
- Potential data redundancy or denormalization

This visual helps you understand the impact of schema changes.

Click nodes to explore individual table relationships:

![Graph Node Details](/images/26-graph-node-details.png)

For each table, see:
- Incoming foreign keys (tables that reference this one)
- Outgoing foreign keys (tables this one references)
- Data flow implications
- Cascading dependencies for change management

## Data Integrity & Verification

### Integrity Checking Queries

Use the Scratchpad to write data integrity verification queries:

![Scratchpad Main View](/images/27-scratchpad-main-view.png)

Create a standard DBA query library for regular integrity checks.

Verify that foreign keys reference valid data:

```
-- Find orphaned orders (no matching customer)
SELECT o.order_id, o.customer_id
FROM orders o
LEFT JOIN customers c ON o.customer_id = c.customer_id
WHERE c.customer_id IS NULL;

-- Find dangling order items (no matching order)
SELECT oi.item_id, oi.order_id
FROM order_items oi
LEFT JOIN orders o ON oi.order_id = o.order_id
WHERE o.order_id IS NULL;
```

These queries identify data inconsistencies that violate referential integrity.

Check that data respects defined constraints:

```
-- Find rows violating NOT NULL constraints
SELECT * FROM users WHERE email IS NULL;

-- Find rows violating UNIQUE constraints
SELECT email, COUNT(*) as count
FROM users
GROUP BY email
HAVING COUNT(*) > 1;

-- Find rows violating business logic constraints
SELECT * FROM orders
WHERE total_amount <= 0
  OR created_at > NOW()
  OR status NOT IN ('pending', 'completed', 'cancelled');
```

Regular constraint verification catches data quality issues early.

Track table statistics for capacity planning:

```
-- Table size and row count analysis
SELECT
  schemaname,
  tablename,
  pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as total_size,
  (SELECT COUNT(*) FROM schemaname||'.'||tablename) as row_count
FROM pg_tables
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
```

This helps you understand table growth and storage requirements.

For auto-increment fields, verify no gaps or anomalies:

```
-- Find missing user IDs (gaps in sequence)
WITH sequence AS (
  SELECT generate_series(MIN(id), MAX(id)) as id
  FROM users
)
SELECT s.id
FROM sequence s
LEFT JOIN users u ON s.id = u.id
WHERE u.id IS NULL
ORDER BY s.id;
```

Gaps sometimes indicate deleted records or application issues.

### Data Quality Audits

Write comprehensive data quality queries:

![Scratchpad Query Results](/images/29-scratchpad-query-results.png)

```
-- Comprehensive data quality audit
SELECT
  CASE
    WHEN email IS NULL THEN 'Missing email'
    WHEN email NOT LIKE '%@%.%' THEN 'Invalid email format'
    WHEN phone IS NULL THEN 'Missing phone'
    WHEN created_at > NOW() THEN 'Future creation date'
    WHEN age < 0 OR age > 150 THEN 'Invalid age'
    WHEN DATEDIFF(NOW(), created_at) > 365*100 THEN 'Account older than 100 years'
    ELSE NULL
  END as quality_issue,
  COUNT(*) as affected_records
FROM users
WHERE (email IS NULL
  OR email NOT LIKE '%@%.%'
  OR phone IS NULL
  OR created_at > NOW()
  OR age < 0 OR age > 150
  OR DATEDIFF(NOW(), created_at) > 365*100)
GROUP BY quality_issue
ORDER BY affected_records DESC;
```

This aggregates quality issues for reporting and prioritization.

Examine the quality report:

![Scratchpad Query Error](/images/30-scratchpad-query-error.png)

Document:
- How many records have issues
- What types of issues are most common
- Which tables are most affected
- Severity of each issue type

Based on the audit:
- Identify root causes of quality issues
- Plan data cleanup procedures
- Implement validation rules to prevent recurrence
- Document findings for stakeholders

## Administrative Maintenance Tasks

### Schema Modification Verification

Before and after schema changes, verify your modifications:

```
-- Verify new column was added correctly
SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_name = 'orders'
ORDER BY ordinal_position;

-- Check index creation
SELECT indexname, indexdef
FROM pg_indexes
WHERE tablename = 'orders';

-- Verify constraint creation
SELECT constraint_name, constraint_type
FROM information_schema.table_constraints
WHERE table_name = 'orders';
```

Always verify schema changes after applying them. Document the modifications for audit trails.

### Statistics & Performance Monitoring

Monitor database performance over time:

```
-- Monitor slow queries
SELECT
  query,
  calls,
  total_time,
  mean_time,
  max_time
FROM pg_stat_statements
WHERE mean_time > 100
ORDER BY mean_time DESC
LIMIT 20;

-- Check table bloat (dead tuples)
SELECT
  schemaname,
  tablename,
  ROUND(dead_tuples::float / live_tuples * 100, 2) as dead_percent
FROM pg_stat_user_tables
WHERE live_tuples > 0
  AND ROUND(dead_tuples::float / live_tuples * 100, 2) > 10
ORDER BY dead_percent DESC;
```

Regular monitoring helps you maintain optimal database performance.

### Cleanup & Maintenance

Execute administrative cleanup tasks safely:

```
-- Delete old archive records (with backup first)
DELETE FROM audit_logs
WHERE created_at < NOW() - INTERVAL '2 years'
AND archived = true;

-- Mark records for deletion rather than immediate delete
UPDATE orders
SET is_deleted = true, deleted_at = NOW()
WHERE created_at < NOW() - INTERVAL '5 years'
  AND status = 'cancelled';
```

Always have backups before executing destructive operations. Test cleanup queries on non-production first.

## DBA Best Practices

Maintain documentation of your schema, constraints, relationships, and standard procedures.

Configure production connections as read-only to prevent accidents.

Schedule routine data integrity verifications to catch issues early.

Track schema changes and have a rollback plan for each modification.

Regularly check slow queries, indexes, and table statistics.

Test all queries and procedures on development/staging before production.

## Query Repository for DBAs

### Common DBA Queries

#### Database Health Check

```
-- Overall database health summary
SELECT
  datname,
  numbackends as active_connections,
  xact_commit as transactions_committed,
  xact_rollback as transactions_rolled_back,
  tup_returned as tuples_read,
  tup_fetched as tuples_fetched,
  tup_inserted as tuples_inserted,
  tup_updated as tuples_updated,
  tup_deleted as tuples_deleted
FROM pg_stat_database
WHERE datname NOT IN ('template0', 'template1', 'postgres');
```

#### Missing Indexes on Foreign Keys

```
-- Identify foreign keys without corresponding indexes
SELECT
  tc.table_name,
  kcu.column_name,
  'MISSING INDEX' as status
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name
WHERE tc.constraint_type = 'FOREIGN KEY'
  AND NOT EXISTS (
    SELECT 1 FROM information_schema.statistics s
    WHERE s.table_name = tc.table_name
    AND s.column_name = kcu.column_name
  );
```

#### Connection Analysis

```
-- Who is connected and what are they doing?
SELECT
  usename,
  application_name,
  state,
  query,
  state_change,
  query_start
FROM pg_stat_activity
WHERE datname = current_database()
  AND usename != 'postgres'
ORDER BY query_start DESC;
```

## Troubleshooting Common DBA Issues

Investigate connection leaks or long-running queries:

```
SELECT usename, COUNT(*) as connections
FROM pg_stat_activity
GROUP BY usename
ORDER BY connections DESC;
```

Check for idle connections or long-running transactions consuming resources.

Analyze execution plans to identify bottlenecks:

```
EXPLAIN ANALYZE
SELECT * FROM large_table WHERE status = 'active';
```

Look for sequential scans on large tables—these should use indexes.

Find the largest tables consuming space:

```
SELECT
  schemaname,
  tablename,
  pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename))
FROM pg_tables
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC
LIMIT 20;
```

Archive old data or add storage capacity based on findings.

Run comprehensive referential integrity checks:

```
-- Template for checking orphaned records
SELECT 'orders missing customers' as issue, COUNT(*) as count
FROM orders o
LEFT JOIN customers c ON o.customer_id = c.customer_id
WHERE c.customer_id IS NULL;
```

Fix identified violations and implement preventive measures.

Verify backup integrity periodically:

```
-- Check that recent backups exist and are current
SELECT * FROM backup_log
WHERE backup_date >= NOW() - INTERVAL '1 day'
ORDER BY backup_date DESC;
```

Ensure backups are recent, complete, and regularly tested.

## Next Steps

Enhance your DBA capabilities:

Master schema exploration and documentation

Learn advanced SQL for administrative tasks

Export schema documentation and reports

Master filtering for targeted administrative queries

With these DBA tools and patterns, you can effectively manage your database infrastructure, verify data integrity, track schema changes, and maintain optimal performance across multiple database instances. WhoDB provides the administrative capabilities essential for professional database administration.

---


# guides/roles/qa-testers.mdx
## Database Tool for Testing - WhoDB for QA Engineers

QA database testing tool for mock data generation, test data management, and database verification. Generate test data, verify database state, debug test failures with WhoDB.

# WhoDB for QA Engineers & Testers

As a QA engineer, you need tools to generate realistic test data, verify database state during testing, manage test environments, and debug database-related test failures. WhoDB provides everything you need to manage your testing database workflows efficiently.

This guide covers mock data generation, test data management, database verification, and debugging techniques essential for comprehensive quality assurance.

## QA Testing Capabilities

Generate realistic test data at scale for comprehensive testing

Manage, organize, and verify your test databases

Verify database state and data integrity during testing

Investigate test failures by inspecting database state

## Setting Up Your Test Environment

### Configure Test Database Connection

Set up WhoDB with your dedicated test database:

![Login Form](/images/03-login-form-filled.png)

Important:
- **Use a separate test database** from production and development
- **Reset between test runs** to maintain clean state
- **Use non-sensitive test credentials** (never use real user data)
- **Enable auto-refresh** if your test framework modifies the database

Confirm your test database schema matches your application code:

![Explore Users Table](/images/07-explore-users-table.png)

Check:
- All required tables exist
- Column names and types match your ORM models
- Foreign key relationships are defined
- Constraints match your business logic
- Indexes are present for expected query patterns

Mismatched schemas are a common source of test failures that pass locally but fail in CI/CD.

Create a setup guide for your team:

**Example Test Environment Setup:**
```
# PostgreSQL
createdb app_test
psql app_test < schema.sql

# MySQL
mysql -e "CREATE DATABASE app_test;"
mysql app_test < schema.sql

# SQLite
touch test.db
sqlite3 test.db < schema.sql
```

Share this with your team to ensure consistency.

## Mock Data Generation Workflow

### Generating Test Data

Open the table where you need to generate test data:

![Data View Users Table](/images/09-data-view-users-table.png)

The data grid shows existing records, helping you understand what test data to generate.

Click the "Generate Mock Data" button:

![Mock Data Dialog](/images/22-data-view-mock-data-dialog.png)

This opens the test data generation interface.

Select your data handling mode:

![Mock Data Append Mode](/images/71-mock-data-append-mode.png)

Always use **Append mode** during active testing. This preserves existing data and lets you delete generated data if something goes wrong.

Use **Overwrite** only when you're explicitly resetting the test database.

Choose the appropriate data volume:

![Row Count Minimum](/images/73-mock-data-row-count-min.png)

Different tests need different data volumes:

**Minimal Tests (1-10 rows):**
```
Use for:
- Unit test verification
- UI single-row operations
- Edge case testing
- Performance baselines with minimal data

Example: "Can I edit a single record correctly?"
```

![Row Count Medium](/images/74-mock-data-row-count-medium.png)

**Standard Tests (50-200 rows):**
```
Use for:
- Integration tests
- List/pagination testing
- Search and filter validation
- Basic performance testing

Example: "Can I paginate correctly through 100 records?"
```

**Load Tests (1000+ rows):**
```
Use for:
- Performance testing
- Sorting efficiency verification
- Large dataset handling
- UI responsiveness under load

Example: "Does the UI stay responsive with 10,000 records?"
```

![Row Count Maximum Clamped](/images/75-mock-data-row-count-max-clamped.png)

Execute the generation:

After generation completes, verify the data was created correctly:
- Count matches expectation
- Data types are correct
- Relationships are valid
- No errors in generated data

### Test Data Generation Strategies

#### Strategy 1: Minimal Data for Quick Tests

Generate just enough data to test a feature:

Generate 2-3 test users for basic functionality:

```
Test Data:
- 1 admin user
- 1 regular user
- 1 inactive user (for permission/role testing)
```

This lets you test basic CRUD operations quickly.

Generate minimal related records:

```
For order testing:
- 1 order per user
- 2-3 order items per order
- No historical data

This tests the basic relationships without bloat.
```

#### Strategy 2: Comprehensive Data for Integration Tests

Generate realistic data distributions for thorough testing:

Generate diverse user states:

```
100 test users with distribution:
- 60 active users (recent activity)
- 25 inactive users (no recent activity)
- 10 premium users (different tier)
- 5 suspended users (restricted access)
```

This tests different user states and access patterns.

Spread data across time ranges:

```
Orders with dates:
- 10% from past year (historical data)
- 50% from past 3 months (recent data)
- 40% from past week (current activity)

This tests time-range queries and reporting.
```

Vary record states:

```
500 orders with status distribution:
- 40% completed
- 30% pending
- 20% cancelled
- 10% failed

This tests filtering and status transitions.
```

#### Strategy 3: Edge Case Testing

Generate specific data to test edge cases:

Create records with boundary data:

```
-- After generation, manually add edge cases
INSERT INTO products VALUES
(999999999, 'Max Int ID', 999999.99),     -- Maximum values
(-1, 'Negative', 0.01),                  -- Negative values
(0, 'Zero', NULL);                       -- NULL values
```

Test data with special characters:

```
INSERT INTO users (name, email) VALUES
('O''Brien', 'test+tag@example.com'),   -- Quotes and special chars
('José García', 'josé@example.com'),     -- Unicode characters
('User, Test', 'comma@test.com');        -- Commas in data
```

Generate data for stress testing:

```
Large Dataset Tests:
- 10,000 user records
- 100,000 order records
- Test sorting, filtering, and export with large datasets
```

## Verifying Database State During Testing

### Checking Test Results

After running tests, open WhoDB to verify database state:

![Data View Table Content](/images/10-data-view-table-content.png)

Check:
- Were records created/updated/deleted as expected?
- Are relationships intact?
- Did the test modify the correct tables?

Apply filters to isolate test results:

![Data View Where Conditions Popover](/images/16-data-view-where-conditions-popover.png)

```
Example filters:
- created_at = TODAY (show today's records)
- status = 'test_pending' (show test-specific records)
- created_by = 'test_user' (show records from test)
```

This helps you verify specific test outcomes without data noise.

Right-click rows to see additional options:

![Context Menu](/images/13-data-view-context-menu.png)

Inspect individual records:
- View complete record data
- Check timestamps and metadata
- Verify related records

### Validating Database Integrity After Tests

Use filtering to verify record counts:

![Data View Add Row Dialog](/images/11-data-view-add-row-dialog.png)

After tests, verify:
- Correct number of records created
- Correct number of records deleted
- Correct number of records updated
- No orphaned records remain

Use Scratchpad for complex verification:

![Scratchpad Main View](/images/27-scratchpad-main-view.png)

```
-- Verify test data integrity
SELECT
  'Total test records' as check_name,
  COUNT(*) as expected,
  (SELECT COUNT(*) FROM orders WHERE created_at >= TODAY) as actual
FROM test_data;

-- Check for orphaned relationships
SELECT 'Orphaned orders' as check_name, COUNT(*) as count
FROM orders o
LEFT JOIN customers c ON o.customer_id = c.customer_id
WHERE c.customer_id IS NULL;

-- Verify constraints
SELECT 'Invalid states' as check_name, COUNT(*) as count
FROM orders
WHERE status NOT IN ('pending', 'completed', 'cancelled');
```

Examine verification results:

![Scratchpad Query Results](/images/29-scratchpad-query-results.png)

Document findings:
- Are all verifications passing?
- Are there unexpected data states?
- Do results align with test expectations?

## Debugging Test Failures

### Investigating Failed Tests

When a test fails, first determine the type:

```
Types of test failures:
- Assertion failure (code expected X, got Y)
- Timeout (operation took too long)
- Error (exception thrown)
- Data-related (unexpected database state)
```

Use WhoDB to examine what the database contains:

![Data View Query Filtered](/images/16-data-view-where-conditions-popover.png)

Apply filters to see the exact state when the test failed:
- Records created: Yes/No/Partial?
- Record values: Expected or different?
- Relationships: Intact or broken?
- Side effects: Any unintended modifications?

Document the discrepancy:

```
Test Expected:
- 1 order record with status='pending'
- 3 order_item records
- 1 customer reference

Database Contained:
- 0 order records
- No order_items
- Error likely in order creation logic
```

### Common Debugging Scenarios

The database modification worked, but the application processed data differently than expected:

```
-- Check what was actually created
SELECT * FROM orders
WHERE customer_id = @test_customer_id
ORDER BY created_at DESC
LIMIT 5;

-- Verify the data
SELECT COUNT(*) as items
FROM order_items
WHERE order_id IN (SELECT id FROM orders WHERE customer_id = @test_customer_id);
```

The discrepancy might be in:
- How your application reads the data
- Default values set by the database
- Timestamps or timezone handling
- Foreign key behavior

Your test data violates a database constraint:

```
-- Find the constraint violation
SELECT * FROM test_data
WHERE (invalid condition);

-- Example: Finding duplicate emails when unique constraint exists
SELECT email, COUNT(*) as count
FROM users
WHERE email IN (SELECT email FROM test_data)
GROUP BY email
HAVING COUNT(*) > 1;
```

Fix by:
- Adjusting test data generation
- Cleaning up before test
- Modifying test approach to work with constraints

Test cleanup didn't work as expected:

```
-- Find remaining test data
SELECT * FROM orders
WHERE test_flag = true
  OR created_by = 'test_user'
  OR created_at = TODAY;

-- Check if deletion actually happened
SELECT COUNT(*) as orphaned_items
FROM order_items
WHERE order_id NOT IN (SELECT id FROM orders);
```

This might indicate:
- Cleanup script didn't run
- Foreign key constraints preventing deletion
- Rollback didn't execute
- Wrong records were targeted for deletion

Generated test data contains unexpected values:

```
-- Find anomalous test data
SELECT *
FROM products
WHERE price < 0
  OR price > 1000000
  OR name IS NULL
  OR category = '';

-- Check for constraints not being met
SELECT * FROM orders
WHERE status NOT IN ('pending', 'completed', 'cancelled')
  OR amount <= 0
  OR created_at > NOW();
```

This indicates:
- Mock data generator has gaps
- Application isn't validating input
- Constraints aren't defined in the database
- Manual test data entry had errors

Application performance degrades with test data:

```
-- Check query performance with test data
EXPLAIN ANALYZE
SELECT * FROM orders
WHERE customer_id = @test_id
ORDER BY created_at DESC
LIMIT 10;

-- Check if indexes exist
SELECT indexname
FROM pg_indexes
WHERE tablename = 'orders'
  AND indexdef LIKE '%customer_id%';

-- Check table statistics
SELECT * FROM pg_stat_user_tables
WHERE relname = 'orders';
```

Debugging points:
- Missing indexes on filter columns
- Outdated table statistics
- Data volume too small to reveal real bottleneck
- Join inefficiency not apparent with small data

## Test Data Management Best Practices

Never test against production. Keep test data isolated.

Clean test data to avoid test interdependencies and false positives.

Document what test data each scenario needs and why.

Generate test data that mirrors real production data distributions.

Keep standard test datasets versioned for reproducibility.

Integrate mock data generation into your test setup scripts.

## Test Scenarios & Examples

### Scenario 1: User Registration Testing

Test user registration workflow with various data:

Create starting user set for testing:

```
20 existing users with variety:
- 10 active users
- 5 inactive users
- 5 premium users
```

Test new user creation:

```
Test Cases:
1. New user can register
2. Duplicate emails rejected
3. Invalid emails rejected
4. Password requirements enforced
5. Default preferences assigned
```

After registration tests, check:

```
SELECT
  COUNT(*) as total_users,
  COUNT(CASE WHEN created_at = TODAY THEN 1 END) as registered_today,
  COUNT(CASE WHEN is_active = true THEN 1 END) as active_count
FROM users;

-- Find any registration errors
SELECT * FROM registration_attempts
WHERE status = 'failed'
ORDER BY created_at DESC;
```

### Scenario 2: Order Processing Testing

Test order workflow with realistic data:

Create complete order test data:

```
100 test customers
500 test products
1000 test orders with distribution:
- 400 completed
- 300 pending
- 200 cancelled
3000 order items distributed across orders
```

Execute order processing tests:

```
Test Cases:
1. Create order from items
2. Calculate totals correctly
3. Apply discounts
4. Handle inventory updates
5. Process payment
6. Update order status
```

If order tests fail:

```
-- Check order creation
SELECT * FROM orders WHERE test_order_id = @id;

-- Check items were added
SELECT * FROM order_items WHERE order_id = @id;

-- Verify calculations
SELECT
  order_id,
  SUM(quantity * price) as calculated_total,
  total as stored_total,
  (total - SUM(quantity * price)) as discrepancy
FROM order_items
GROUP BY order_id
HAVING calculated_total != total;
```

### Scenario 3: Permission/Authorization Testing

Verify access control with role-based test data:

Create test users with different permissions:

```
Admin Users: 2
  - Full system access

Moderator Users: 5
  - Can view all content
  - Can delete/edit reported content

Regular Users: 50
  - Can view own content only
  - Limited actions

Guest Access: Verify properly denied
```

Run permission checks:

```
Test Cases:
1. Admin can access all resources
2. Moderators see moderation panel
3. Regular users see only their data
4. Guests get access denied
```

Check authorization was enforced:

```
-- Check access patterns
SELECT
  user_role,
  resource_accessed,
  COUNT(*) as access_count
FROM access_log
WHERE test_run_id = @run_id
GROUP BY user_role, resource_accessed;

-- Find any denied accesses
SELECT * FROM access_log
WHERE status = 'denied'
  AND test_run_id = @run_id;
```

## Integration with Test Frameworks

### Test Database Reset

Create a test setup script that uses WhoDB-compatible connections:

```
-- test_setup.sql
-- Run before each test suite

-- Disable foreign key checks during cleanup
SET FOREIGN_KEY_CHECKS = 0;

-- Truncate all test tables
TRUNCATE TABLE order_items;
TRUNCATE TABLE orders;
TRUNCATE TABLE customers;
TRUNCATE TABLE users;

-- Reset auto-increment
ALTER TABLE users AUTO_INCREMENT = 1;
ALTER TABLE customers AUTO_INCREMENT = 1;
ALTER TABLE orders AUTO_INCREMENT = 1;
ALTER TABLE order_items AUTO_INCREMENT = 1;

-- Re-enable checks
SET FOREIGN_KEY_CHECKS = 1;

-- Generate base test data
INSERT INTO users (name, email) VALUES
('Test User 1', 'test1@example.com'),
('Test User 2', 'test2@example.com');
```

### Continuous Integration Integration

Add database verification to your CI/CD pipeline:

```
#!/bin/bash
# test_verification.sh

# Run your tests
npm test

# Get database state after tests
TEST_RESULT=$?

if [ $TEST_RESULT -ne 0 ]; then
  # Capture database state for debugging
  mysql app_test -e "SELECT * FROM orders WHERE created_at = TODAY;" > test_orders.txt
  mysql app_test -e "SHOW ENGINE INNODB STATUS;" > innodb_status.txt
  echo "Test failed. Database state saved."
fi

exit $TEST_RESULT
```

## Next Steps

Enhance your QA testing with WhoDB:

Master all mock data generation options

Write verification queries for complex test scenarios

Master filtering for precise test data verification

Explore comprehensive development testing patterns

With WhoDB's test data generation and verification tools, you can confidently test your applications. Generate realistic data, verify behavior, debug failures, and ensure data integrity throughout your testing workflow. This comprehensive testing approach leads to higher quality software and fewer production issues.

---


# use-cases/database-exploration.mdx
## Database Exploration

Learn how to explore unknown databases, discover schemas, understand structure, and visualize relationships

# Database Exploration

One of WhoDB's core strengths is helping you understand unfamiliar databases. Whether you're onboarding to a new project, inheriting a legacy system, or analyzing a third-party database, WhoDB provides the tools to quickly grasp the structure and relationships.

This guide is perfect for developers joining new teams, DBAs managing multiple databases, and data analysts exploring new data sources.

## The Discovery Workflow

When you first connect to an unknown database, follow this systematic approach to build a comprehensive understanding:

After successfully connecting, you'll see the initial view of your database's structure.

![Storage Unit List](/images/05-storage-unit-list.png)

This view shows all the main storage units (tables, views, collections) in your default schema. Take a moment to scan the list and get a sense of scale—how many tables are there? What are they named?

Expand the sidebar to see the full organizational structure of your database.

![Sidebar Navigation](/images/06-storage-unit-list-with-sidebar.png)

The sidebar reveals the hierarchy:
- **Database**: Your current database connection
- **Schemas**: Logical groupings of tables (in PostgreSQL and MySQL)
- **Tables/Views/Collections**: Individual storage units

Look for patterns in naming. Do table names use prefixes? Suffixes? This reveals organizational structure and can hint at the business domains.

Click the "Explore" tab on any interesting table to view its complete structure.

![Explore Table](/images/07-explore-users-table.png)

This view shows critical information:
- Column names and data types
- Primary keys (marked distinctly)
- Nullable constraints
- Default values

Take notes on what you observe. Which columns are indexed? Which are required?

For deeper investigation, check the metadata panel showing comprehensive table information.

![Explore Metadata](/images/08-explore-table-metadata.png)

This panel reveals:
- **Storage statistics**: Row count, size, last modified
- **Indexes**: What's optimized for fast lookup?
- **Constraints**: Primary keys, unique constraints, check constraints
- **Relationships**: Foreign key connections to other tables
- **Table properties**: Engine, collation, and other database-specific settings

Switch to the Graph view to see how all tables connect to each other.

![Graph View](/images/24-graph-view-schema-topology.png)

The graph view instantly shows:
- **Table clusters**: Groups of related tables
- **Relationship chains**: How data flows through the database
- **Isolated tables**: Tables with no relationships
- **Hub tables**: Central tables connected to many others

This visual representation often reveals the true architecture that documentation may not capture.

Click on table nodes in the graph to see relationship details.

![Graph Node Details](/images/26-graph-view-node-details.png)

For each connection between tables, understand:
- **Cardinality**: One-to-many, one-to-one, or many-to-many relationships
- **Foreign keys**: Which columns establish the relationship
- **Direction**: Parent-to-child or child-to-parent
- **Purpose**: What business logic does this relationship represent?

## Understanding Database Structure

### Identifying Table Categories

As you explore, you'll notice tables fall into recognizable patterns:

These represent the main business concepts. Look for tables like:
- `users`
- `products`
- `orders`
- `accounts`

These are typically your starting point for analysis. They contain primary business data and have many relationships.

These record events and changes over time:
- `orders`
- `transactions`
- `payments`
- `audit_logs`

They typically have timestamps and reference core entities. Good for time-series analysis.

These contain static or slowly-changing data:
- `countries`
- `status_types`
- `categories`
- `permission_roles`

Usually small and connected via foreign keys. Understanding these helps decode enum-like values.

These enable many-to-many relationships:
- `user_roles`
- `product_tags`
- `course_enrollments`

Typically have composite keys and foreign keys to two other tables. Critical for understanding complex relationships.

These track metadata and history:
- `schema_migrations`
- `job_queue`
- `event_logs`
- `analytics_events`

Often prefixed with underscores or special names. May be safe to ignore for initial exploration.

## Practical Exploration Workflows

### Workflow 1: Map the Data Model

Open the Graph visualization and take a screenshot for documentation.

Use the layout controls in the graph to arrange nodes logically.

Look for tables with many outgoing relationships. These are typically your core entities (users, products, orders).

Follow the connections:
- From core entities, where do relationships lead?
- Are there circular dependencies?
- What's the depth of the entity hierarchy?

Create a mental (or written) map:
```
Users
├── Orders (1:N)
│   ├── Order Items (1:N)
│   │   └── Products (N:1)
│   └── Payments (1:1)
└── UserPreferences (1:1)
```

Look for tables that seem isolated or poorly connected. These might be:
- New features not yet integrated
- Historical data kept separate
- Denormalized copies for performance
- Administrative/system tables

### Workflow 2: Understand Column Semantics

Click through tables in explore mode to understand what data each column actually represents:

Look for patterns that reveal meaning:
- `created_at`, `updated_at`: Timestamps for tracking
- `is_active`, `is_deleted`: Boolean flags
- `_id`: Identifiers and foreign keys
- `_type`: Discriminator columns for inheritance
- `status`: Categorical values (often enum)

Understanding these patterns saves investigation time.

Different databases use types differently:
- **VARCHAR(255)**: Short text, often names or identifiers
- **TEXT**: Long text, descriptions or content
- **DECIMAL/NUMERIC**: Precise values like money
- **INTEGER vs BIGINT**: Check for potential overflow concerns
- **ENUM**: Fixed set of values in PostgreSQL
- **JSON/JSONB**: Flexible nested structures

The data type often hints at what the column represents.

- **NOT NULL**: Required data, always populated
- **UNIQUE**: No duplicates, often for identifiers
- **DEFAULT**: Automatic values, reveals business logic
- **CHECK**: Validation rules, defines data constraints
- **Foreign Key**: Cross-table references, defines relationships

Constraints reveal business rules embedded in the schema.

### Workflow 3: Explore Data Samples

To understand what data actually exists:

Click on a table to view its data in the main grid.

![Data View](/images/09-data-view-users-table.png)

Start with smaller tables or reference tables first—they're easier to understand quickly.

Don't just look at structure; look at actual values:
- What are real examples of IDs?
- What date ranges are represented?
- Are there many NULL values?
- What's the scale—10 rows or 10 million?

This reveals much about how the table is actually used.

Apply filters to understand data patterns:

![Filter UI](/images/16-data-view-where-conditions-popover.png)

- Filter by status to see different states of data
- Filter by date to understand temporal patterns
- Filter to find edge cases and exceptions

Use search to find specific patterns:

![Search Highlight](/images/19-data-view-search-highlight.png)

For instance, search for "test" or "admin" to find system records. Search for specific user IDs to trace their data through the system.

Look for red flags:
- Many NULL values might indicate unused columns
- Unusual data patterns might indicate legacy data
- Inconsistent formats might reveal data entry problems
- Extreme values (very old dates, huge numbers) might indicate data quality issues

### Workflow 4: Create Ad-Hoc Queries for Investigation

When basic exploration isn't enough, use the Scratchpad for deeper analysis:

Navigate to the Scratchpad view for SQL query execution.

![Scratchpad](/images/23-data-view-embedded-scratchpad.png)

You can also access a quick scratchpad directly from the data view.

Write queries that answer specific questions about the structure:

```
-- Find all tables and their row counts
SELECT table_name, row_count
FROM information_schema.tables
WHERE table_schema = 'public'
ORDER BY row_count DESC;

-- Find unused columns (columns that are mostly NULL)
SELECT column_name, COUNT(*) as non_null_count
FROM your_table
GROUP BY column_name
HAVING COUNT(*) < (SELECT COUNT(*) FROM your_table) * 0.01;

-- Find foreign key relationships
SELECT
  tc.table_name,
  kcu.column_name,
  ccu.table_name AS foreign_table_name
FROM information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu
  ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage AS ccu
  ON ccu.constraint_name = tc.constraint_name
WHERE tc.constraint_type = 'FOREIGN KEY';
```

Run the query and study the results. These metadata queries often reveal:
- Tables you didn't see in the sidebar (views, temporary tables)
- Relationships not immediately obvious
- Data distribution and scale

## Key Insights to Discover

As you explore, document these critical insights:

Total number of tables, rows in major tables, estimated data size. This determines what queries are safe to run.

The 3-5 most important tables and how they relate. This is your mental model of the database.

Constraints and relationships that encode business logic. These reveal what operations are valid.

Which tables are actively maintained vs. historical. Which columns are reliably populated.

Tables that connect to external systems, API data, or third-party services. Often marked by special naming or structure.

Heavily indexed tables or tables with special handling. These are typically your most-queried tables.

## Tips for Efficient Exploration

Check if the repository has documentation, entity diagrams, or SQL schema files. This provides context for your exploration.

Learn the naming conventions early. Understanding patterns means you can predict table and column names.

Export the graph view as a reference. Having a visual schema saved locally is invaluable.

Always explore on development or read-only databases first, not production.

While WhoDB helps you discover structure, talking to someone familiar with the system saves time on interpretation.

Keep notes on what you discover. Future you (and team members) will appreciate the documentation.

Database schemas often have comments or descriptions. Check the table/column comments in the metadata view.

## Common Exploration Challenges & Solutions

If your database has hundreds of tables:
- Filter by schema (if using PostgreSQL/MySQL)
- Start with the most recently modified tables
- Look for tables with "active" in the name
- Check git history for schema changes
- Use the graph view to identify clusters

If table/column names don't clearly indicate purpose:
- Read all column names in each table carefully
- Look at actual data values to understand purpose
- Check for comments or descriptions in metadata
- Look for patterns in foreign keys
- Write queries to understand relationships

If data seems duplicated across tables:
- Check for shadow columns (e.g., `user_name` in both `users` and `orders`)
- This often indicates performance optimizations or data warehousing
- Track back to the source of truth table
- Understand when denormalized data is updated

If you find tables that seem unused:
- Check if they have foreign key relationships
- Look at row counts and last modified dates
- These might be:
  - Legacy features no longer in use
  - Historical data kept for compliance
  - Migration staging tables
  - Development or test data accidentally left behind

## Next Steps

Now that you've explored the database structure, you're ready for:

Run analysis queries and export findings

Use mock data and testing workflows

Master advanced SQL queries

Learn how to export and share data

You now have the tools to systematically explore any database, understand its structure, and quickly become productive with new systems. The combination of visual schema exploration, metadata examination, and data sampling provides a comprehensive discovery process.

---


# use-cases/data-analysis.mdx
## Data Analysis

Use WhoDB for filtering, querying, and exporting data for analysis

# Data Analysis

WhoDB is a powerful tool for data analysts, business intelligence professionals, and anyone who needs to extract insights from databases. Whether you're answering business questions, preparing data for reports, or investigating anomalies, WhoDB provides the tools to analyze data efficiently.

This guide covers filtering, querying, exporting, and common analysis patterns you'll use daily.

## Data Analysis Workflow

### Phase 1: Explore and Filter

Start by understanding what data you're working with:

Find and open the table containing the data you want to analyze.

![Data View](/images/09-data-view-users-table.png)

The data grid shows your data in a familiar spreadsheet-like format, with all your records visible.

Use the filter interface to narrow down to relevant records.

![Filter UI](/images/16-data-view-where-conditions-popover.png)

Start with simple conditions:
- `status = 'active'` to focus on current records
- `created_at > '2024-01-01'` to focus on recent data
- `amount > 0` to exclude zero-value records

The filter interface supports all comparison operators:
- Equals (=)
- Not Equals (!=)
- Greater Than (>)
- Less Than (\<)
- Greater Than or Equal (>=)
- Less Than or Equal (\<=)

Chain multiple filters together to create complex conditions:

![Multiple Conditions](/images/40-scratchpad-action-result.png)

You can use AND logic to combine conditions:
- `status = 'active' AND created_at > '2024-01-01' AND amount > 100`

This focuses your analysis on exactly the subset you need.

Use the search feature to find specific text across all columns.

![Search Highlight](/images/19-data-view-search-highlight.png)

Quick searches are useful for:
- Finding a specific user by name or email
- Locating error messages or anomalous values
- Checking if specific data exists in the table

Control how many rows appear per page for better performance.

![Page Size Dropdown](/images/15-data-view-page-size-dropdown.png)

- Small tables (< 100 rows): Use 50-100 rows per page for better overview
- Large tables (> 1M rows): Use 10-25 rows per page to keep the interface responsive
- Filtered data: May be able to use larger page sizes since you've already reduced the result set

### Phase 2: Sort and Organize

Once you have the right subset, organize it for analysis:

Click a column header to sort data. Click again to reverse the sort order.

For analysis, sort by:
- **Date columns**: Most recent first (DESC) or chronologically (ASC)
- **Amount columns**: Largest first (DESC) to see extreme values
- **Category columns**: Alphabetically to group similar items
- **Status columns**: To group records by state

For complex analysis, sort by multiple columns:

Hold Shift while clicking column headers to add additional sort levels

Example: Sort by `category` then `amount DESC` to see:
- All product categories grouped together
- Within each category, highest amounts first

This reveals patterns like which categories have the most expensive items.

With properly sorted and filtered data, look for:
- **Outliers**: Extreme values at the top or bottom
- **Gaps**: Missing values or unusual jumps
- **Clusters**: Groups of similar values
- **Sequences**: Logical progressions or patterns

### Phase 3: Run Analysis Queries

For sophisticated analysis, switch to the Scratchpad to write custom SQL:

Navigate to the Scratchpad view or open the embedded scratchpad from your data view.

![Scratchpad Code Editor](/images/28-scratchpad-code-editor.png)

The Scratchpad is a full SQL development environment with syntax highlighting and execution controls.

Use SQL to answer specific questions about your data.

**Example 1: Summary Statistics**
```
SELECT
  COUNT(*) as total_count,
  COUNT(DISTINCT user_id) as unique_users,
  AVG(amount) as average_amount,
  MIN(amount) as minimum_amount,
  MAX(amount) as maximum_amount,
  STDDEV(amount) as std_deviation
FROM orders
WHERE status = 'completed'
AND created_at > '2024-01-01';
```

This reveals the scale and distribution of your data.

**Example 2: Time Series Analysis**
```
SELECT
  DATE_TRUNC('month', created_at) as month,
  COUNT(*) as order_count,
  SUM(amount) as total_revenue,
  AVG(amount) as avg_order_value
FROM orders
WHERE created_at > '2023-01-01'
GROUP BY DATE_TRUNC('month', created_at)
ORDER BY month DESC;
```

This shows trends over time, revealing seasonal patterns or growth/decline.

**Example 3: Category Analysis**
```
SELECT
  category,
  COUNT(*) as product_count,
  SUM(sales) as total_sales,
  AVG(price) as avg_price,
  MAX(price) as max_price
FROM products
WHERE active = true
GROUP BY category
ORDER BY total_sales DESC;
```

This breaks down performance by category, identifying your best and worst performers.

**Example 4: Cohort Analysis**
```
SELECT
  DATE_TRUNC('month', signup_date) as cohort_month,
  DATE_TRUNC('month', last_active) as activity_month,
  COUNT(*) as user_count
FROM users
WHERE signup_date IS NOT NULL
GROUP BY DATE_TRUNC('month', signup_date), DATE_TRUNC('month', last_active)
ORDER BY cohort_month DESC, activity_month DESC;
```

This tracks user retention by cohort—showing which signup cohorts remain active.

**Example 5: Anomaly Detection**
```
WITH stats AS (
  SELECT
    AVG(amount) as avg_amount,
    STDDEV(amount) as std_dev
  FROM transactions
  WHERE created_at > NOW() - INTERVAL '30 days'
)
SELECT
  t.*
FROM transactions t, stats
WHERE ABS(t.amount - stats.avg_amount) > (3 * stats.std_dev)
ORDER BY t.created_at DESC;
```

This finds unusual values that deviate more than 3 standard deviations—likely anomalies or fraud.

Execute the query to see your analysis results.

![Query Results](/images/86-scratchpad-select-query-result.png)

The results appear in a data grid below your query, using the same sorting and pagination features as the main data view.

Based on the results, refine your query:
- Add or adjust filters
- Include additional columns
- Try different time periods
- Adjust grouping or aggregation

Iteration is key to effective analysis.

All queries are automatically saved to history.

![Query History](/images/33-scratchpad-query-history.png)

Access your saved queries anytime. Frequently used analysis queries can be bookmarked for quick access later.

## Common Analysis Query Patterns

### Aggregations & Summaries

Use GROUP BY to summarize data by category:

```
-- Revenue by customer tier
SELECT
  customer_tier,
  COUNT(*) as customer_count,
  SUM(lifetime_value) as total_value,
  AVG(lifetime_value) as avg_customer_value
FROM customers
GROUP BY customer_tier
ORDER BY total_value DESC;
```

GROUP BY queries are the foundation of most business analytics—use them constantly to break down data into meaningful segments.

### Comparisons & Benchmarking

Compare performance across segments:

```
-- Compare this month vs last month
SELECT
  EXTRACT(MONTH FROM created_at) as month,
  EXTRACT(YEAR FROM created_at) as year,
  COUNT(*) as sales_count,
  SUM(amount) as revenue
FROM sales
WHERE created_at > NOW() - INTERVAL '2 months'
GROUP BY EXTRACT(YEAR FROM created_at), EXTRACT(MONTH FROM created_at)
ORDER BY year DESC, month DESC;
```

### Ranking & Top-N Analysis

Find your best and worst performers:

```
-- Top 10 products by revenue
SELECT
  product_name,
  SUM(quantity * price) as revenue,
  COUNT(*) as order_count,
  ROW_NUMBER() OVER (ORDER BY SUM(quantity * price) DESC) as rank
FROM order_items
GROUP BY product_name
ORDER BY rank
LIMIT 10;
```

### Joins for Cross-Table Analysis

Combine data from multiple tables:

```
-- Customers with their order summary
SELECT
  c.customer_id,
  c.customer_name,
  COUNT(o.order_id) as total_orders,
  SUM(o.order_total) as lifetime_value,
  MAX(o.created_at) as last_order_date
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.customer_name
ORDER BY lifetime_value DESC;
```

This pattern combines customer data with their order history in a single view.

### Window Functions for Context

Calculate metrics while maintaining row detail:

```
-- Orders with running total and percentile rank
SELECT
  order_id,
  amount,
  SUM(amount) OVER (ORDER BY created_at) as running_total,
  PERCENT_RANK() OVER (ORDER BY amount) * 100 as percentile
FROM orders
WHERE created_at > '2024-01-01'
ORDER BY created_at DESC;
```

Window functions let you see individual records alongside aggregate metrics—perfect for context.

## Exporting Analysis Results

Once you have your analysis, export the data for sharing or further processing:

You have three options:

![Export Dialog](/images/20-data-view-export-dialog.png)

- **Export All**: All rows in the table (after filters applied)
- **Export Selected**: Only rows you've selected
- **Export Filtered**: Only rows matching your current filters and search

For analysis, usually "Export Filtered" is most useful.

Select the format that best fits your downstream use:

![Export Format](/images/21-data-view-export-format-dropdown.png)

Best for:
- Excel or Google Sheets analysis
- Data science workflows (Python, R)
- Generic data exchange

CSV is the most compatible format.

Best for:
- Business users and non-technical stakeholders
- Complex spreadsheet formatting
- Pivot tables and charts
- When you want to include formulas

Provides a more polished deliverable.

Best for:
- API integration
- NoSQL databases
- JavaScript/web applications
- Preserving nested data structures

JSON preserves data types and relationships better than CSV.

Best for:
- Backup and restore
- Sharing data with other databases
- Version control of specific datasets
- Reproducibility

SQL INSERT statements are human-readable and executable.

Customize the export to your needs:

**Delimiter Options** (for CSV):
- Comma (standard, universal)
- Semicolon (common in European systems)
- Pipe | (good for data with commas)
- Tab (for tab-separated values)

**Column Selection**:
- Choose which columns to include
- Reorder columns if needed
- Exclude sensitive data

**Encoding**:
- UTF-8 (recommended, universal)
- ISO-8859-1 (for legacy systems)
- Others for specific regional needs

Click the Export button to download your file to your computer.

The file downloads immediately and is ready for use in your analysis tools, spreadsheets, or dashboards.

## Analysis Best Practices

Before diving into detail, get high-level summaries. Understand the scale and distribution first.

After any operation, check that your row counts make sense. Strange counts often indicate misunderstandings about the data.

Most analysis questions are time-bound. Always include date filtering to focus on relevant periods.

For complex analysis, break it into steps:
1. First query: Clean and filter raw data
2. Second query: Aggregate or calculate metrics
3. Third query: Format for presentation

This is easier to debug than one massive query.

Run the same analysis different ways to verify results:
- Sum vs COUNT with SUM: Do they align?
- JOIN vs LEFT JOIN: Any difference? If not, no NULL values exist
- Filter conditions: Do they capture what you expect?

When exporting analysis results, document:
- What date range was included?
- What filters were applied?
- What does NULL mean in this context?
- Were any rows excluded and why?

Context matters for interpretation.

If available, connect to a read-only replica for analysis queries. This ensures you can't accidentally modify data.

## Troubleshooting Common Analysis Issues

- **Add indexes**: Check if heavily-filtered columns are indexed
- **Reduce scope**: Add date ranges or other filters to reduce rows processed
- **Simplify joins**: Unnecessary joins slow queries significantly
- **Pre-aggregate**: Use intermediate queries to reduce data before final analysis

Try: `EXPLAIN` before your query to see the execution plan.

- **Check NULL handling**: NULLs often behave unexpectedly in aggregates
- **Verify joins**: LEFT JOIN vs INNER JOIN produces very different results
- **Validate filters**: Ensure your WHERE conditions match your intent
- **Consider timezones**: Time-based analysis often fails due to timezone confusion

Try: Run queries step-by-step, checking results at each stage.

- **Filter more aggressively**: Export only the rows you really need
- **Export fewer columns**: Remove unnecessary fields
- **Use JSON instead of CSV**: More compact representation
- **Split into multiple exports**: Export by date range or category

Large files are slow to open and manipulate in spreadsheet software.

- **Check delimiter**: Wrong delimiter creates misaligned columns
- **Check encoding**: Character encoding issues create strange symbols
- **Check date format**: Different systems expect different date formats
- **Verify NULL representation**: Different formats handle NULLs differently

Excel tip: Use "Text to Columns" to fix delimiter issues.

## Advanced Analysis Scenarios

### Scenario 1: Churn Analysis

Identify which customers are at risk of leaving:

```
-- Customers who haven't ordered recently
SELECT
  c.customer_id,
  c.customer_name,
  MAX(o.created_at) as last_order_date,
  NOW() - MAX(o.created_at) as days_since_order,
  COUNT(o.order_id) as lifetime_orders,
  SUM(o.amount) as lifetime_value
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.customer_name
HAVING MAX(o.created_at) < NOW() - INTERVAL '90 days'
ORDER BY days_since_order DESC;
```

Export these results to a customer success team for re-engagement campaigns.

### Scenario 2: Data Quality Audit

Find data anomalies and quality issues:

```
-- Records with potential quality issues
SELECT
  *,
  CASE
    WHEN email NOT LIKE '%@%.%' THEN 'Invalid email'
    WHEN phone_number IS NULL THEN 'Missing phone'
    WHEN created_at > NOW() THEN 'Future date'
    WHEN length(first_name) > 50 THEN 'Suspiciously long name'
    ELSE 'OK'
  END as quality_issue
FROM users
WHERE
  email NOT LIKE '%@%.%'
  OR phone_number IS NULL
  OR created_at > NOW()
  OR length(first_name) > 50
ORDER BY created_at DESC;
```

Export and share with the data team to fix underlying data entry processes.

### Scenario 3: Cohort Retention

Track how user retention changes by signup cohort:

```
WITH cohorts AS (
  SELECT
    user_id,
    DATE_TRUNC('month', created_at)::date as signup_month
  FROM users
),
activity AS (
  SELECT
    user_id,
    DATE_TRUNC('month', activity_date)::date as activity_month
  FROM user_activity
)
SELECT
  c.signup_month,
  EXTRACT(MONTH FROM a.activity_month - c.signup_month) as months_since_signup,
  COUNT(DISTINCT c.user_id) as active_users
FROM cohorts c
LEFT JOIN activity a ON c.user_id = a.user_id
GROUP BY c.signup_month, months_since_signup
ORDER BY c.signup_month DESC, months_since_signup;
```

This shows whether newer cohorts have better or worse retention than historical cohorts.

## Next Steps

Now that you're confident with data analysis, explore:

Use these same techniques with generated mock data

Explore new datasets before analyzing them

Master advanced SQL techniques

Learn export customization in depth

You now have a complete toolkit for data analysis. From filtering and sorting through complex SQL queries to exporting results, you can answer virtually any question your data can reveal. The combination of interactive filtering, powerful SQL execution, and flexible export makes WhoDB an ideal analysis companion.

---


# use-cases/testing-development.mdx
## Testing & Development

Use WhoDB for testing workflows, mock data generation, and development database management

# Testing & Development

One of WhoDB's most powerful features for developers is the ability to generate realistic mock data and quickly test database interactions. Whether you're writing integration tests, developing new features, or debugging database issues, WhoDB streamlines your testing workflows.

This guide covers mock data generation, query testing, and development workflows that accelerate your development cycle.

## Development Database Workflow

### Setting Up Your Development Database

First, connect WhoDB to your development or staging database—not production.

Use separate connection profiles for:
- **Development**: Local or cloud-hosted dev database
- **Staging**: Production-like environment for testing
- **Backups**: Read-only mirrors for analysis

Never test on production databases directly.

Navigate to tables you're working with and verify their current state.

Look for:
- Are there existing records?
- What's the current max ID or sequence number?
- Are there any constraints that affect testing?

This context is important for generating realistic test data.

For each table you'll test with, understand:
- Foreign key dependencies
- NOT NULL constraints
- UNIQUE constraints
- DEFAULT values
- Data type limits

This affects what mock data you can generate.

## Generating Mock Data

### The Mock Data Generator

WhoDB includes a powerful mock data generator for creating test datasets:

Click the "Generate Mock Data" button in your table view.

![Mock Data Dialog](/images/22-data-view-mock-data-dialog.png)

This opens the configuration interface for test data generation.

Set up how many rows you want to generate:

![Generation Options](/images/71-mock-data-append-mode.png)

Choose your data handling mode:
- **Append**: Add mock data to existing records (safe, reversible)
- **Overwrite**: Replace all existing data (use with caution on development only)

For most testing scenarios, append mode is safer because you can delete the generated data if something goes wrong.

Specify how many rows to generate:

![Row Count Options](/images/74-mock-data-row-count-medium.png)

Different scenarios need different data volumes:

Use for:
- Quick unit tests
- Testing single-row operations
- Debugging UI with minimal data
- Edge cases with small datasets

Example test: "Can I edit a single row correctly?"

Use for:
- Integration tests
- Feature development
- Testing with realistic pagination
- Performance testing with moderate data

Example test: "Does pagination work with 100 rows?"

Use for:
- Load testing
- Performance benchmarking
- Testing query optimization
- Realistic staging datasets

Example test: "Does search still work with 10,000 rows?"

The UI enforces a 200-row limit for data safety, but you can run multiple generations to accumulate more data.

Click "Generate" to create the mock data.

The generator creates:
- **Realistic column values**: Names, emails, dates, numbers
- **Foreign key relationships**: Valid references to related tables
- **Constraint compliance**: Respecting NOT NULL and UNIQUE constraints
- **Diverse data**: Multiple variations to test different code paths

After generation, verify the data was created correctly by viewing the table.

### Mock Data Generation Patterns

#### Pattern 1: Bootstrap a Complete Schema

For a brand new feature, populate all related tables at once:

If you have foreign key dependencies, generate in order:
1. Parent tables first (users, categories)
2. Middle tables (orders, products)
3. Junction tables (user_roles, product_tags)
4. Transaction tables (payments, events)

This ensures foreign key constraints are satisfied.

- Parent tables: 10-50 rows (users, companies)
- Transaction tables: 100-500 rows (orders, events)
- Detail tables: Same as parent (order items = orders)

This creates realistic data distribution.

Generate each table in append mode so they accumulate naturally.

For repeated testing cycles, you can clear and regenerate using overwrite mode.

#### Pattern 2: Test Specific Scenarios

Generate data that exercises particular code paths:

```
Example: Testing discount code functionality
1. Generate 50 users (append)
2. Generate 30 orders with various amounts
3. Generate 5 different discount codes
4. Manually update some orders to use discount codes
5. Test your discount logic against this dataset
```

Use a mix of generated data and manual entries for edge cases.

#### Pattern 3: Reproduce Production Issues

When debugging production issues locally:

Use your analysis queries to export a sample of real production data (safely anonymized).

```
-- Export 100 recent orders for local testing
SELECT * FROM orders
WHERE created_at > NOW() - INTERVAL '7 days'
LIMIT 100;
```

Import the data or use it as a template for mock data generation.

This gives you realistic data to test against.

Generate additional records to test edge cases you want to verify.

## Testing Workflows

### Workflow 1: Unit Testing with Mock Data

When writing unit tests that interact with a database:

Generate a consistent set of mock data before each test run.

```
// Test setup
beforeEach(async () => {
  // Generate 5 test users
  await generateMockData('users', { count: 5, mode: 'append' });
  // Generate 10 test orders
  await generateMockData('orders', { count: 10, mode: 'append' });
});

afterEach(async () => {
  // Clean up by deleting test records
  await deleteTestData();
});
```

This ensures tests start with known data state.

Verify your application can Create, Read, Update, Delete records:

```
test('can create new order', async () => {
  const newOrder = await createOrder({ user_id: 1, amount: 99.99 });
  expect(newOrder.id).toBeDefined();

  // Verify in WhoDB
  // Open table, filter by user_id, confirm new record appears
});

test('can update order status', async () => {
  await updateOrder(1, { status: 'shipped' });

  // Verify in WhoDB
  // Open table, view order 1, confirm status changed
});
```

Use WhoDB to verify edge case handling:

```
test('handles NULL values correctly', async () => {
  // Generate data with NULL optional fields
  // Verify application displays NULLs appropriately

  // In WhoDB: View table, confirm NULL fields show correctly
  // Filter by NOT NULL to see complete records
});

test('handles large numbers correctly', async () => {
  // Generate data with boundary values
  // Verify no overflow or truncation

  // In WhoDB: Check numeric columns for precision
});
```

After each test, quickly check results in WhoDB:

![Data View](/images/09-data-view-users-table.png)

Browse the data to confirm test operations succeeded, or filter to isolate test records.

### Workflow 2: Integration Testing

When testing how multiple components interact:

Generate mock data representing realistic application state:

1. **Users table**: 30 diverse user records
2. **Products table**: 20 products across categories
3. **Orders table**: 100 orders from the users
4. **Order items table**: Multiple items per order
5. **Reviews table**: Some products reviewed multiple times

This creates a rich dataset for integration testing.

Test complex workflows like:
- Placing an order and verifying inventory decreases
- Updating user profile and seeing changes reflected in order history
- Calculating totals correctly across multiple orders
- Applying discounts and verifying calculations

After each operation, verify results in WhoDB:

![Filter UI](/images/16-data-view-where-conditions-popover.png)

Use filters to isolate test records and confirm state changed correctly.

Use queries to verify data consistency:

```
-- Verify order items reference valid products
SELECT oi.order_id, oi.product_id
FROM order_items oi
LEFT JOIN products p ON oi.product_id = p.id
WHERE p.id IS NULL;

-- Verify order totals match line item sums
SELECT o.id, o.total, SUM(oi.quantity * oi.price)
FROM orders o
LEFT JOIN order_items oi ON o.id = oi.order_id
GROUP BY o.id
HAVING o.total != SUM(oi.quantity * oi.price);
```

Run these queries in Scratchpad after your integration tests to verify data integrity.

### Workflow 3: Performance Testing

When optimizing queries or testing with large datasets:

Create dataset sizes matching your production scale:

![Row Count Options](/images/75-mock-data-row-count-max-clamped.png)

- If production has 10K users, generate 10K test users
- If production has 1M orders, generate 100K-1M test orders
- Adjust down to test performance at smaller scales

Generate in stages: 100 rows → 1K rows → 10K rows to see where performance degrades.

In Scratchpad, write the queries your application will execute:

```
-- Count queries (common operation)
SELECT COUNT(*) FROM users WHERE status = 'active';

-- Join queries (common in user interfaces)
SELECT u.*, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.created_at > '2024-01-01'
GROUP BY u.id;

-- Search queries (common in applications)
SELECT * FROM products
WHERE name ILIKE '%laptop%' OR description ILIKE '%laptop%';
```

Execute these and note execution time.

For query optimization:

1. **Before**: Execute query with current code, note time
2. **Optimize**: Add index, rewrite query, etc.
3. **After**: Execute query with optimization, note time
4. **Compare**: Calculate performance improvement

```
-- Example: Adding index
-- Before optimization
SELECT * FROM orders WHERE user_id = 123;  -- 2.5 seconds

-- Add index
CREATE INDEX idx_orders_user_id ON orders(user_id);

-- After optimization
SELECT * FROM orders WHERE user_id = 123;  -- 0.05 seconds
-- 50x faster!
```

Document performance improvements for your team.

Simulate concurrent users:

```
// Simulate 100 concurrent queries
const queries = Array(100).fill(
  'SELECT * FROM orders WHERE user_id = ?'
);

const start = Date.now();
await Promise.all(queries);
const elapsed = Date.now() - start;

console.log(`100 queries completed in ${elapsed}ms`);
console.log(`Average: ${elapsed/100}ms per query`);
```

Monitor database performance in WhoDB while load tests run:
- Watch query execution times in Scratchpad
- Check if connection pool is being strained
- Verify no deadlocks occur

## Query Testing and Debugging

### Testing Query Logic

When writing complex queries, test them iteratively:

Begin with basic queries and build complexity:

```
-- Step 1: Start simple
SELECT * FROM users LIMIT 5;

-- Step 2: Add filtering
SELECT * FROM users
WHERE created_at > '2024-01-01'
LIMIT 5;

-- Step 3: Add aggregation
SELECT
  created_at::date,
  COUNT(*) as user_count
FROM users
WHERE created_at > '2024-01-01'
GROUP BY created_at::date;

-- Step 4: Add joining
SELECT
  u.id,
  u.username,
  COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.created_at > '2024-01-01'
GROUP BY u.id
ORDER BY order_count DESC;
```

Build incrementally, verifying results at each step.

After each query, ask:
- Do row counts make sense?
- Do values look reasonable?
- Are there unexpected NULLs?
- Do aggregates add up correctly?

![Query Results](/images/86-scratchpad-select-query-result.png)

Review the result grid carefully—errors often jump out visually.

Once your query works for normal data, test edge cases:

```
-- Original query (works for normal case)
SELECT u.*, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id;

-- Test 1: Users with NO orders (LEFT JOIN should include them)
-- Verify COUNT = 0 for users without orders

-- Test 2: Users with MANY orders
-- Verify COUNT is accurate for high-volume users

-- Test 3: Orders with NULL user_id
-- Verify these don't corrupt the results

-- Test 4: Recent data only
-- Add date filtering and verify results still correct
```

Write the same query multiple ways and compare results:

```
-- Approach 1: Using LEFT JOIN and COUNT
SELECT u.id, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id;

-- Approach 2: Using subquery
SELECT u.id, (SELECT COUNT(*) FROM orders WHERE user_id = u.id) as order_count
FROM users u;

-- Approach 3: Using window functions
SELECT DISTINCT u.id,
  COUNT(*) OVER (PARTITION BY u.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;

-- All three should give identical results
-- But they may have different performance characteristics
```

Understanding these differences makes you a better SQL developer.

### Debugging Failing Queries

When a query doesn't return what you expect:

If a complex query fails, identify which part:

```
-- Original failing query
SELECT u.id, u.name, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.created_at > '2024-01-01'
AND o.total > 100
GROUP BY u.id
HAVING COUNT(o.id) > 5;

-- Test each part individually:

-- Part 1: Users created recently
SELECT COUNT(*) FROM users WHERE created_at > '2024-01-01';
-- Result: 150 users

-- Part 2: Orders with total > 100
SELECT COUNT(*) FROM orders WHERE total > 100;
-- Result: 300 orders

-- Part 3: Join them (still no aggregation)
SELECT u.id, o.id
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.created_at > '2024-01-01'
AND o.total > 100
LIMIT 10;
-- Check if results look reasonable

-- Part 4: Add aggregation
SELECT u.id, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.created_at > '2024-01-01'
AND o.total > 100
GROUP BY u.id;

-- Part 5: Add HAVING clause
-- (now it should match the original query)
```

This isolation helps identify exactly which part is causing issues.

Verify your assumptions about the data:

```
-- Assumption: Every order has a user_id
SELECT COUNT(*) FROM orders WHERE user_id IS NULL;
-- If result > 0, you have invalid data to handle

-- Assumption: No duplicate user records
SELECT user_id, COUNT(*) as cnt FROM users GROUP BY user_id HAVING COUNT(*) > 1;
-- If results appear, you have duplicates

-- Assumption: Order totals are always positive
SELECT * FROM orders WHERE total <= 0;
-- If results appear, you need to handle this
```

Many query failures result from incorrect assumptions about data.

When a query runs slowly:

```
EXPLAIN ANALYZE
SELECT u.*, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id
ORDER BY order_count DESC;
```

The output shows:
- How many rows the database expects to process
- What indexes are being used
- Where time is being spent
- Opportunities for optimization

## Development Workflows with Mock Data

### Workflow: Feature Development Cycle

Generate initial mock data for your feature:
- User data (30 rows)
- Feature-specific data (100 rows)
- Edge case data (10-20 rows)

```
-- After generation, verify data distribution
SELECT status, COUNT(*) FROM users GROUP BY status;
-- Should show realistic distribution across active/inactive/pending
```

Build your feature against the mock data:
- Your code can query freely without affecting production
- You can modify test data as you discover new requirements
- You can regenerate completely if you make mistakes

After each coding session:
1. Run your feature
2. Check results in WhoDB
3. Verify data state changed correctly
4. Test edge cases

```
// Feature: Apply discount to order
const order = await getOrder(1);
const discounted = await applyDiscount(order, 'SUMMER20'); // 20% off
// Check in WhoDB: View order 1, verify total decreased by 20%
```

When something unexpected happens, write queries to understand why:

```
-- Why didn't discount apply?
SELECT id, original_total, discount_code, new_total,
  (original_total - new_total) as savings
FROM orders
WHERE discount_code = 'SUMMER20'
ORDER BY created_at DESC
LIMIT 5;
```

If you need fresh test data:

1. Export current test results (for documentation)
2. Delete all test records
3. Regenerate fresh mock data
4. Retry the development cycle

Before committing:
1. Delete all test mock data
2. Verify production (or staging) is unaffected
3. Run full test suite
4. Deploy changes with confidence

## Query Examples for Common Development Tasks

### Monitoring and Debugging

```
-- Recent errors logged
SELECT * FROM error_logs
ORDER BY created_at DESC
LIMIT 20;

-- Failed transactions
SELECT * FROM transactions
WHERE status = 'failed'
AND created_at > NOW() - INTERVAL '1 hour'
ORDER BY created_at DESC;

-- Database activity by table
SELECT table_name, COUNT(*) as activity_count
FROM audit_log
WHERE created_at > NOW() - INTERVAL '1 hour'
GROUP BY table_name
ORDER BY activity_count DESC;
```

### Data Validation

```
-- Orphaned records (foreign key violations)
SELECT o.id FROM orders o
LEFT JOIN users u ON o.user_id = u.id
WHERE u.id IS NULL;

-- Duplicate entries
SELECT email, COUNT(*) as cnt
FROM users
GROUP BY email
HAVING COUNT(*) > 1;

-- Data freshness
SELECT table_name, MAX(created_at) as last_record
FROM audit_log
GROUP BY table_name
ORDER BY last_record;
```

### Performance Investigation

```
-- Slowest queries in history
SELECT query, avg_execution_time, execution_count
FROM query_log
ORDER BY avg_execution_time DESC
LIMIT 20;

-- Table sizes and row counts
SELECT table_name, row_count, size_mb
FROM table_stats
ORDER BY size_mb DESC;

-- Most-accessed tables
SELECT table_name, access_count
FROM table_access_stats
ORDER BY access_count DESC;
```

## Best Practices for Development Testing

Never test on production. Use development or local databases exclusively.

Document what mock data you generated and why. This helps team members understand your test scenarios.

Build scripts or use WhoDB API to automatically generate test data for CI/CD pipelines.

Test with data volumes similar to production to catch performance issues early.

Keep analysis and debugging queries in version control. Future debugging of similar issues is easier with examples.

Delete old test data periodically to keep your development database lean and fast.

Use consistent prefixes or IDs for test data so you can easily identify and clean up your test records.

Before running destructive tests, export your current data state for rollback if needed.

## Next Steps

Ready to advance your development practices?

Learn how to explore unfamiliar databases

Use WhoDB for advanced data analysis

Master advanced SQL techniques

Learn comprehensive export capabilities

You now have a complete testing and development workflow. From generating realistic mock data through integrated testing with performance validation, you have tools to accelerate development and confidence in your database code. The combination of mock data generation, interactive querying, and rapid iteration creates a developer experience that catches bugs early and speeds up development cycles.

---


# use-cases/data-migration.mdx
## Data Migration

Complete guide to using WhoDB for database migration projects. Export, transform, validate, and import data between databases with confidence.

# Data Migration

Database migrations are critical operational events that require precision, validation, and confidence. Whether you're migrating from one database platform to another, consolidating multiple databases, or modernizing your infrastructure, WhoDB provides the tools to execute migrations safely and verify data integrity throughout the process.

This guide covers planning, exporting, validating, and importing data. Real-world migration workflows with SQL examples and step-by-step validation procedures.

## Understanding Database Migrations

A successful migration involves multiple phases, each with specific objectives and validation requirements:

1. **Planning & Assessment**: Understand your source data
2. **Extraction**: Export data from the legacy system
3. **Transformation**: Clean and prepare data for the new system
4. **Validation**: Verify data integrity before import
5. **Import**: Load data into the new database
6. **Post-Migration Verification**: Confirm migration success

WhoDB excels at phases 1, 2, 3, 4, and 6—the critical data validation and transformation stages.

## Phase 1: Pre-Migration Assessment

Before migrating a single row of data, understand what you're working with.

In WhoDB, connect to your source database—the system you're migrating FROM. This might be a legacy MySQL database, an old PostgreSQL instance, or even SQLite.

![Database Types](/images/01-login-page.png)

WhoDB supports multiple database types, making it ideal for cross-platform migrations.

Navigate through the Explore tab to understand your database structure.

![Explore Schema](/images/08-explore-table-metadata.png)

For each table, document:
- **Table names and purposes**: What business data does each table hold?
- **Column count and types**: How complex is each table?
- **Primary keys and indexes**: How is data organized?
- **Foreign key relationships**: What dependencies exist?
- **Row counts**: How much data are you migrating?

This context is crucial for planning your migration strategy.

Use WhoDB's filtering and query capabilities to identify data quality problems before migration:

```
-- Find NULL values in critical fields
SELECT * FROM customers
WHERE email IS NULL OR phone IS NULL;

-- Find duplicates that might violate constraints
SELECT email, COUNT(*) as cnt
FROM customers
GROUP BY email
HAVING COUNT(*) > 1;

-- Find orphaned records (foreign key issues)
SELECT o.* FROM orders o
LEFT JOIN customers c ON o.customer_id = c.id
WHERE c.id IS NULL;

-- Find unusual data types or formatting
SELECT * FROM orders
WHERE order_date > NOW();  -- Future dates

-- Find empty or invalid strings
SELECT * FROM products
WHERE name = '' OR TRIM(name) = '';
```

Export these results to a validation report for your team to review.

Calculate the total data volume:

![Data View](/images/09-data-view-users-table.png)

Use queries to count records by table:

```
SELECT
  'customers' as table_name, COUNT(*) as row_count FROM customers
UNION ALL
SELECT 'orders', COUNT(*) FROM orders
UNION ALL
SELECT 'order_items', COUNT(*) FROM order_items
UNION ALL
SELECT 'products', COUNT(*) FROM products
ORDER BY row_count DESC;
```

This helps you understand:
- Total data volume to migrate
- Whether you can do it in one batch or need staging
- Expected migration duration
- Resource requirements

## Phase 2: Data Extraction and Export

Once you've assessed the source data, extract it for transformation.

Decide which tables to migrate. Most migrations include:
- Core business entities (customers, products, orders)
- Historical data (transactions, events, audit logs)
- Configuration data (settings, parameters)

Some tables to consider leaving behind:
- Temporary or cache tables
- Session data (usually regenerated)
- Analytics/reporting tables (can be rebuilt)

Document your decisions for your migration team.

For each table, decide on export granularity:

**Option 1: Single Export**
- Export entire tables at once
- Best for small tables (< 100K rows)
- Simple but large files

**Option 2: Staged Exports**
- Export by date range (e.g., 2022, 2023, 2024)
- Export by category or status
- Best for large tables with natural partitions
- Allows parallel processing and easier recovery

**Option 3: Sampled Export**
- For testing: export 1% or 10% sample
- Migrate sample first to validate process
- Then do full migration with confidence

Most migrations use a combination—sample export for testing, then full staged exports for production.

Navigate to each table and use WhoDB's export functionality.

![Export Dialog](/images/20-data-view-export-dialog.png)

For the first pass, export all data:

```
1. Navigate to table
2. Click "Export All"
3. Choose format (CSV for transformation, SQL for direct import)
4. Download file
5. Verify file size and completeness
```

Name your exports consistently:
- `customers_2024-10-31_full.csv`
- `orders_2024-01-to-12.csv`
- `order_items_2024-full.sql`

This naming scheme prevents confusion during multi-table migrations.

Choose the format based on your needs:

![Export Format](/images/21-data-view-export-format-dropdown.png)

**CSV Format** - Best for transformation:
- Open in Excel or Python/R
- Clean and enrich data
- Handle special characters and encoding issues
- Then import to new database

**SQL Format** - Best for similar databases:
- If migrating between PostgreSQL versions or MySQL versions
- Direct INSERT statements
- Preserves data types precisely
- Faster import

**JSON Format** - Best for complex relationships:
- Preserves nested data structures
- Good for NoSQL migrations
- Easier to transform programmatically

For this guide, we'll assume CSV export for transformation flexibility.

After export, verify the data is complete:

1. **Check file size**: Is it reasonable for the row count?
2. **Check row count**: `wc -l` on CSV should match database row count + 1 (header)
3. **Check sampling**: Open file, spot-check first and last rows
4. **Check for truncation**: Look for incomplete rows at end

```
# On macOS/Linux, verify row count
wc -l customers_2024-10-31_full.csv
# Should be row_count + 1

# Check file size
ls -lh customers_2024-10-31_full.csv
# Should be reasonable for data size

# Spot check first few rows
head -n 5 customers_2024-10-31_full.csv
```

## Phase 3: Data Transformation and Cleaning

Before importing to the new system, clean and prepare your data.

Before any transformation, understand the data in your exports:

```
-- Count rows in the export
SELECT COUNT(*) FROM customers;

-- Check for NULL values by column
SELECT
  COUNT(CASE WHEN id IS NULL THEN 1 END) as null_ids,
  COUNT(CASE WHEN email IS NULL THEN 1 END) as null_emails,
  COUNT(CASE WHEN created_at IS NULL THEN 1 END) as null_created
FROM customers;

-- Find problematic data types
SELECT * FROM orders
WHERE amount::text ~ '[^0-9.-]';  -- Non-numeric characters in amount
```

Use Python, SQL, or your preferred tool to transform data:

**Example 1: Standardize email addresses**
```
import pandas as pd

# Read export
df = pd.read_csv('customers_2024-10-31_full.csv')

# Standardize emails
df['email'] = df['email'].str.lower().str.strip()

# Remove duplicates
df = df.drop_duplicates(subset=['email'], keep='first')

# Save cleaned version
df.to_csv('customers_cleaned.csv', index=False)
```

**Example 2: Handle date format differences**
```
-- If migrating from MySQL (datetime) to PostgreSQL (timestamp)
SELECT
  id,
  email,
  -- Convert date strings to timestamp
  TO_TIMESTAMP(created_at_string, 'YYYY-MM-DD HH24:MI:SS') as created_at,
  -- Handle NULL dates
  CASE WHEN updated_at_string IS NULL
    THEN NULL
    ELSE TO_TIMESTAMP(updated_at_string, 'YYYY-MM-DD HH24:MI:SS')
  END as updated_at
FROM customers_import
WHERE created_at_string IS NOT NULL;
```

**Example 3: Handle encoding issues**
```
# Some systems export with wrong encoding
import chardet

# Detect encoding
with open('customers_export.csv', 'rb') as f:
    result = chardet.detect(f.read(10000))
    encoding = result['encoding']

# Read with correct encoding
df = pd.read_csv('customers_export.csv', encoding=encoding)

# Fix encoding issues
df['name'] = df['name'].encode('utf-8').decode('utf-8', errors='ignore')

df.to_csv('customers_fixed.csv', index=False, encoding='utf-8')
```

**Example 4: Transform between database schemas**
```
-- Old schema: users table with everything
-- New schema: separate users and user_profiles tables

-- Create migration view
SELECT
  id as user_id,
  email,
  created_at,
  -- These stay in users table
FROM old_users

-- Create separate profile data
SELECT
  id as user_profile_id,
  user_id,
  first_name,
  last_name,
  avatar_url,
  -- These move to user_profiles table
FROM old_users;
```

Address specific data challenges:

**Handling Missing Foreign Keys**
```
-- Before migration, decide: keep or remove orphaned records?
-- Option 1: Remove orphaned records
DELETE FROM orders
WHERE customer_id NOT IN (SELECT id FROM customers);

-- Option 2: Set to NULL (if allowed)
UPDATE orders SET customer_id = NULL
WHERE customer_id NOT IN (SELECT id FROM customers);

-- Option 3: Create placeholder records
INSERT INTO customers (id, email, created_at)
SELECT DISTINCT customer_id, 'unknown@example.com', NOW()
FROM orders
WHERE customer_id NOT IN (SELECT id FROM customers);
```

**Handling Duplicates**
```
-- Identify which duplicates to keep
WITH ranked_duplicates AS (
  SELECT
    *,
    ROW_NUMBER() OVER (PARTITION BY email ORDER BY created_at ASC) as rn
  FROM customers
)
SELECT * FROM ranked_duplicates WHERE rn > 1;

-- Keep oldest, remove newer duplicates
DELETE FROM customers c1
WHERE EXISTS (
  SELECT 1 FROM customers c2
  WHERE c1.email = c2.email
  AND c2.created_at < c1.created_at
);
```

**Handling Encoding Issues**
```
-- For PostgreSQL: Fix encoding
UPDATE customers
SET name = CONVERT(name, 'USING utf8')
WHERE name LIKE '%\xEF%';

-- For MySQL: Different approach
UPDATE customers
SET name = CONVERT(CAST(CONVERT(name USING latin1) AS BINARY) USING utf8);
```

Organize cleaned data for import:

```
migration/
├── staging/
│   ├── customers_cleaned.csv
│   ├── products_cleaned.csv
│   ├── orders_cleaned.csv
│   └── order_items_cleaned.csv
├── validation/
│   ├── row_count_comparison.csv
│   ├── data_quality_report.csv
│   └── migration_issues.log
└── migration_notes.md
```

This organization makes imports systematic and traceable.

## Phase 4: Pre-Migration Validation

Before importing to production, validate everything is ready.

Verify you're not losing data:

```
-- Count all records in source
SELECT
  'customers' as table_name, COUNT(*) as source_count FROM old_customers
UNION ALL
SELECT 'orders', COUNT(*) FROM old_orders
UNION ALL
SELECT 'products', COUNT(*) FROM old_products
UNION ALL
SELECT 'order_items', COUNT(*) FROM old_order_items;

-- Compare with staged files (after cleaning)
-- customers_cleaned.csv: 5,432 rows
-- orders_cleaned.csv: 28,104 rows
-- products_cleaned.csv: 847 rows
-- order_items_cleaned.csv: 112,503 rows
```

Expected discrepancies:
- Some deletions are intentional (orphaned records)
- Some duplicates removed
- Document all differences in a reconciliation report

Ensure destination database can store all values:

```
-- Check numeric ranges
SELECT
  MIN(amount), MAX(amount) FROM orders;
-- If MAX(amount) = 999999999.99, ensure destination supports DECIMAL(12,2)

-- Check text lengths
SELECT
  MAX(LENGTH(description)) FROM products;
-- If MAX = 5000 chars, ensure destination supports TEXT not VARCHAR(255)

-- Check date ranges
SELECT
  MIN(created_at), MAX(created_at) FROM orders;
-- If dates go to 2099, ensure destination year is supported
```

Check data will satisfy destination constraints:

```
-- Verify unique constraints will be satisfied
SELECT email, COUNT(*) as cnt
FROM customers_staged
GROUP BY email
HAVING COUNT(*) > 1;

-- Verify foreign keys will be valid
SELECT DISTINCT customer_id FROM orders_staged
WHERE customer_id NOT IN (SELECT id FROM customers_staged);

-- Verify non-null constraints
SELECT COUNT(*) FROM customers_staged WHERE email IS NULL;
```

Use this in a comprehensive pre-import checklist.

Document everything for stakeholder review:

```
# Pre-Migration Validation Report
Date: 2024-10-31
Source DB: Legacy MySQL
Destination DB: PostgreSQL Production

## Row Counts
| Table | Source | After Cleaning | Difference | Status |
|-------|--------|-----------------|-----------|--------|
| customers | 5,500 | 5,432 | -68 (duplicates removed) | ✓ OK |
| orders | 28,200 | 28,104 | -96 (orphaned removed) | ✓ OK |
| products | 850 | 847 | -3 (inactive removed) | ✓ OK |
| order_items | 112,600 | 112,503 | -97 (orphaned removed) | ✓ OK |

## Data Quality
- Duplicate emails removed: 68
- Orphaned orders (no customer): 96
- Invalid dates: 0
- NULL in required fields: 0
- Character encoding issues: FIXED

## Constraint Violations
- Would-be unique constraint violations: 0
- Would-be foreign key violations: 0
- Would-be NOT NULL violations: 0

## Ready for Migration: YES
Approved by: [Data Lead]
Date: [Today]
```

This report provides confidence and accountability for the migration.

## Phase 5: Performing the Migration

With everything validated, perform the actual migration.

Ensure destination database is ready:

```
-- Destination is empty or in known state
-- All tables created with correct schema
-- All indexes defined
-- All constraints configured
-- Test connection works

-- Verify with simple query
SELECT * FROM customers LIMIT 1;
-- Should return: ERROR - no data (which is expected)
```

Start with a non-critical table to verify the process:

**Import customers table via WhoDB:**
1. In new database connection
2. Create customers table if not exists
3. Use WhoDB or SQL import to load customers_cleaned.csv

```
-- PostgreSQL example
COPY customers(id, email, name, created_at)
FROM '/path/to/customers_cleaned.csv'
WITH (FORMAT csv, HEADER true, NULL 'NULL');

-- MySQL example
LOAD DATA INFILE '/path/to/customers_cleaned.csv'
INTO TABLE customers
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS
(id, email, name, created_at);
```

**Verify import:**
```
SELECT COUNT(*) FROM customers;
-- Should equal 5,432
```

Before importing dependent tables, validate the first import:

```
-- Verify counts match
SELECT COUNT(*) as imported_count FROM customers;
-- Should be 5,432

-- Spot check data
SELECT * FROM customers LIMIT 5;
-- Verify data looks correct

-- Check for truncation
SELECT MAX(LENGTH(email)) FROM customers;
-- Verify no emails were truncated

-- Check dates converted correctly
SELECT MIN(created_at), MAX(created_at) FROM customers;
-- Should be reasonable dates
```

Import tables with foreign keys in dependency order:

```
1. customers (parent)
2. products (parent, no dependencies)
3. orders (depends on customers)
4. order_items (depends on orders and products)
5. reviews (depends on customers and products)
```

For each, repeat: Import → Verify → Check constraints → Continue

```
-- Import orders (depends on customers)
COPY orders(id, customer_id, order_date, total)
FROM '/path/to/orders_cleaned.csv'
WITH (FORMAT csv, HEADER true, NULL 'NULL');

-- Verify
SELECT COUNT(*) FROM orders;  -- 28,104
SELECT COUNT(DISTINCT customer_id) FROM orders;  -- 5,200+ (less than 5,432 customers)

-- Verify no orphaned orders
SELECT COUNT(*) FROM orders o
LEFT JOIN customers c ON o.customer_id = c.id
WHERE c.id IS NULL;  -- Should be 0
```

After large imports, rebuild indexes for performance:

```
-- PostgreSQL
REINDEX TABLE customers;
REINDEX TABLE orders;
REINDEX TABLE products;

-- MySQL
OPTIMIZE TABLE customers;
OPTIMIZE TABLE orders;
OPTIMIZE TABLE products;

-- Or rebuild specific indexes
ALTER TABLE orders ADD CONSTRAINT fk_orders_customers
FOREIGN KEY (customer_id) REFERENCES customers(id);
CREATE INDEX idx_orders_customer_id ON orders(customer_id);
```

## Phase 6: Post-Migration Verification

After import, comprehensively verify data integrity.

Use WhoDB's querying to compare source and destination:

![Query Results](/images/86-scratchpad-select-query-result.png)

Create side-by-side counts:

```
-- In destination database
SELECT
  'customers' as table_name, COUNT(*) as count FROM customers
UNION ALL
SELECT 'orders', COUNT(*) FROM orders
UNION ALL
SELECT 'products', COUNT(*) FROM products
UNION ALL
SELECT 'order_items', COUNT(*) FROM order_items;

-- Compare with pre-migration validation report
-- All counts should match exactly
```

Verify foreign key relationships:

```
-- No orphaned orders
SELECT COUNT(*) FROM orders o
LEFT JOIN customers c ON o.customer_id = c.id
WHERE c.id IS NULL;  -- Should be 0

-- No orphaned order_items
SELECT COUNT(*) FROM order_items oi
LEFT JOIN orders o ON oi.order_id = o.id
WHERE o.id IS NULL;  -- Should be 0

-- All references are valid
SELECT COUNT(*) FROM order_items oi
LEFT JOIN products p ON oi.product_id = p.id
WHERE p.id IS NULL;  -- Should be 0
```

Manually verify sample data looks correct:

![Data View](/images/09-data-view-users-table.png)

In WhoDB:
1. Open each table
2. View first 10 rows
3. Verify data looks reasonable
4. Check for encoding issues or truncation
5. Verify dates converted correctly

```
-- Check for suspicious values
SELECT * FROM customers WHERE email LIKE '%@%' = false;
SELECT * FROM orders WHERE total < 0;
SELECT * FROM products WHERE price IS NULL;
```

Verify all constraints are satisfied:

```
-- Check unique constraints
SELECT email, COUNT(*) FROM customers GROUP BY email HAVING COUNT(*) > 1;
-- Should return 0 rows

-- Check not-null constraints
SELECT COUNT(*) FROM customers WHERE email IS NULL;
-- Should return 0

-- Check check constraints
SELECT COUNT(*) FROM orders WHERE total < 0;
-- Should return 0 (if you have a CHECK constraint)
```

Run the same queries on both databases and compare results:

```
-- Query 1: Total revenue by customer
SELECT
  customer_id,
  SUM(total) as total_spent
FROM orders
GROUP BY customer_id
ORDER BY total_spent DESC
LIMIT 10;

-- Run this on BOTH old and new database
-- Results should be IDENTICAL

-- Query 2: Product performance
SELECT
  product_id,
  COUNT(*) as order_count,
  SUM(quantity) as total_sold
FROM order_items
GROUP BY product_id
ORDER BY order_count DESC;

-- Run on both databases - compare results
```

Any differences indicate migration issues to investigate.

Verify new database performs acceptably:

![Scratchpad Results](/images/87-scratchpad-count-query-result.png)

```
-- Test common application queries
-- Query 1: Get recent orders
SELECT * FROM orders
WHERE created_at > NOW() - INTERVAL '7 days'
ORDER BY created_at DESC;

-- Query 2: Join query
SELECT
  o.id,
  c.email,
  SUM(oi.quantity * oi.price) as total
FROM orders o
JOIN customers c ON o.customer_id = c.id
LEFT JOIN order_items oi ON o.id = oi.order_id
GROUP BY o.id, c.email;

-- Check execution time - should be reasonable
-- Compare with old database - new should be similar or faster
```

Document successful migration:

```
# Post-Migration Verification Report
Date Completed: 2024-10-31
Source DB: Legacy MySQL (shutdown)
Destination DB: PostgreSQL Production

## Row Count Verification
✓ customers: 5,432 rows (matches pre-migration count)
✓ orders: 28,104 rows (matches)
✓ products: 847 rows (matches)
✓ order_items: 112,503 rows (matches)

## Relationship Integrity
✓ No orphaned orders: 0 records without valid customer_id
✓ No orphaned order items: 0 records without valid order_id
✓ All product references: 0 invalid product_ids

## Data Quality
✓ Unique constraints: No duplicates found
✓ NOT NULL constraints: No NULL values in required fields
✓ Check constraints: All values within expected ranges

## Query Comparison Results
✓ 10 common queries run on both databases
✓ All results identical to legacy database
✓ Performance acceptable

## Application Testing
✓ Application successfully connects to new database
✓ Read operations working correctly
✓ Write operations working correctly
✓ No encoding issues observed

## Sign-Off
Migration is COMPLETE and VERIFIED.
Data integrity confirmed.
Ready for production use.

Approved by: [Database Admin]
Date: 2024-10-31
```

This serves as your official record of successful migration.

## Complete Migration Checklist

Use this checklist for your migration project:

- [ ] Connect to source database in WhoDB
- [ ] Explore and document schema
- [ ] Identify data quality issues
- [ ] Count rows and estimate volume
- [ ] Create transformation scripts
- [ ] Test transformation on sample data
- [ ] Design staging area
- [ ] Get stakeholder sign-off on plan

- [ ] Verify destination database ready
- [ ] Final row count comparison
- [ ] Final data quality validation
- [ ] All transformation scripts tested
- [ ] Backups created
- [ ] Rollback plan documented
- [ ] Test and production environments ready
- [ ] Notify stakeholders

- [ ] Stop applications accessing source database
- [ ] Final export of any changed data
- [ ] Import customers (test run)
- [ ] Verify customer import
- [ ] Import products
- [ ] Import orders
- [ ] Import order_items
- [ ] Verify all imports
- [ ] Rebuild indexes
- [ ] Run final validation queries

- [ ] Monitor application performance
- [ ] Monitor error rates
- [ ] Check for data-related errors
- [ ] Archive old database (don't delete yet)
- [ ] Update documentation
- [ ] Update connection strings if needed
- [ ] Final stakeholder sign-off
- [ ] Schedule cleanup (delete old DB if all OK)

## Common Migration Challenges

**Problem**: Data exports with garbled characters or symbols.

**Solution**:
- Always export as UTF-8
- Detect source encoding before transformation
- Test character conversion in staging before production
- WhoDB supports multiple encodings - verify in export settings

**Problem**: Import fails because of orphaned records.

**Solution**:
1. Query source database to find orphaned records
2. Decide: delete or reassign to valid parent
3. Clean source data before export
4. Import parents first, then dependents
5. Disable constraints during import if necessary (re-enable after)

**Problem**: Date strings won't convert, numbers overflow, text truncates.

**Solution**:
- Verify destination database column types match data
- Use explicit type conversion in transformation
- Test with sample data first
- Use CAST() or TO_DATE() functions appropriately

**Problem**: New database queries run slower than old database.

**Solution**:
- Compare row counts (more data = slower queries)
- Verify indexes are created
- Run ANALYZE or OPTIMIZE after import
- Compare query execution plans between old and new
- Check if queries were rewritten during conversion

**Problem**: Cannot export/import entire database at once.

**Solution**:
- Use staged exports by date range
- Migrate one month at a time
- Use parallel imports if possible
- Archive old data and migrate separately
- Consider whether all historical data is needed

**Problem**: Cannot afford long application downtime during migration.

**Solution**:
- Migrate most data while systems running
- Quick final sync of recent changes only
- Use application connection redirection
- Test failover before migration day
- Have rollback plan ready
- Target: < 5 minute downtime for switchover

## Best Practices for Safe Migrations

Never test your migration process for the first time on production. Run complete migration to a staging environment first. This catches issues and builds confidence.

Don't shut down the source database immediately after migration. Keep it for 1-2 weeks for validation and potential rollback. Archive backups for much longer (6+ months).

Every step of the migration should be documented with dates, counts, query results, and decisions. This is invaluable if issues arise later.

Know exactly how to roll back to the old system if something goes wrong. Practice this before migration day.

The verification phase is not optional. Run multiple independent validations confirming data integrity. The extra hour spent validating saves 10 hours of troubleshooting later.

For migration testing and validation, use read-only database connections when possible. This prevents accidental data modification during the critical verification phase.

Plan migrations to minimize application downtime. Export non-dependent data while systems run. Only pause applications for the final sync of changed data.

After the migration, monitor application logs and performance metrics closely for 24-48 hours. Watch for data-related errors, query performance issues, or unexpected behaviors.

## Next Steps

Ready to execute your migration?

Use analysis techniques to validate migrated data

Master SQL queries for validation

Deep dive into export configuration

Test your migration with mock data first

You now have a complete framework for database migrations. From pre-migration assessment through post-migration verification, WhoDB provides the tools to execute migrations safely and validate data integrity. By following these phases—planning, extraction, transformation, validation, import, and verification—you can migrate databases with confidence, knowing your data is intact and your applications will continue operating seamlessly in their new home.

---


# use-cases/debugging-production.mdx
## Debugging Production Issues

Use WhoDB to safely debug production database issues. Read-only access, query patterns for troubleshooting, and methods to find root causes without risking data.

# Debugging Production Issues

Production database issues are stressful and urgent. When users report problems, data looks corrupt, or queries are slow, you need to diagnose the root cause quickly. WhoDB provides a safe, read-only way to investigate production databases without risking data integrity or performance.

This guide covers safe debugging techniques, SQL patterns for investigation, and systematic approaches to finding and understanding production issues.

## The Challenge of Production Debugging

Production databases contain real, critical data. This reality shapes how you investigate issues:

**Constraints**:
- Read-only access only (you shouldn't modify production data)
- Performance matters (heavy queries affect users)
- Interruptions cost money (downtime is unacceptable)
- High stakes (wrong diagnosis leads to wrong solutions)

**Objectives**:
- Understand the root cause quickly
- Minimize impact on production traffic
- Provide evidence-based diagnosis
- Enable targeted fixes

WhoDB's read-only interface, efficient querying, and data visualization make it ideal for this challenging task.

## Before You Start: Connection Setup

### Use a Read-Only Connection

Never debug production with write access. Set up a read-only database user:

```
-- PostgreSQL: Create read-only role
CREATE ROLE readonly_debugger WITH LOGIN PASSWORD 'secure_password';
GRANT CONNECT ON DATABASE production TO readonly_debugger;
GRANT USAGE ON SCHEMA public TO readonly_debugger;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_debugger;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly_debugger;

-- MySQL: Create read-only user
CREATE USER 'readonly_debugger'@'%' IDENTIFIED BY 'secure_password';
GRANT SELECT ON production.* TO 'readonly_debugger'@'%';
FLUSH PRIVILEGES;
```

Connect to production in WhoDB using this read-only account:

![Database Types](/images/01-login-page.png)

This prevents accidental modifications while you're investigating.

### Understand Performance Impact

Even SELECT queries impact production if they're heavy. Follow these guidelines:

- **Add LIMIT**: Always limit result sets (LIMIT 1000 not LIMIT 1000000)
- **Use WHERE clauses**: Filter aggressively to reduce rows scanned
- **Check indexes**: Use indexed columns in WHERE conditions
- **Test first**: Run queries in non-peak hours when possible
- **Monitor queries**: Watch for slow-running investigations

```
-- GOOD: Targets specific data with LIMIT
SELECT * FROM user_events
WHERE user_id = 12345
AND created_at > NOW() - INTERVAL '1 hour'
LIMIT 1000;

-- BAD: Could scan entire table and return millions
SELECT * FROM user_events;
```

## Common Production Issues and Debugging Approaches

### Issue 1: Users Reporting Missing or Corrupt Data

When users report data problems, start with systematic investigation.

Determine how widespread the problem is:

```
-- Is it one user or many?
SELECT
  user_id,
  COUNT(*) as event_count,
  MAX(created_at) as last_event
FROM user_events
WHERE status = 'lost'  -- or whatever indicates the problem
GROUP BY user_id
ORDER BY event_count DESC
LIMIT 20;

-- Is it recent or historical?
SELECT
  DATE(created_at) as date,
  COUNT(*) as affected_count
FROM user_events
WHERE status = 'lost'
GROUP BY DATE(created_at)
ORDER BY date DESC
LIMIT 30;

-- What percentage of data is affected?
SELECT
  COUNT(*) as total_records,
  COUNT(CASE WHEN status = 'lost' THEN 1 END) as affected,
  ROUND(100.0 * COUNT(CASE WHEN status = 'lost' THEN 1 END) / COUNT(*), 2) as percent_affected
FROM user_events;
```

A small percentage (1-5%) might be a data entry error. A large percentage (>50%) suggests a systematic bug.

Look for changes around the problem time:

```
-- Recent data patterns
SELECT
  DATE_TRUNC('hour', created_at) as hour,
  COUNT(*) as record_count,
  COUNT(CASE WHEN status = 'lost' THEN 1 END) as affected_count,
  ROUND(100.0 * COUNT(CASE WHEN status = 'lost' THEN 1 END) / COUNT(*), 2) as percent_affected
FROM user_events
WHERE created_at > NOW() - INTERVAL '72 hours'
GROUP BY DATE_TRUNC('hour', created_at)
ORDER BY hour DESC;
```

Look for a sharp change in the percentage affected. The hour where it jumps is likely when the problem started.

Understand what specifically is wrong:

```
-- Sample affected records
SELECT * FROM user_events
WHERE status = 'lost'
LIMIT 10;

-- Compare with normal records
SELECT * FROM user_events
WHERE status != 'lost'
LIMIT 10;
```

Use WhoDB's data view to spot the differences:

![Data View](/images/09-data-view-users-table.png)

Look for patterns:
- Missing fields (NULLs where there should be values)
- Wrong values (zeros instead of actual amounts)
- Encoding issues (garbled characters)
- Incomplete data (truncated text)

Correlate the data problem with application behavior:

```
-- Did something change in related tables?
SELECT
  table_name,
  MAX(updated_at) as last_modified
FROM information_schema.tables
WHERE table_schema = 'public'
ORDER BY last_modified DESC
LIMIT 10;

-- Do affected records share characteristics?
SELECT
  CASE
    WHEN price > 1000 THEN 'high_price'
    WHEN price > 100 THEN 'medium_price'
    ELSE 'low_price'
  END as price_category,
  COUNT(*) as affected_count
FROM user_events
WHERE status = 'lost'
GROUP BY price_category;

-- Check for NULL values or default values
SELECT
  *,
  CASE
    WHEN amount IS NULL THEN 'missing_amount'
    WHEN amount = 0 THEN 'zero_amount'
    WHEN created_at = updated_at THEN 'never_updated'
    ELSE 'normal'
  END as status
FROM orders
WHERE id IN (SELECT order_id FROM user_events WHERE status = 'lost')
LIMIT 20;
```

The pattern often points to the cause (e.g., a bug in a recent code deployment, a data import error, or a configuration change).

Document your findings:

```
# Production Issue Investigation Report
Issue: User reported missing order data
Date Discovered: 2024-10-31 14:30 UTC

## Scope
- Affected Users: 127 (0.8% of active user base)
- Affected Records: 3,247 orders
- Time Period: 2024-10-31 08:00 - 12:00 UTC
- Severity: Medium (small user base affected)

## Pattern
- All affected orders have `total_amount = 0`
- All affected orders created between 08:00-12:00
- Unaffected orders have correct amounts
- Problem started 2024-10-31 08:00 UTC

## Findings
- Correlation: Application deployed 2024-10-31 07:45 UTC (15 minutes before problem)
- Hypothesis: New deployment introduced bug calculating order totals

## Recommendation
- Revert to previous deployment
- Verify order totals in test environment
- Manually correct affected orders (or regenerate from logs)
- Deploy fix after validation

## Evidence
- Query results: [attached]
- Sample affected records: [attached]
- Timeline correlation: [attached]
```

This report provides actionable evidence to your team.

### Issue 2: Slow Queries or Performance Degradation

When performance suddenly degrades, systematic investigation reveals the cause.

Run the Scratchpad to test your key queries:

![Scratchpad Code Editor](/images/27-scratchpad-main-view.png)

```
-- Test a common query
SELECT
  u.id,
  u.email,
  COUNT(o.id) as order_count,
  SUM(o.total) as lifetime_value
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.created_at > '2024-01-01'
GROUP BY u.id
LIMIT 1000;
```

Check execution time. If it's notably slower than usual, continue investigating.

![Query Results](/images/29-scratchpad-query-results.png)

Missing or broken indexes cause performance problems:

```
-- PostgreSQL: Check index status
SELECT
  schemaname,
  tablename,
  indexname,
  idx_scan,
  idx_tup_read,
  idx_tup_fetch
FROM pg_stat_user_indexes
ORDER BY idx_scan DESC
LIMIT 20;

-- MySQL: Check table statistics
SHOW INDEX FROM users;

-- Verify a specific index is being used
EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';
-- Should show "Using index" in the output
```

Look for:
- Indexes with zero or very low `idx_scan` count (might be unused)
- Queries that should use indexes but don't

Query results:

```
-- Count rows by table
SELECT
  'users' as table_name, COUNT(*) as count FROM users
UNION ALL
SELECT 'orders', COUNT(*) FROM orders
UNION ALL
SELECT 'order_items', COUNT(*) FROM order_items;

-- Check growth rate
SELECT
  DATE(created_at) as date,
  COUNT(*) as records_created
FROM orders
WHERE created_at > NOW() - INTERVAL '30 days'
GROUP BY DATE(created_at)
ORDER BY date DESC;

-- Are some tables growing unusually fast?
SELECT
  'orders_yesterday' as period,
  COUNT(*) as count
FROM orders
WHERE created_at > NOW() - INTERVAL '1 day'
AND created_at < NOW() - INTERVAL '2 days'

UNION ALL

SELECT 'orders_today',
  COUNT(*)
FROM orders
WHERE created_at > NOW() - INTERVAL '1 day';
```

Rapid growth can cause query slowdowns if indexes aren't updated.

Lock contention causes slow queries:

```
-- PostgreSQL: Check for locks
SELECT
  pid,
  usename,
  application_name,
  state,
  query
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY query_start ASC;

-- MySQL: Check processlist
SHOW PROCESSLIST;

-- Look for long-running transactions
SELECT
  pid,
  now() - pg_stat_activity.query_start AS duration,
  query,
  state
FROM pg_stat_activity
WHERE (now() - pg_stat_activity.query_start) > interval '5 minutes';
```

Long-running queries or locks indicate contention. Document PIDs and queries.

Outdated statistics cause poor query plans:

```
-- PostgreSQL: Check stats freshness
SELECT
  schemaname,
  tablename,
  last_vacuum,
  last_autovacuum,
  last_analyze,
  last_autoanalyze
FROM pg_stat_user_tables
WHERE schemaname = 'public'
ORDER BY last_analyze DESC;

-- MySQL: Check table statistics
SELECT
  TABLE_SCHEMA,
  TABLE_NAME,
  UPDATE_TIME
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'your_database'
ORDER BY UPDATE_TIME DESC;
```

If stats are days old, they may be causing poor query plans.

Use EXPLAIN to see how queries are executing:

```
-- PostgreSQL: Detailed execution plan
EXPLAIN ANALYZE
SELECT
  o.id,
  c.email,
  COUNT(oi.id) as item_count
FROM orders o
JOIN customers c ON o.customer_id = c.id
LEFT JOIN order_items oi ON o.id = oi.order_id
WHERE o.created_at > NOW() - INTERVAL '7 days'
GROUP BY o.id, c.email;

-- MySQL: Execution plan
EXPLAIN
SELECT * FROM orders WHERE customer_id = 123;
```

Look for:
- Sequential scans that should use indexes
- Nested loop joins when hash joins would be better
- High "Actual rows" vs estimated rows (indicates bad statistics)

Record your investigation:

```
# Performance Issue Report
Issue: User reports slow order list loading
Occurred: 2024-10-31 between 14:00-15:00 UTC

## Diagnosis
**Slow Query**: Get user's orders (normally 200ms, now 8000ms)

**Root Cause**: Missing index on orders.customer_id
- Query doing full table scan of 5.2M orders
- Index should exist but was dropped during recent migration

## Evidence
- Query: [query from Scratchpad]
- Execution time: 8,234ms (vs normal 200ms)
- Rows scanned: 5,200,000 (vs expected 50)
- Execution plan: [EXPLAIN output]

## Solution
Create index: `CREATE INDEX idx_orders_customer_id ON orders(customer_id);`
Expected improvement: 40x faster

## Recommendation
- Create the index immediately
- Monitor query performance after
- Add index creation to post-deployment checklist
```

### Issue 3: Data Inconsistency or Referential Integrity Problems

When relationships between tables are broken, queries fail or show wrong results.

Find records with missing parent references:

![Where Conditions](/images/16-data-view-where-conditions-popover.png)

```
-- Find orders with no matching customer
SELECT o.id, o.customer_id, o.created_at
FROM orders o
LEFT JOIN customers c ON o.customer_id = c.id
WHERE c.id IS NULL
LIMIT 100;

-- Find order_items with no matching order
SELECT oi.* FROM order_items oi
LEFT JOIN orders o ON oi.order_id = o.id
WHERE o.id IS NULL
LIMIT 100;

-- Find payments with no matching order
SELECT p.* FROM payments p
LEFT JOIN orders o ON p.order_id = o.id
WHERE o.id IS NULL
LIMIT 100;
```

Orphaned records indicate:
- Data deletion that didn't cascade properly
- Failed foreign key constraint enforcement
- Data corruption or import errors

Duplicates break unique constraints:

```
-- Find duplicate emails (should be unique)
SELECT email, COUNT(*) as cnt
FROM customers
GROUP BY email
HAVING COUNT(*) > 1
ORDER BY cnt DESC;

-- Find duplicate order numbers
SELECT order_number, COUNT(*) as cnt
FROM orders
GROUP BY order_number
HAVING COUNT(*) > 1;

-- Find likely duplicates (same email, different ID)
SELECT
  email,
  GROUP_CONCAT(id) as duplicate_ids,
  COUNT(*) as cnt
FROM customers
WHERE email IS NOT NULL
GROUP BY email
HAVING COUNT(*) > 1
ORDER BY cnt DESC;
```

Duplicates indicate:
- Race conditions during record creation
- Data import errors
- Failed constraint enforcement

Some rows violate defined constraints:

```
-- Check for NULLs in NOT NULL fields
SELECT COUNT(*) as null_count FROM users WHERE email IS NULL;
SELECT COUNT(*) as null_count FROM orders WHERE customer_id IS NULL;

-- Check for invalid statuses
SELECT DISTINCT status FROM orders;
-- Then check if these are valid enum values

-- Check for negative amounts (if not allowed)
SELECT * FROM orders WHERE total_amount < 0 LIMIT 10;

-- Check for future dates (if not allowed)
SELECT * FROM orders WHERE created_at > NOW() LIMIT 10;
```

Correlate problems with application changes:

```
-- When did the problem start?
SELECT
  DATE(created_at) as date,
  COUNT(CASE WHEN customer_id IS NULL THEN 1 END) as null_customer_count,
  COUNT(*) as total_orders
FROM orders
WHERE created_at > NOW() - INTERVAL '30 days'
GROUP BY DATE(created_at)
ORDER BY date DESC;

-- Is it in old or new data?
SELECT
  COUNT(CASE WHEN created_at < '2024-10-01' AND customer_id IS NULL THEN 1 END) as old_data_issues,
  COUNT(CASE WHEN created_at >= '2024-10-01' AND customer_id IS NULL THEN 1 END) as new_data_issues;
```

Sudden increases correlate with code deployments or data changes.

Document data integrity issues:

```
# Data Integrity Issue Report
Issue: Orphaned order records found
Discovered: 2024-10-31

## Problem Summary
- 347 orders have customer_id values that don't exist
- These orders cannot be viewed in application
- Affects approximately 0.02% of all orders

## When Did It Occur?
- Problem exists in records from 2024-10-15 onward
- Likely caused by data import on 2024-10-15 14:30

## Impact Assessment
- 347 affected orders (low impact)
- ~$2,300 in affected order value
- Users cannot see these orders in their order history

## Remediation Options
1. **Delete**: Remove the 347 orphaned orders
2. **Reassign**: Assign to a customer based on context
3. **Create placeholders**: Create customer records for these orders

## Recommendation
Reassign orders to customer based on last activity context, or delete if > 30 days old
```

## Debugging SQL: Common Patterns

### Finding Recently Changed Data

```
-- Records modified today
SELECT * FROM orders
WHERE DATE(updated_at) = TODAY()
ORDER BY updated_at DESC
LIMIT 100;

-- Bulk operations (many updates at once)
SELECT
  updated_at,
  COUNT(*) as update_count
FROM orders
WHERE updated_at > NOW() - INTERVAL '24 hours'
GROUP BY updated_at
ORDER BY update_count DESC
LIMIT 20;

-- Records with unexpected changes
SELECT
  *,
  CASE
    WHEN status = 'cancelled' AND updated_at > NOW() - INTERVAL '1 hour' THEN 'recently_cancelled'
    WHEN total_amount != original_total_amount THEN 'amount_changed'
    WHEN customer_id IS NULL AND created_at < NOW() - INTERVAL '7 days' THEN 'old_orphan'
    ELSE 'normal'
  END as concern
FROM orders
WHERE updated_at > NOW() - INTERVAL '24 hours'
LIMIT 50;
```

### Finding N+1 Query Patterns

Sometimes applications execute N queries when they should do 1:

```
-- Look for repeated queries in logs
-- Example: If you see 10,000 queries like:
-- SELECT * FROM inventory WHERE product_id = 1
-- SELECT * FROM inventory WHERE product_id = 2
-- ... repeated 9,998 times
-- This is an N+1 problem

-- Better approach: One query that joins everything
SELECT
  p.id,
  p.name,
  i.quantity
FROM products p
LEFT JOIN inventory i ON p.id = i.product_id;
```

### Identifying Expensive Operations

```
-- Find queries with expensive DISTINCT
SELECT DISTINCT user_id FROM user_events;
-- If this scans millions of rows, it's expensive

-- Find queries doing table-wide aggregations
SELECT COUNT(*) FROM user_events;  -- Scans entire table
SELECT SUM(amount) FROM orders;    -- Scans entire table

-- Better approach: Keep running totals
SELECT COUNT(*) FROM user_events_totals WHERE date = TODAY();
```

## Safe Query Patterns for Production Debugging

Always use these patterns when debugging production:

```
-- Always limit result sets
SELECT * FROM large_table LIMIT 100;

-- For analysis, use OFFSET carefully
SELECT * FROM orders
WHERE created_at > NOW() - INTERVAL '7 days'
LIMIT 1000;  -- Don't do LIMIT 1000000
```

```
-- Always run counts/aggregates before drilling into details
SELECT COUNT(*) as total_records FROM user_events;
SELECT COUNT(*) as affected_records FROM user_events WHERE status = 'error';

-- Only then drill into specific rows
SELECT * FROM user_events WHERE status = 'error' LIMIT 10;
```

```
-- Bad: Scans entire table
SELECT * FROM orders;

-- Good: Filters to relevant records
SELECT * FROM orders
WHERE created_at > NOW() - INTERVAL '24 hours'
LIMIT 100;

-- Best: Even more specific
SELECT * FROM orders
WHERE customer_id = 123
AND created_at > NOW() - INTERVAL '7 days'
ORDER BY created_at DESC
LIMIT 100;
```

```
-- Queries that use indexes run fast
SELECT * FROM orders WHERE customer_id = 123;
SELECT * FROM users WHERE email = 'test@example.com';

-- Add ORDER BY with indexed columns
SELECT * FROM orders
WHERE customer_id = 123
ORDER BY created_at DESC;

-- Avoid functions on indexed columns (prevents index use)
SELECT * FROM orders
WHERE MONTH(created_at) = 10;  -- Can't use index on created_at

-- Better: Use ranges
SELECT * FROM orders
WHERE created_at >= '2024-10-01'
AND created_at < '2024-11-01';  -- Can use index
```

```
-- Always check execution plan before running slow query
EXPLAIN
SELECT * FROM orders WHERE customer_id = 123;

-- Look for:
-- - Sequential Scan (bad - uses index)
-- - Index Scan (good - uses index)
-- - Nested Loop Join (ok for small tables, bad for large)
-- - Hash Join (good for large joins)
```

## Debugging Workflow Checklist

Use this systematic approach when debugging production:

- [ ] Get exact description of the problem from user
- [ ] Determine which users/data are affected
- [ ] Note when problem started
- [ ] Determine impact (how many users, lost revenue, etc.)

- [ ] Check row counts and data volume
- [ ] Determine if problem is widespread or localized
- [ ] Look for time-based patterns
- [ ] Check if similar problems exist elsewhere

- [ ] Query for affected records
- [ ] Compare with unaffected records
- [ ] Look for patterns in the differences
- [ ] Correlate with application deployments
- [ ] Check query performance
- [ ] Look for data integrity issues

- [ ] Document all queries run
- [ ] Save query results
- [ ] Take screenshots of WhoDB views
- [ ] Note timings and scope
- [ ] List possible causes and evidence

- [ ] Propose solution with confidence level
- [ ] Explain expected outcome
- [ ] Note any risks or side effects
- [ ] Suggest monitoring after fix

## Monitoring After a Fix

After identifying and fixing a production issue, verify the fix worked:

```
-- Re-run your investigation query
SELECT
  COUNT(*) as total_records,
  COUNT(CASE WHEN status = 'lost' THEN 1 END) as affected_count
FROM user_events
WHERE created_at > NOW() - INTERVAL '1 hour';

-- The affected_count should be zero or much lower

-- Monitor for regression
SELECT
  DATE_TRUNC('hour', created_at) as hour,
  COUNT(*) as record_count,
  COUNT(CASE WHEN status = 'lost' THEN 1 END) as affected_count
FROM user_events
WHERE created_at > NOW() - INTERVAL '24 hours'
GROUP BY DATE_TRUNC('hour', created_at)
ORDER BY hour DESC;
```

Save these queries for future reference. When similar issues occur, you can quickly run the same diagnostics.

## Error Queries: Common Debugging Examples

These are ready-to-use queries for common debugging scenarios:

```
SELECT
  id,
  user_id,
  amount,
  error_message,
  created_at,
  updated_at
FROM transactions
WHERE status = 'failed'
AND created_at > NOW() - INTERVAL '1 hour'
ORDER BY created_at DESC
LIMIT 50;
```

Use this to investigate payment failures or transaction errors.

```
SELECT o.id, o.customer_id, c.id
FROM orders o
LEFT JOIN customers c ON o.customer_id = c.id
WHERE o.customer_id IS NOT NULL
AND c.id IS NULL
LIMIT 100;
```

Use this to find orphaned records and foreign key issues.

```
SELECT
  user_id,
  DATE(created_at) as date,
  COUNT(*) as event_count,
  MAX(created_at) as last_event
FROM user_activity_log
WHERE created_at > NOW() - INTERVAL '7 days'
GROUP BY user_id, DATE(created_at)
HAVING COUNT(*) > 1000  -- Unusually high activity
ORDER BY event_count DESC
LIMIT 20;
```

Use this to find suspicious user behavior or data pipeline issues.

```
SELECT
  query,
  calls,
  ROUND(total_time::numeric, 2) as total_ms,
  ROUND(mean_time::numeric, 2) as avg_ms,
  ROUND(max_time::numeric, 2) as max_ms
FROM pg_stat_statements
WHERE query NOT LIKE '%pg_stat_statements%'
ORDER BY mean_time DESC
LIMIT 20;
```

Use this to find slow or frequently-executed queries.

```
SELECT
  table_name,
  MAX(updated_at) as last_update,
  NOW() - MAX(updated_at) as time_since_update,
  COUNT(*) as row_count
FROM (
  SELECT table_name, MAX(created_at) as updated_at FROM users UNION ALL
  SELECT 'orders', MAX(updated_at) FROM orders UNION ALL
  SELECT 'payments', MAX(updated_at) FROM payments
) AS tables
GROUP BY table_name
ORDER BY last_update DESC;
```

Use this to detect stale data that indicates pipeline failures.

## Next Steps

Master debugging with these related guides:

Learn advanced SQL techniques for investigation

Apply analytical patterns to diagnosis

Master filtering for targeted queries

Understand your database structure for debugging

Systematic, evidence-based debugging transforms you from guessing to knowing. With WhoDB's read-only access, efficient queries, and data visualization, you can safely investigate production issues without risking your data. By following these patterns and maintaining a library of debugging queries, you'll become the person who solves production problems with confidence, armed with data and clear analysis.

---


# use-cases/ai-data-exploration.mdx
## AI-Powered Data Exploration

Discover how to use WhoDB's AI Chat Assistant to rapidly explore and understand unfamiliar databases without writing SQL

# AI-Powered Data Exploration

Exploring an unfamiliar database typically involves hours of reading documentation, writing test queries, and piecing together relationships. WhoDB's AI Chat Assistant transforms this time-consuming process into a natural conversation, allowing you to understand database structure, discover data patterns, and map relationships in minutes instead of hours.

This guide demonstrates a complete exploration workflow using AI to understand a new database from scratch

## The Challenge: Understanding Unfamiliar Databases

Database exploration presents significant challenges for everyone on your team:

### Common Scenarios

You join a project with a mature database containing 50+ tables. The documentation is outdated, and the senior developers are too busy to provide detailed walkthroughs. You need to understand:

- What entities exist and how they relate
- Where specific data lives
- Which tables are actively used vs. legacy
- What queries are safe to run

**Traditional Approach**: Spend days reading schema definitions, examining foreign keys, and writing exploratory queries
**Time Required**: 2-5 days to gain basic understanding

Your business stakeholders need insights from a production database you've never seen. You need to quickly:

- Identify which tables contain relevant data
- Understand data quality and completeness
- Map relationships between customer, order, and product data
- Generate sample queries for reporting

**Traditional Approach**: Request schema documentation, create ERD diagrams, write dozens of test queries
**Time Required**: 1-3 days before you can produce meaningful analysis

Your company acquired another business, and you need to assess their database. Critical questions include:

- How is data organized?
- Are there data quality issues?
- What are the relationships and dependencies?
- Where are the potential performance bottlenecks?

**Traditional Approach**: Generate schema reports, manually trace foreign keys, sample data from each table
**Time Required**: 2-4 days for comprehensive audit

You're working with a new client who needs database optimization. You have limited time to:

- Understand their data model
- Identify inefficient patterns
- Locate performance-critical tables
- Propose improvements

**Traditional Approach**: Schedule meetings with client DBA, review documentation, manually explore schema
**Time Required**: 1-2 days before you can provide recommendations

### Why Traditional Exploration is Slow

**Manual Query Writing**: Each question requires crafting SQL queries from scratch
```
-- Just to see what's in a table requires remembering syntax
SELECT * FROM users LIMIT 10;
SELECT COUNT(*) FROM users;
SELECT DISTINCT status FROM users;
```

**Schema Navigation**: Finding relationships means manually tracing foreign keys through information schema tables

**Trial and Error**: Understanding data patterns requires multiple query iterations

**Context Switching**: Constantly switching between documentation, schema viewers, and query tools

## How AI Chat Assistant Accelerates Exploration

WhoDB's AI Chat Assistant eliminates these barriers by letting you explore databases through natural conversation:

Ask questions in plain English without SQL knowledge

AI understands your tables, columns, and relationships automatically

See data immediately without writing queries

Ask about connections and get comprehensive answers

![Chat Interface](/images/101-chat-initial-page.png)

### The AI Advantage

**Speed**: Get answers in seconds instead of minutes
**Accuracy**: AI generates syntactically correct SQL every time
**Learning**: See generated SQL to understand how queries work
**Context**: AI remembers your conversation for follow-up questions

## Complete AI Exploration Workflow

Let's walk through exploring a completely unfamiliar e-commerce database using only the AI Chat Assistant.

### Phase 1: Discovery - What Exists?

Start by understanding what's in the database at a high level.

Navigate to the Chat interface in WhoDB's sidebar.

![Chat Initial Page](/images/101-chat-initial-page.png)

The chat interface is ready for your questions.

Start with the most fundamental question:

``` Natural Language Query
What tables are available in this database?
```

**AI Response**:
```
Your database contains the following tables:
- users
- products
- categories
- orders
- order_items
- payments
- reviews
- shipping_addresses
- inventory
- wishlists
- promotions
```

![Simple Text Response](/images/105-chat-simple-text-response.png)

**What You Learn**: The database has 11 tables representing a typical e-commerce system with users, products, orders, and supporting features.

Ask about specific tables to understand their role:

``` Follow-Up Questions
What does the orders table contain?
Describe the structure of the products table
What's in the order_items table?
```

**AI Response Examples**:
```
The orders table contains customer purchase records including:
- Order ID (primary key)
- User ID (foreign key to users)
- Order date and status
- Total amount
- Shipping and billing information
```

**What You Learn**: Table purposes and high-level contents without viewing actual data yet.

Understand the scale of data:

``` Natural Language Query
How many rows are in each table?
```

**AI Response** (generates and executes query):
```
users: 15,234 rows
products: 8,567 rows
categories: 42 rows
orders: 45,123 rows
order_items: 128,456 rows
payments: 45,089 rows
reviews: 12,345 rows
shipping_addresses: 18,901 rows
inventory: 8,567 rows
wishlists: 3,456 rows
promotions: 156 rows
```

![SQL Query Results](/images/106-chat-sql-query-results.png)

**What You Learn**: The database contains moderate data volume. `order_items` is the largest table, which makes sense (each order has multiple items). `categories` and `promotions` are small reference tables.

In just 4 questions, you understand what exists, the purpose of each table, and the scale of data—a process that would traditionally take 30+ minutes

### Phase 2: Structure Analysis - How is Data Organized?

Now that you know what exists, understand how data is structured.

Look at the structure of important tables:

``` Natural Language Query
Show me the structure of the users table
```

**AI Response**:
```
The users table has the following columns:
- id (integer, primary key)
- email (varchar, unique)
- username (varchar)
- password_hash (varchar)
- first_name (varchar)
- last_name (varchar)
- created_at (timestamp)
- updated_at (timestamp)
- status (varchar) - values: active, inactive, suspended
- email_verified (boolean)
```

**Follow-up Query**:
```
What's the structure of the orders table?
```

**What You Learn**: Column names, data types, and constraints reveal how entities are defined and what validation exists.

Understand how records are uniquely identified:

``` Natural Language Query
Show me the primary key columns for all tables
```

**AI Response**:
```
users: id
products: id
categories: id
orders: id
order_items: id
payments: id
reviews: id
shipping_addresses: id
inventory: product_id
wishlists: id
promotions: id
```

**What You Learn**: Most tables use simple integer IDs. `inventory` uses `product_id` as primary key, suggesting a one-to-one relationship with products.

Discover what data is required vs. optional:

``` Natural Language Query
Which columns in the users table allow NULL values?
```

**AI Response**:
```
Nullable columns in users table:
- first_name
- last_name
- phone_number
- date_of_birth
- profile_image

Non-nullable columns:
- id, email, username, password_hash, created_at, status
```

**What You Learn**: Email and username are required (non-nullable), but names are optional. This suggests users might sign up with minimal information.

Identify uniqueness requirements:

``` Natural Language Query
What columns have unique constraints?
```

**AI Response**:
```
Unique constraints found:
- users.email
- users.username
- products.sku
- orders.order_number
```

**What You Learn**: Users can't duplicate emails or usernames. Products have unique SKUs. Orders have unique order numbers for customer reference.

### Phase 3: Data Sampling - What's Actually in There?

Structure tells you what can exist. Sampling shows what actually exists.

Look at real data to understand patterns:

``` Natural Language Query
Show me 5 sample users
```

**AI Response**: Interactive table showing 5 user records

![SQL Query Results](/images/106-chat-sql-query-results.png)

**What You See**:
- User IDs are sequential integers starting from 1
- Emails follow normal patterns (name@domain.com)
- Created dates range from 2023 to present
- Status is mostly "active" with a few "inactive"
- Some users have NULL names

**Key Insight**: This is real customer data, not test data. Handle carefully.

Understand how data is spread:

``` Natural Language Query
Count users by status
```

**AI Response**:
```
active: 14,123
inactive: 987
suspended: 124
```

**Follow-up**:
```
What's the distribution of products by category?
```

**AI Response**: Shows category breakdown with counts

**What You Learn**: Most users are active (healthy sign). Product distribution reveals which categories are most populated.

Understand temporal scope:

``` Natural Language Query
What's the date range of orders in the database?
```

**AI Response**:
```
Earliest order: 2022-03-15
Latest order: 2024-11-12
Total span: 2 years, 7 months
```

**Follow-up**:
```
Show me order count by month for the last 6 months
```

**AI Response**: Monthly breakdown showing recent trends

![Aggregation Query](/images/109-chat-aggregation-query.png)

**What You Learn**: Database contains over 2 years of historical data. Recent months show growth pattern (or decline, depending on the trend).

Look for potential issues:

``` Natural Language Query
Show me users with missing email addresses
```

**AI Response**:
```
0 rows returned
```

**More Quality Checks**:
```
Find products with null or zero prices
Show me orders with no order items
Find any users created in the future
```

**What You Learn**: Email is always populated (good data quality). These targeted checks reveal if there are data integrity issues to be aware of.

Understand typical data patterns:

``` Natural Language Query
What are the most common user email domains?
```

**AI Response**:
```
gmail.com: 4,567
yahoo.com: 2,345
hotmail.com: 1,234
company.com: 987
outlook.com: 876
```

**Follow-up**:
```
What are the most popular product categories?
```

**What You Learn**: User base is primarily consumer (Gmail/Yahoo). Product category popularity guides where to focus analysis.

The AI assistant generates appropriate aggregation queries automatically. Toggle to SQL view to see how grouping and counting work.

### Phase 4: Relationship Mapping - How Do Tables Connect?

Understanding relationships reveals how data flows through the system.

Ask about relationships directly:

``` Natural Language Query
How is the orders table related to other tables?
```

**AI Response**:
```
The orders table has the following relationships:
- orders.user_id → users.id (many-to-one)
- orders.shipping_address_id → shipping_addresses.id (many-to-one)
- orders → order_items (one-to-many)
- orders → payments (one-to-one)
```

**What You Learn**: Orders connect to users (who placed them), shipping addresses (where they go), order items (what's in them), and payments (how they're paid).

Follow a customer's journey through the system:

``` Natural Language Query
Show me a specific user's orders with their items
```

**AI Response**: Executes join query showing user → orders → order_items

``` Follow-Up
For user id 5, show their orders with shipping addresses
```

**AI Response**: Multi-table join with user, orders, and shipping_addresses

**What You Learn**: You can trace a complete customer journey from user record through orders to shipping and payment.

Identify junction tables:

``` Natural Language Query
How are products related to categories?
```

**AI Response**:
```
products.category_id → categories.id (many-to-one)
Each product belongs to one category
```

**Follow-up**:
```
Are there any many-to-many relationships?
```

**AI Response**:
```
Yes, the wishlists table connects users to products:
- wishlists.user_id → users.id
- wishlists.product_id → products.id
Multiple users can wishlist the same product
Users can have multiple products in their wishlist
```

**What You Learn**: Products belong to single categories (simpler model). Wishlists enable many-to-many user-product relationships.

Build a mental model of the entire system:

``` Natural Language Query
Describe all the relationships between users, orders, products, and payments
```

**AI Response**:
```
Data Flow:
1. users (customer accounts)
   ↓
2. orders (purchase records)
   - References user_id
   - References shipping_address_id
   ↓
3. order_items (line items in each order)
   - References order_id
   - References product_id
   ↓
4. products (items for sale)
   - References category_id
   ↓
5. payments (financial transactions)
   - References order_id
```

**What You Learn**: The complete data flow from user through order to payment. This mental model guides all future queries.

Verify referential integrity:

``` Natural Language Query
Find any orders with no matching user
```

**AI Response**:
```
0 rows returned
```

**More Integrity Checks**:
```
Find order_items with no matching product
Show me payments with no corresponding order
```

**What You Learn**: Foreign key constraints are enforced (good database design). No orphaned records exist.

At this point, you have a complete understanding of the database structure, relationships, and data quality—all without writing a single SQL query yourself

### Phase 5: Quality Assessment - Is Data Clean?

Before relying on this data, assess its quality and completeness.

Identify missing or incomplete data:

``` Natural Language Query
What percentage of users have verified emails?
```

**AI Response**:
```
Verified: 12,456 (81.7%)
Not verified: 2,778 (18.3%)
```

**Follow-up Checks**:
```
How many orders are missing shipping addresses?
What percentage of products have reviews?
How many users have never placed an order?
```

**What You Learn**: Email verification rate is good. You might find that some data is incomplete (e.g., 30% of users never ordered), which affects analysis.

Find unusual data that might indicate issues:

``` Natural Language Query
Show me products with unusually high or low prices
```

**AI Response**: Products priced at $0 or over $10,000

**More Outlier Checks**:
```
Find orders with extremely high total amounts
Show me users created with future dates
Find products with negative inventory
```

**What You Learn**: A few products priced at $0 might be errors or special items. High-value orders could be bulk purchases or fraud.

Verify data makes logical sense:

``` Natural Language Query
Are there any orders where the total doesn't match the sum of order items?
```

**AI Response**: Executes calculation comparing order totals to sum of items

``` Follow-Up
Find users with orders but no shipping address on record
Show me payments that don't match their order totals
```

**What You Learn**: Data consistency issues that might indicate bugs in the application or data entry problems.

Understand how current the data is:

``` Natural Language Query
When was the most recent order placed?
```

**AI Response**:
```
Most recent order: 2024-11-12 14:35:22 (2 hours ago)
```

**Follow-up**:
```
How many orders were placed today?
When were products last updated?
```

**What You Learn**: Data is current and actively updated. This is a live, operational database.

If you discover data quality issues during exploration, document them but avoid attempting fixes without proper authorization, especially on production databases

## Real-World Scenarios

### Scenario 1: New Developer's First Day

**Context**: Sarah joins a team maintaining a legacy application. She needs to implement a new feature that displays a customer's order history.

**Traditional Approach**: Read outdated wiki documentation, ask busy teammates, study schema diagrams, write test queries.

**AI-Powered Approach**:

``` AI Conversation
Sarah: What tables store customer and order data?

AI: The main tables are:
- users (customer accounts)
- orders (purchase records)
- order_items (individual items in each order)
- products (item details)
```

```
Sarah: How do I join users to their orders?

AI: Join users.id to orders.user_id
```

```
Sarah: Show me a user with their orders

AI: [Returns sample data showing the join]
```

```
Sarah: Show me all orders for user 5 sorted by date

AI: [Generates query]
```

![SQL Code View](/images/107-chat-sql-code-view.png)

Sarah toggles to SQL view, copies the query, and adapts it for her feature.

**Time Saved**: 3 hours reduced to 15 minutes

### Scenario 2: Analyst's Urgent Report Request

**Context**: Mike's manager needs a report on customer behavior by 2 PM. Mike has never seen this database before.

**Traditional Approach**: Request database access, wait for credentials, study schema, write queries, debug syntax errors, iterate on results.

**AI-Powered Approach**:

``` AI Conversation
Mike: What data do you have about customers?

AI: [Lists users table with fields]

Mike: Show me customer signup trends by month

AI: [Generates date aggregation query and shows results]
```

```
Mike: Break that down by email domain

AI: [Adds email domain extraction to the query]

Mike: Only show the last 6 months

AI: [Adds date filter]
```

Uses WhoDB's export feature to download results for presentation

**Time Saved**: 4 hours reduced to 30 minutes. Report delivered on time.

### Scenario 3: DBA's Database Audit

**Context**: James needs to audit a newly acquired database to identify performance issues and optimization opportunities.

**Traditional Approach**: Generate schema reports, manually count rows, write complex metadata queries, trace foreign keys through system tables.

**AI-Powered Approach**:

``` AI Conversation
James: What are the largest tables by row count?

AI: [Shows table sizes in descending order]

James: Which tables have the most columns?

AI: [Lists tables with column counts]
```

```
James: Show me tables without primary keys

AI: [Queries information schema]

James: Find columns that should probably be indexed

AI: [Identifies foreign key columns without indexes]
```

```
James: Show me all foreign key relationships

AI: [Lists all FK constraints with parent/child tables]

James: Are there any circular dependencies?

AI: [Analyzes relationship chains]
```

**Time Saved**: 2 days reduced to 3 hours

### Scenario 4: Consultant's Client Assessment

**Context**: Lisa is consulting for a client who needs database optimization. She has 2 hours to understand their system before the meeting.

**AI-Powered Approach**:

``` AI Conversation Flow
Lisa: What tables exist?
→ [Lists all tables]

Lisa: Which are the core business tables?
→ [AI identifies main entity tables]

Lisa: Show me row counts and sizes
→ [Shows scale of data]

Lisa: What are the relationships between users, orders, and products?
→ [Explains data model]

Lisa: Find any data quality issues
→ [Identifies nulls, orphaned records, inconsistencies]

Lisa: Which tables have the most complex relationships?
→ [Shows junction tables and many-to-many]

Lisa: Show me order patterns by hour of day
→ [Generates temporal analysis]
```

**Outcome**: Lisa walks into the meeting with comprehensive understanding of the database, identified 3 performance bottlenecks, and has specific recommendations ready.

**Time Saved**: Would require 1-2 days of preparation, completed in 2 hours

## Best Practices for AI-Powered Exploration

### Start Broad, Then Narrow

```
What tables exist?
How many rows in each table?
```
Get the big picture first.

```
Which tables are most important?
What are the core business entities?
```
Focus on what matters.

```
Show me the structure of [specific table]
How is [table] related to [other table]?
```
Understand details only after you have context.

### Use Follow-Up Questions

The AI remembers your conversation context:

``` Good Conversation Flow
You: Show me all users
AI: [Returns 15,000 users]

You: Just show 10
AI: [Limits to 10, remembers we're talking about users]

You: Only active ones
AI: [Filters to active, still knows it's the users table]

You: With their order counts
AI: [Adds join to orders with COUNT, maintains all previous context]
```

Each question builds on previous context. You don't need to repeat "users table" in every question

### Toggle to SQL View for Learning

Every AI response that returns data can show you the generated SQL:

![SQL Code View](/images/107-chat-sql-code-view.png)

**Why View SQL?**
- Learn SQL syntax naturally
- Verify the AI understood correctly
- Copy queries for documentation
- Adapt for your own code

### Save Useful Queries to Scratchpad

When you discover useful queries, preserve them:

![Move to Scratchpad](/images/113-chat-move-to-scratchpad-dialog.png)

**What to Save**:
- Queries that reveal important patterns
- Complex joins you'll need again
- Data quality checks worth repeating
- Exploratory queries that became important

### Ask About Data, Not Just Structure

Go beyond "what columns exist" to "what data exists":

``` Structure Questions (Good)
What's in the users table?
How are orders structured?

Data Questions (Better)
What are common user email domains?
What's the distribution of order statuses?
Are there any data quality issues?
```

Data questions reveal how the database is actually used.

### Verify AI Responses with Samples

When the AI makes a claim, verify it:

```
AI: "Orders always have a payment record"

You: Show me orders without payments
[Verify the claim is true]
```

AI is usually correct, but always verify important assumptions with sample data

## Time Comparison: Traditional vs. AI Exploration

### Traditional Exploration Timeline

**Day 1 (4-6 hours)**:
- 1 hour: Request and obtain database access
- 2 hours: Study schema documentation (if it exists)
- 1-2 hours: Write queries to list tables and examine structures
- 1 hour: Sample data from key tables

**Day 2 (4-6 hours)**:
- 2 hours: Manually trace foreign key relationships
- 2 hours: Write queries to understand data distributions
- 1 hour: Identify data quality issues
- 1 hour: Document findings

**Total: 8-12 hours spread over 2 days**

### AI-Powered Exploration Timeline

**Session 1 (30-45 minutes)**:
- 5 minutes: Connect to database and open AI chat
- 10 minutes: Discover all tables and their purposes
- 10 minutes: Sample data from key tables
- 10 minutes: Understand relationships
- 5 minutes: Check data quality

**Session 2 (15-30 minutes)** (optional deeper dive):
- 10 minutes: Explore edge cases
- 10 minutes: Analyze data distributions
- 5 minutes: Document findings (copy AI responses)

**Total: 45-75 minutes in a single session**

### Speed Improvement: 10-15x Faster

| Task | Traditional | AI-Powered | Speedup |
|------|-------------|------------|---------|
| Discover tables | 20 min | 2 min | 10x |
| Understand structure | 60 min | 5 min | 12x |
| Sample data | 45 min | 5 min | 9x |
| Map relationships | 120 min | 10 min | 12x |
| Check data quality | 60 min | 10 min | 6x |
| Document findings | 45 min | 5 min | 9x |
| **Total** | **8-12 hours** | **45-75 min** | **10-15x** |

AI-powered exploration delivers comprehensive understanding in under an hour versus the traditional 1-2 days

## Common Exploration Questions

Use these question templates for efficient exploration:

### Discovery Phase
```
What tables are available?
How many rows are in each table?
What are the largest tables?
Which tables were most recently modified?
```

### Structure Phase
```
What's the structure of the [table] table?
What columns are in [table]?
Which columns in [table] are required vs. optional?
What are the primary keys?
```

### Data Phase
```
Show me 10 sample rows from [table]
What are the most common values in [column]?
What's the date range of data in [table]?
Count records by [category column]
```

### Relationship Phase
```
How is [table1] related to [table2]?
What foreign keys exist in [table]?
Show me [entity] with its related [other entities]
Are there any many-to-many relationships?
```

### Quality Phase
```
Find rows with missing [column] values
Show me duplicate records in [table]
Are there any orphaned records in [table]?
What percentage of [table] has [condition]?
Find any [entity] with invalid [attribute]
```

## Handling Errors During Exploration

![Error Message](/images/108-chat-error-message.png)

Even with AI, you might encounter errors:

**Error**: `relation "tablename" does not exist`

**Cause**: Typo in table name or table is in different schema

**Solution**:
```
What tables are available?
[Verify correct table name and spelling]
```

**Error**: `query execution timeout`

**Cause**: Query on very large table without limits

**Solution**:
```
Show me just 100 rows from [large table]
Count rows in [table] where [recent date filter]
```
Always add limits and filters for large tables.

**Error**: `permission denied for table`

**Cause**: Database user lacks SELECT permission

**Solution**: Contact database administrator for read access. In production, you should have read-only credentials.

**Error**: AI returns unexpected results

**Cause**: Question could be interpreted multiple ways

**Solution**: Rephrase more specifically:
```
Ambiguous: "Show me recent records"
Specific: "Show me users created in the last 7 days"
```

## Next Steps

Now that you've mastered AI-powered database exploration, take your skills further:

Use AI to generate complex analysis queries and business reports

Master advanced AI querying techniques and conversation patterns

Learn manual exploration techniques using WhoDB's schema visualization

Save and refine AI-generated queries in the Scratchpad

## Key Takeaways

What traditionally takes 1-2 days can be accomplished in 45-75 minutes using natural language conversation with the AI assistant.

Ask questions in plain English. The AI generates syntactically correct SQL automatically, adapting to your specific database type.

The AI remembers your conversation, allowing natural follow-up questions without repeating context.

While AI is highly accurate, always verify critical assumptions by sampling actual data.

Begin with high-level inventory (what tables exist), then progressively narrow focus to specific areas of interest.

Use the "Move to Scratchpad" feature to preserve valuable queries for documentation and reuse.

Toggle to SQL view to see how your questions translate to queries—an invaluable way to learn SQL naturally.

You're now equipped to explore any unfamiliar database rapidly using AI. What used to take days now takes less than an hour, allowing you to become productive with new systems immediately.

---


# best-practices/security.mdx
## Security Best Practices

Essential security practices for managing databases with WhoDB

# Security Best Practices

Database security is critical for protecting sensitive data and maintaining system integrity. This guide covers essential security practices when using WhoDB to manage your databases.

## Connection Security

### Use Encrypted Connections

Always use encrypted connections when accessing databases over networks. WhoDB supports SSL/TLS for all major database systems.

**PostgreSQL SSL Configuration:**
```
Host: production-db.example.com
Port: 5432
Username: whodb_user
Password: [secure password]
Database: myapp
SSL Mode: require
```

Configure SSL modes based on your security requirements:
- **disable**: No SSL (only for local development)
- **require**: SSL required, but certificate not verified
- **verify-ca**: SSL required, verify certificate authority
- **verify-full**: SSL required, verify CA and hostname

**MySQL SSL Configuration:**
```
Host: mysql-prod.example.com
Port: 3306
Username: app_reader
Password: [secure password]
Database: production
SSL: Enabled
SSL CA: /path/to/ca-cert.pem
```

### Network Security

Implement network-level security controls:

**Firewall Rules:**
- Restrict database access to specific IP ranges
- Use VPN or SSH tunneling for remote access
- Never expose database ports directly to the internet

**SSH Tunneling:**
When direct database access is restricted, use SSH tunneling:
```
ssh -L 5432:localhost:5432 user@bastion-host.example.com
```
Then connect WhoDB to `localhost:5432`.

### Connection String Security

Never hardcode credentials in application code or configuration files. WhoDB stores connection profiles securely, but additional precautions are necessary:

**Environment Variables:**
Store sensitive connection details in environment variables rather than committing them to version control.

**Secrets Management:**
For production environments, use dedicated secrets management solutions:
- HashiCorp Vault
- AWS Secrets Manager
- Azure Key Vault
- Google Cloud Secret Manager

## Authentication and Access Control

### Principle of Least Privilege

Create database users with minimum necessary permissions. Avoid using administrative accounts for routine operations.

**PostgreSQL Read-Only User:**
```
CREATE ROLE whodb_readonly;
GRANT CONNECT ON DATABASE myapp TO whodb_readonly;
GRANT USAGE ON SCHEMA public TO whodb_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO whodb_readonly;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT SELECT ON TABLES TO whodb_readonly;

CREATE USER readonly_user WITH PASSWORD 'secure_password';
GRANT whodb_readonly TO readonly_user;
```

**MySQL Read-Only User:**
```
CREATE USER 'readonly_user'@'%' IDENTIFIED BY 'secure_password';
GRANT SELECT ON myapp.* TO 'readonly_user'@'%';
FLUSH PRIVILEGES;
```

### Role-Based Access

Create different database roles for different purposes:

**Development Access:**
- Read and write access to development databases
- No access to production data
- Limited to specific schemas or tables

**Analyst Access:**
- Read-only access to specific tables
- No access to sensitive columns (PII, passwords)
- Query execution limits

**Operations Access:**
- Read and write for specific operations
- Time-limited credentials
- Audit logging required

**Administrative Access:**
- Full access for emergencies only
- Multi-factor authentication required
- Comprehensive audit trail

### Password Management

Implement strong password policies:

**Password Requirements:**
- Minimum 16 characters
- Mix of uppercase, lowercase, numbers, and symbols
- No dictionary words or common patterns
- Regular rotation (90 days for sensitive systems)

**Password Storage:**
- Use password managers for credential storage
- Never share passwords via email or chat
- Use separate passwords for each environment

## Credential Management

### Rotating Credentials

Regularly rotate database credentials, especially after:
- Employee departure
- Suspected security breach
- Routine security maintenance
- Application updates

**Rotation Process:**
1. Create new credentials with same permissions
2. Update applications to use new credentials
3. Verify all services are using new credentials
4. Revoke old credentials
5. Document rotation in security logs

### Multi-Factor Authentication

Where supported, enable multi-factor authentication for database access:
- Time-based one-time passwords (TOTP)
- Hardware security keys
- Biometric authentication

## Audit Logging

### Database-Level Auditing

Enable comprehensive audit logging for production databases.

**PostgreSQL pgAudit:**
```
CREATE EXTENSION pgaudit;
ALTER SYSTEM SET pgaudit.log = 'write, ddl';
ALTER SYSTEM SET pgaudit.log_level = 'log';
ALTER SYSTEM SET pgaudit.log_relation = on;
SELECT pg_reload_conf();
```

**MySQL Audit Plugin:**
```
INSTALL PLUGIN audit_log SONAME 'audit_log.so';
SET GLOBAL audit_log_policy = 'ALL';
SET GLOBAL audit_log_format = 'JSON';
```

### Application-Level Logging

WhoDB operations should be logged at the application level:
- Track which users execute which queries
- Log data modifications with timestamps
- Record failed authentication attempts
- Monitor for suspicious activity patterns

### Log Review

Regularly review audit logs for:
- Unauthorized access attempts
- Unusual query patterns
- Large data exports
- Schema modifications
- Failed authentication attempts
- After-hours access

## Production Access Guidelines

### Change Management

Implement strict change management for production databases:

**Before Making Changes:**
1. Document the intended change
2. Get approval from required stakeholders
3. Test changes in staging environment
4. Schedule maintenance window if needed
5. Prepare rollback plan

**During Changes:**
1. Verify current database state
2. Create backup before modifications
3. Execute changes with minimal scope
4. Verify results immediately
5. Monitor system performance

**After Changes:**
1. Document actual changes made
2. Update configuration management
3. Notify affected teams
4. Monitor for issues
5. Archive change documentation

### Emergency Access

Establish procedures for emergency production access:

**Break-Glass Procedures:**
- Defined criteria for emergency access
- Documented escalation path
- Automatic notification to security team
- Comprehensive logging of all actions
- Post-incident review required

## Data Privacy and Compliance

### Sensitive Data Handling

Identify and protect sensitive data:

**Personal Identifiable Information (PII):**
- Names, addresses, phone numbers
- Social security numbers
- Financial information
- Health records

**Protection Strategies:**
- Column-level encryption for sensitive fields
- Masking in non-production environments
- Access logging for PII queries
- Retention policies and data deletion

### Regulatory Compliance

Ensure database management practices comply with relevant regulations:

**GDPR Requirements:**
- Right to be forgotten (data deletion)
- Data portability
- Consent management
- Breach notification procedures

**HIPAA Requirements:**
- Access controls and authentication
- Audit logging
- Encryption at rest and in transit
- Business associate agreements

**PCI DSS Requirements:**
- Cardholder data protection
- Strong access controls
- Regular security testing
- Incident response plan

## Query Security

### SQL Injection Prevention

While WhoDB uses parameterized queries internally, be cautious with:

**Dynamic Query Construction:**
Never concatenate user input directly into SQL queries in scratchpad mode:
```
-- DANGEROUS: Never do this
SELECT * FROM users WHERE username = '[user_input]';

-- SAFE: Use parameters
SELECT * FROM users WHERE username = $1;
```

**Stored Procedures:**
Review stored procedures for SQL injection vulnerabilities, especially those using dynamic SQL.

### Query Whitelisting

For automated or scheduled queries:
- Maintain approved query templates
- Review and approve new query patterns
- Monitor for deviations from approved queries
- Use views to restrict data access

## Backup and Recovery

### Backup Verification

Security depends on reliable backups:

**Backup Testing:**
- Regularly test backup restoration
- Verify backup encryption
- Test recovery time objectives (RTO)
- Test recovery point objectives (RPO)

**Backup Security:**
- Encrypt backups at rest
- Secure backup transfer channels
- Restrict access to backup files
- Store backups in separate locations

## Security Monitoring

### Real-Time Monitoring

Implement monitoring for security events:

**Alert Triggers:**
- Multiple failed authentication attempts
- Queries accessing unusual data volumes
- Schema modifications
- Privilege escalations
- Unusual access times or locations

**Monitoring Tools:**
- Database activity monitoring (DAM)
- Security information and event management (SIEM)
- Intrusion detection systems (IDS)
- Log aggregation and analysis

## Incident Response

### Incident Detection

Establish procedures for detecting security incidents:
- Automated alerting for suspicious activity
- Regular log review
- User reports of unusual behavior
- Performance anomalies

### Response Plan

Create and maintain an incident response plan:

**Immediate Actions:**
1. Isolate affected systems
2. Preserve evidence
3. Assess scope and impact
4. Notify security team
5. Begin containment

**Investigation:**
1. Analyze logs and audit trails
2. Identify attack vectors
3. Determine data exposure
4. Document findings
5. Report to stakeholders

**Recovery:**
1. Patch vulnerabilities
2. Restore from clean backups
3. Reset compromised credentials
4. Verify system integrity
5. Resume normal operations

**Post-Incident:**
1. Conduct lessons learned review
2. Update security procedures
3. Implement additional controls
4. Notify affected parties if required
5. Document incident and response

## Security Checklist

Use this checklist before granting database access:

**Connection Security:**
- [ ] SSL/TLS enabled and configured
- [ ] Network access restricted by IP
- [ ] VPN or SSH tunnel required for remote access
- [ ] No database ports exposed to internet

**Authentication:**
- [ ] Strong, unique passwords used
- [ ] Service accounts follow least privilege
- [ ] No shared credentials
- [ ] MFA enabled where available

**Authorization:**
- [ ] User has minimum necessary permissions
- [ ] Access limited to required databases/schemas
- [ ] Sensitive tables have additional restrictions
- [ ] Read-only access used where possible

**Auditing:**
- [ ] Database audit logging enabled
- [ ] Application logging configured
- [ ] Log retention policy defined
- [ ] Log review process established

**Compliance:**
- [ ] Regulatory requirements identified
- [ ] Data classification completed
- [ ] Privacy controls implemented
- [ ] Incident response plan documented

**Monitoring:**
- [ ] Security alerts configured
- [ ] Anomaly detection enabled
- [ ] Regular security reviews scheduled
- [ ] Incident response procedures tested

## Summary

Database security requires a layered approach combining technical controls, operational procedures, and continuous monitoring. By following these best practices, you can significantly reduce security risks while maintaining the productivity benefits of WhoDB's database management capabilities. Regular security reviews and updates to procedures ensure your database security posture remains strong as threats evolve.

---


# best-practices/performance.mdx
## Performance Optimization

Techniques for optimizing database query performance in WhoDB

# Performance Optimization

Database performance directly impacts application responsiveness and user experience. This guide covers proven techniques for optimizing query performance and managing database resources effectively in WhoDB.

## Understanding Query Performance

### Query Execution Fundamentals

Before optimizing queries, understand how databases execute them:

**Query Processing Stages:**
1. **Parsing**: SQL syntax validation and query tree construction
2. **Planning**: Query optimizer determines execution strategy
3. **Optimization**: Query plan refinement based on statistics
4. **Execution**: Actual data retrieval and processing
5. **Result Return**: Data formatting and transmission

**Performance Factors:**
- Table size and row count
- Index availability and quality
- Data distribution and statistics
- Query complexity and joins
- Hardware resources (CPU, memory, I/O)
- Concurrent user load

### Performance Metrics

Track these key metrics to identify performance issues:

**Query-Level Metrics:**
- Execution time (total and breakdown)
- Rows examined vs. rows returned
- Index usage
- Temporary table creation
- Sort operations

**System-Level Metrics:**
- Query throughput (queries per second)
- Connection pool utilization
- Cache hit ratios
- I/O wait times
- CPU and memory usage

## Query Optimization Strategies

### Use EXPLAIN to Analyze Queries

The EXPLAIN command reveals how the database executes your query. WhoDB makes it easy to analyze query plans.

**PostgreSQL EXPLAIN:**
```
EXPLAIN ANALYZE
SELECT u.username, o.order_date, o.total
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.order_date > '2024-01-01'
ORDER BY o.order_date DESC;
```

**Key Information to Review:**
- **Seq Scan**: Full table scan (potentially slow)
- **Index Scan**: Using an index (generally fast)
- **Nested Loop**: Join method for small result sets
- **Hash Join**: Join method for larger result sets
- **Sort**: Explicit sorting operation
- **Cost**: Estimated query cost
- **Actual Time**: Real execution time

**MySQL EXPLAIN:**
```
EXPLAIN FORMAT=JSON
SELECT p.product_name, COUNT(*) as order_count
FROM products p
JOIN order_items oi ON p.id = oi.product_id
GROUP BY p.product_name
HAVING order_count > 100;
```

**Warning Signs:**
- Type: ALL (full table scan)
- Extra: Using filesort (disk-based sorting)
- Extra: Using temporary (temporary table creation)
- Rows: Large numbers indicating inefficiency

### Optimize WHERE Clauses

WHERE clause optimization is fundamental to query performance.

**Use Indexed Columns:**
```
-- Inefficient: Function on indexed column prevents index use
SELECT * FROM users WHERE LOWER(email) = 'user@example.com';

-- Efficient: Direct comparison uses index
SELECT * FROM users WHERE email = 'user@example.com';
```

**Avoid Leading Wildcards:**
```
-- Inefficient: Leading wildcard prevents index use
SELECT * FROM products WHERE name LIKE '%shoes%';

-- Efficient: Trailing wildcard can use index
SELECT * FROM products WHERE name LIKE 'running%';
```

**Use Appropriate Data Types:**
```
-- Inefficient: Type conversion required
SELECT * FROM orders WHERE order_id = '12345';

-- Efficient: Matching data type
SELECT * FROM orders WHERE order_id = 12345;
```

### Optimize JOIN Operations

Joins are common performance bottlenecks in complex queries.

**Join Order Matters:**
Join smaller tables first to reduce intermediate result sizes:
```
-- Better: Join smaller lookup tables first
SELECT c.name, o.order_date, p.product_name
FROM customers c
JOIN orders o ON c.id = o.customer_id
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id
WHERE c.country = 'US';
```

**Use Appropriate Join Types:**
- **INNER JOIN**: Only matching rows (most efficient)
- **LEFT JOIN**: All left table rows (use when necessary)
- **RIGHT JOIN**: All right table rows (consider reversing to LEFT)
- **FULL OUTER JOIN**: All rows from both tables (least efficient)

**Index Foreign Keys:**
Always create indexes on foreign key columns:
```
CREATE INDEX idx_orders_customer_id ON orders(customer_id);
CREATE INDEX idx_order_items_order_id ON order_items(order_id);
CREATE INDEX idx_order_items_product_id ON order_items(product_id);
```

### Limit Result Sets

Retrieving unnecessary data wastes resources.

**Use LIMIT for Large Tables:**
```
-- Better: Limit results when you don't need all rows
SELECT * FROM logs
WHERE log_date > '2024-01-01'
ORDER BY log_date DESC
LIMIT 1000;
```

**Select Specific Columns:**
```
-- Inefficient: Retrieves all columns
SELECT * FROM users WHERE active = true;

-- Efficient: Only needed columns
SELECT id, username, email FROM users WHERE active = true;
```

**Use Pagination:**
For displaying data in pages, use offset and limit:
```
-- Page 3, 50 items per page
SELECT id, title, created_at
FROM articles
ORDER BY created_at DESC
LIMIT 50 OFFSET 100;
```

Note: For large offsets, consider keyset pagination for better performance.

## Indexing Strategies

### Index Basics

Indexes dramatically improve query performance but require careful planning.

**When to Create Indexes:**
- Columns frequently used in WHERE clauses
- Columns used in JOIN conditions
- Columns used in ORDER BY clauses
- Columns used in GROUP BY clauses
- Foreign key columns

**Index Types:**
- **B-tree**: Default, good for equality and range queries
- **Hash**: Fast equality comparisons, no range support
- **GIN/GiST**: Full-text search and array operations
- **Bitmap**: Multiple index combination

### Composite Indexes

Composite indexes cover multiple columns and are powerful optimization tools.

**Column Order Matters:**
```
-- Index for queries filtering by country then city
CREATE INDEX idx_users_country_city ON users(country, city);

-- This query uses the index efficiently
SELECT * FROM users WHERE country = 'US' AND city = 'Seattle';

-- This query also uses the index
SELECT * FROM users WHERE country = 'US';

-- This query does NOT use the index efficiently
SELECT * FROM users WHERE city = 'Seattle';
```

**General Rule:**
Place the most selective (filters to fewer rows) column first, and columns used together frequently.

### Covering Indexes

Covering indexes include all columns needed for a query, eliminating table lookups.

**Example:**
```
-- Query that runs frequently
SELECT username, email FROM users WHERE country = 'US';

-- Covering index includes all needed columns
CREATE INDEX idx_users_country_covering
ON users(country)
INCLUDE (username, email);
```

### Index Maintenance

Indexes require maintenance to remain effective.

**PostgreSQL Index Maintenance:**
```
-- Rebuild index to remove bloat
REINDEX INDEX idx_users_email;

-- Update statistics for query planner
ANALYZE users;

-- Vacuum to reclaim space
VACUUM ANALYZE users;
```

**MySQL Index Maintenance:**
```
-- Optimize table to rebuild indexes
OPTIMIZE TABLE users;

-- Update statistics
ANALYZE TABLE users;
```

**When to Rebuild:**
- After large bulk operations
- When query performance degrades
- Regular maintenance schedule (monthly/quarterly)
- After significant data distribution changes

## Managing Large Result Sets

### Cursor-Based Pagination

For large result sets, cursor-based pagination is more efficient than offset-based.

**Keyset Pagination:**
```
-- Initial query
SELECT id, username, created_at
FROM users
WHERE active = true
ORDER BY created_at DESC, id DESC
LIMIT 50;

-- Next page (using last seen values)
SELECT id, username, created_at
FROM users
WHERE active = true
  AND (created_at, id) < ('2024-01-15 10:30:00', 12345)
ORDER BY created_at DESC, id DESC
LIMIT 50;
```

This approach maintains consistent performance regardless of page depth.

### Streaming Results

For very large exports, stream results instead of loading all into memory:

**Batch Processing:**
```
-- Process in chunks
SELECT id, email, status
FROM users
WHERE id > $last_processed_id
ORDER BY id
LIMIT 10000;
```

Process each batch, update `$last_processed_id`, and repeat.

### Aggregation Optimization

Optimize aggregation queries for large datasets.

**Use Materialized Views:**
```
-- PostgreSQL materialized view for expensive aggregation
CREATE MATERIALIZED VIEW daily_sales_summary AS
SELECT
  DATE(order_date) as sale_date,
  COUNT(*) as order_count,
  SUM(total_amount) as total_sales
FROM orders
GROUP BY DATE(order_date);

-- Refresh periodically
REFRESH MATERIALIZED VIEW daily_sales_summary;
```

**Use Incremental Aggregation:**
Instead of recalculating everything, maintain running totals:
```
-- Update summary table incrementally
INSERT INTO daily_sales_summary (sale_date, order_count, total_sales)
SELECT
  DATE(order_date),
  COUNT(*),
  SUM(total_amount)
FROM orders
WHERE order_date >= CURRENT_DATE
GROUP BY DATE(order_date)
ON CONFLICT (sale_date)
DO UPDATE SET
  order_count = EXCLUDED.order_count,
  total_sales = EXCLUDED.total_sales;
```

## Connection Pooling

### Understanding Connection Pools

Database connections are expensive to create. Connection pooling reuses connections for better performance.

**Benefits:**
- Reduced connection overhead
- Better resource utilization
- Improved scalability
- Consistent performance under load

### Connection Pool Configuration

**Key Parameters:**
- **Minimum Pool Size**: Connections kept open during idle periods
- **Maximum Pool Size**: Upper limit on concurrent connections
- **Connection Timeout**: Maximum wait time for available connection
- **Idle Timeout**: Time before idle connection is closed
- **Max Lifetime**: Maximum connection age before refresh

**Recommended Starting Values:**
```
Minimum Pool Size: 5
Maximum Pool Size: 20
Connection Timeout: 30 seconds
Idle Timeout: 10 minutes
Max Lifetime: 30 minutes
```

Adjust based on:
- Application concurrent user count
- Query execution time
- Database server capacity
- Network latency

### Connection Pool Monitoring

Monitor these metrics:
- Active connections
- Idle connections
- Connection wait times
- Connection creation rate
- Connection errors

**Warning Signs:**
- Frequent connection timeouts
- High connection wait times
- Maximum pool size consistently reached
- Many short-lived connections

## Query Caching

### Application-Level Caching

Cache query results at the application level for frequently accessed data.

**Good Candidates for Caching:**
- Reference data (countries, categories)
- User session data
- Dashboard aggregations
- Recently viewed items

**Cache Invalidation Strategies:**
- Time-based expiration (TTL)
- Event-based invalidation
- Versioned cache keys
- Least Recently Used (LRU) eviction

### Database Query Cache

Some databases provide built-in query caching.

**MySQL Query Cache (Deprecated in 8.0):**
Modern MySQL versions don't include query cache. Use application-level caching instead.

**PostgreSQL Shared Buffers:**
PostgreSQL caches frequently accessed data pages in shared buffers. Configure appropriately:
```
-- View current setting
SHOW shared_buffers;

-- Recommended: 25% of system RAM for dedicated database server
ALTER SYSTEM SET shared_buffers = '4GB';
```

## Performance Testing

### Load Testing

Test performance under realistic load conditions before deploying to production.

**Testing Scenarios:**
- Normal load: Expected concurrent users
- Peak load: Maximum anticipated users
- Stress test: Beyond expected capacity
- Endurance test: Sustained load over time

**Tools:**
- Apache JMeter
- pgbench (PostgreSQL)
- sysbench (MySQL)
- Custom load scripts

### Performance Baselines

Establish performance baselines to detect regressions.

**Baseline Metrics:**
- Query execution times (p50, p95, p99)
- Throughput (queries per second)
- Error rates
- Resource utilization

**Regular Monitoring:**
- Compare current performance to baseline
- Investigate significant deviations
- Update baselines after infrastructure changes
- Track trends over time

## Performance Tips Summary

**Query Optimization:**
- Use EXPLAIN to understand query execution
- Index foreign keys and frequently filtered columns
- Avoid functions on indexed columns in WHERE clauses
- Select only needed columns
- Use appropriate JOIN types and order

**Indexing:**
- Create indexes on columns used in WHERE, JOIN, and ORDER BY
- Use composite indexes for multi-column queries
- Consider covering indexes for frequently run queries
- Maintain indexes regularly
- Don't over-index (each index has overhead)

**Data Management:**
- Use pagination for large result sets
- Implement cursor-based pagination for deep pagination
- Stream large exports instead of loading all into memory
- Use materialized views for expensive aggregations
- Archive old data to keep tables smaller

**Resource Management:**
- Configure connection pooling appropriately
- Monitor connection pool metrics
- Implement application-level caching
- Optimize database server configuration
- Scale horizontally when vertical scaling insufficient

**Testing and Monitoring:**
- Establish performance baselines
- Test under realistic load conditions
- Monitor query performance continuously
- Set up alerts for performance degradation
- Regular performance reviews and optimization

## Common Anti-Patterns to Avoid

**N+1 Query Problem:**
```
-- Bad: Separate query for each user's orders
-- Application code loops: for each user, SELECT orders WHERE user_id = ?

-- Good: Single query with JOIN
SELECT u.*, o.*
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.active = true;
```

**SELECT * in Production:**
Always select specific columns to reduce data transfer and improve cache efficiency.

**Missing Indexes on Foreign Keys:**
Always index foreign key columns used in joins.

**Overusing DISTINCT:**
DISTINCT adds overhead. If you need DISTINCT, consider whether your joins are correct.

**Large IN Clauses:**
```
-- Less efficient for large lists
SELECT * FROM products WHERE id IN (1, 2, 3, ..., 10000);

-- Better: Use temporary table or array
SELECT * FROM products WHERE id = ANY($1::int[]);
```

## Summary

Database performance optimization is an iterative process requiring measurement, analysis, and refinement. Start with proper indexing, optimize queries using EXPLAIN, and implement efficient data access patterns. Regular monitoring and testing ensure performance remains acceptable as data volumes grow and usage patterns change. WhoDB's query analysis tools make it easy to identify and resolve performance issues before they impact users.

---


# best-practices/data-management.mdx
## Data Management Best Practices

Safe and effective data management techniques for WhoDB users

# Data Management Best Practices

Effective data management balances operational efficiency with data safety. This guide covers essential practices for managing data safely and effectively using WhoDB, from routine operations to complex data transformations.

## Data Safety Principles

### Always Backup Before Changes

The most important rule of data management is simple: always have a backup before making changes.

**Types of Changes Requiring Backups:**
- Bulk updates or deletes
- Schema modifications
- Data migrations
- Testing new queries on production data
- Running unfamiliar scripts
- Major application updates

**Backup Strategies:**
- Full database backup for major changes
- Table-level backup for isolated changes
- Row-level backup for small, targeted changes
- Transaction savepoints for multi-step operations

**Creating Backups:**

PostgreSQL:
```
# Full database backup
pg_dump -h localhost -U username -d database_name > backup_$(date +%Y%m%d_%H%M%S).sql

# Single table backup
pg_dump -h localhost -U username -d database_name -t table_name > table_backup.sql

# Compressed backup
pg_dump -h localhost -U username -d database_name | gzip > backup.sql.gz
```

MySQL:
```
# Full database backup
mysqldump -h localhost -u username -p database_name > backup_$(date +%Y%m%d_%H%M%S).sql

# Single table backup
mysqldump -h localhost -u username -p database_name table_name > table_backup.sql

# All databases
mysqldump -h localhost -u username -p --all-databases > all_databases_backup.sql
```

MongoDB:
```
# Full database backup
mongodump --host localhost --port 27017 --db database_name --out /backup/location

# Single collection backup
mongodump --host localhost --db database_name --collection collection_name --out /backup/location
```

### Verify Backups

Backups are only useful if they can be restored successfully.

**Backup Verification Process:**
1. Create test database or schema
2. Restore backup to test location
3. Verify data integrity
4. Test critical queries
5. Document verification date
6. Automate verification where possible

**Regular Testing Schedule:**
- Test restore procedures monthly
- Verify backup completeness
- Measure restoration time
- Update recovery documentation
- Train team members on restoration

### Use Transactions Appropriately

Transactions ensure data consistency by treating multiple operations as a single unit of work.

**Transaction Basics:**
```
BEGIN;

UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

-- Verify changes before committing
SELECT id, balance FROM accounts WHERE id IN (1, 2);

-- If correct:
COMMIT;

-- If incorrect:
ROLLBACK;
```

**When to Use Transactions:**
- Multiple related updates
- Data migrations
- Batch operations
- Testing complex queries
- Any operation that must be atomic

**Transaction Best Practices:**
- Keep transactions short
- Avoid user interaction during transactions
- Don't hold transactions during long operations
- Use appropriate isolation levels
- Monitor for deadlocks

## Safe Data Modification

### Test Queries Before Execution

Always test data modification queries before running them on production data.

**Safe Testing Workflow:**

1. **Select Before Update/Delete:**
```
-- First, SELECT to see what will be affected
SELECT * FROM users WHERE last_login < '2020-01-01';

-- Review the results, then execute the update
-- UPDATE users SET active = false WHERE last_login < '2020-01-01';
```

2. **Use Transactions for Testing:**
```
BEGIN;

UPDATE products SET price = price * 1.10 WHERE category = 'electronics';

-- Review the changes
SELECT id, name, price FROM products WHERE category = 'electronics';

-- If correct: COMMIT; otherwise: ROLLBACK;
ROLLBACK;
```

3. **Test on Subset First:**
```
-- Test on small subset
UPDATE orders
SET status = 'archived'
WHERE order_date < '2020-01-01'
LIMIT 10;

-- If successful, run on full dataset
-- UPDATE orders SET status = 'archived' WHERE order_date < '2020-01-01';
```

### Use WHERE Clauses Carefully

Missing or incorrect WHERE clauses cause some of the most devastating data loss incidents.

**Dangerous Patterns:**
```
-- DANGER: Missing WHERE clause updates all rows
UPDATE users SET role = 'admin';

-- DANGER: Incorrect logic updates wrong rows
UPDATE products SET discontinued = true WHERE active = true;
-- (Should be: WHERE active = false)
```

**Safety Measures:**
- Always write WHERE clause first
- Use SELECT to verify WHERE logic
- Double-check column names and values
- Use transactions for reversibility
- Limit rows affected during testing

### Implement Row-Level Verification

For critical updates, verify each affected row.

**Verification Query Pattern:**
```
-- Create temporary backup table
CREATE TABLE orders_backup AS
SELECT * FROM orders WHERE status = 'pending';

-- Perform update
UPDATE orders
SET status = 'processing', updated_at = CURRENT_TIMESTAMP
WHERE status = 'pending';

-- Verify changes
SELECT
  b.id,
  b.status as old_status,
  o.status as new_status
FROM orders_backup b
JOIN orders o ON b.id = o.id
WHERE b.status != o.status;

-- If incorrect, rollback using backup table
-- If correct, drop backup table
DROP TABLE orders_backup;
```

## Bulk Operations

### Planning Bulk Operations

Bulk operations require careful planning to avoid impacting system performance.

**Pre-Operation Checklist:**
- [ ] Backup created and verified
- [ ] Operation tested on subset
- [ ] Maintenance window scheduled
- [ ] Rollback plan documented
- [ ] Monitoring in place
- [ ] Stakeholders notified
- [ ] Resource requirements assessed

### Batch Processing

Process large datasets in batches to avoid locking tables and consuming excessive resources.

**Batch Update Pattern:**
```
-- Process in batches of 1000 rows
DO $$
DECLARE
  batch_size INTEGER := 1000;
  processed INTEGER := 0;
  total INTEGER;
BEGIN
  SELECT COUNT(*) INTO total FROM users WHERE active = false;

  WHILE processed < total LOOP
    UPDATE users
    SET archived = true
    WHERE id IN (
      SELECT id FROM users
      WHERE active = false AND archived = false
      LIMIT batch_size
    );

    processed := processed + batch_size;

    -- Short delay to reduce system load
    PERFORM pg_sleep(0.1);

    RAISE NOTICE 'Processed % of % rows', processed, total;
  END LOOP;
END $$;
```

**Benefits of Batch Processing:**
- Reduces lock contention
- Allows concurrent operations
- Easier to monitor progress
- Can be paused and resumed
- Lower memory usage

### Handling Large Deletes

Large delete operations can cause performance issues and transaction log growth.

**Incremental Delete Strategy:**
```
-- Delete in chunks
DELETE FROM logs
WHERE id IN (
  SELECT id FROM logs
  WHERE created_at < '2020-01-01'
  LIMIT 10000
);

-- Repeat until done
-- Monitor table size reduction: SELECT COUNT(*) FROM logs;
```

**Truncate for Full Table Deletion:**
```
-- Much faster than DELETE for removing all rows
TRUNCATE TABLE staging_data;

-- Truncate with cascade for related tables
TRUNCATE TABLE orders CASCADE;
```

## Data Validation

### Input Validation

Validate data before insertion or update to maintain data quality.

**Validation Checks:**

Data Type Validation:
```
-- Ensure numeric values are within range
SELECT * FROM products
WHERE price < 0 OR price > 1000000;

-- Check date validity
SELECT * FROM events
WHERE event_date > CURRENT_DATE + INTERVAL '10 years';
```

Format Validation:
```
-- Validate email format
SELECT * FROM users
WHERE email !~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}$';

-- Validate phone format
SELECT * FROM contacts
WHERE phone !~ '^\+?[0-9]{10,15}$';
```

Business Rule Validation:
```
-- Check inventory consistency
SELECT product_id, SUM(quantity) as total
FROM inventory_movements
GROUP BY product_id
HAVING total < 0;

-- Verify referential integrity
SELECT o.id
FROM orders o
LEFT JOIN customers c ON o.customer_id = c.id
WHERE c.id IS NULL;
```

### Constraint Management

Use database constraints to enforce data integrity automatically.

**Essential Constraints:**

Primary Keys:
```
ALTER TABLE users ADD PRIMARY KEY (id);
```

Foreign Keys:
```
ALTER TABLE orders
ADD CONSTRAINT fk_customer
FOREIGN KEY (customer_id)
REFERENCES customers(id)
ON DELETE RESTRICT;
```

Unique Constraints:
```
ALTER TABLE users ADD CONSTRAINT unique_email UNIQUE (email);
```

Check Constraints:
```
ALTER TABLE products
ADD CONSTRAINT check_price
CHECK (price >= 0);

ALTER TABLE orders
ADD CONSTRAINT check_status
CHECK (status IN ('pending', 'processing', 'shipped', 'delivered', 'cancelled'));
```

Not Null Constraints:
```
ALTER TABLE users ALTER COLUMN email SET NOT NULL;
ALTER TABLE orders ALTER COLUMN order_date SET NOT NULL;
```

### Data Quality Monitoring

Continuously monitor data quality to detect issues early.

**Quality Metrics:**
- Null value percentages
- Duplicate record counts
- Constraint violation attempts
- Data distribution anomalies
- Referential integrity breaks

**Quality Monitoring Queries:**
```
-- Check for duplicate emails
SELECT email, COUNT(*)
FROM users
GROUP BY email
HAVING COUNT(*) > 1;

-- Find orphaned records
SELECT COUNT(*)
FROM order_items oi
LEFT JOIN orders o ON oi.order_id = o.id
WHERE o.id IS NULL;

-- Identify null critical fields
SELECT COUNT(*) as null_emails
FROM users
WHERE email IS NULL;

-- Check data freshness
SELECT
  MAX(updated_at) as last_update,
  EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - MAX(updated_at)))/3600 as hours_since_update
FROM products;
```

## Data Migration

### Planning Migrations

Data migrations require thorough planning and testing.

**Migration Planning Checklist:**
- [ ] Source and target schemas documented
- [ ] Data transformation logic defined
- [ ] Data volume and duration estimated
- [ ] Dependencies identified
- [ ] Testing strategy created
- [ ] Rollback procedure documented
- [ ] Validation queries prepared

### Migration Testing

Test migrations in non-production environment before running in production.

**Testing Phases:**

1. **Unit Testing:**
   - Test individual transformation functions
   - Verify edge cases
   - Validate error handling

2. **Integration Testing:**
   - Test complete migration process
   - Verify referential integrity
   - Check constraint compliance

3. **Performance Testing:**
   - Measure migration duration
   - Assess system impact
   - Optimize batch sizes

4. **Data Validation:**
   - Compare row counts
   - Verify data accuracy
   - Check completeness

**Validation Query Examples:**
```
-- Verify row counts match
SELECT
  (SELECT COUNT(*) FROM source_table) as source_count,
  (SELECT COUNT(*) FROM target_table) as target_count;

-- Check for missing records
SELECT s.id
FROM source_table s
LEFT JOIN target_table t ON s.id = t.id
WHERE t.id IS NULL;

-- Verify data accuracy (sample)
SELECT
  s.id,
  s.value as source_value,
  t.value as target_value
FROM source_table s
JOIN target_table t ON s.id = t.id
WHERE s.value != t.value
LIMIT 100;
```

### Rollback Procedures

Every migration needs a documented rollback procedure.

**Rollback Strategy:**
1. Keep original data until migration validated
2. Document reverse transformation logic
3. Test rollback procedure
4. Define rollback decision criteria
5. Assign rollback authority

**Example Rollback Process:**
```
-- Step 1: Stop application writes to new table
-- Step 2: Restore from backup
DROP TABLE IF EXISTS new_users;
CREATE TABLE new_users AS SELECT * FROM users_backup;

-- Step 3: Verify restoration
SELECT COUNT(*) FROM new_users;

-- Step 4: Rename tables
BEGIN;
ALTER TABLE users RENAME TO users_failed_migration;
ALTER TABLE new_users RENAME TO users;
COMMIT;

-- Step 5: Resume application
```

## Data Archival

### Archival Strategy

Archive old data to maintain system performance while preserving historical information.

**When to Archive:**
- Data no longer actively used
- Regulatory retention requirements met
- Table size impacting performance
- Historical reference needed

**Archival Approaches:**

Separate Archive Tables:
```
-- Create archive table
CREATE TABLE orders_archive (LIKE orders INCLUDING ALL);

-- Move old data
INSERT INTO orders_archive
SELECT * FROM orders
WHERE order_date < '2020-01-01';

-- Verify and delete
DELETE FROM orders
WHERE order_date < '2020-01-01'
  AND id IN (SELECT id FROM orders_archive);
```

Partitioning:
```
-- PostgreSQL table partitioning
CREATE TABLE orders (
  id SERIAL,
  order_date DATE NOT NULL,
  -- other columns
) PARTITION BY RANGE (order_date);

CREATE TABLE orders_2023 PARTITION OF orders
  FOR VALUES FROM ('2023-01-01') TO ('2024-01-01');

CREATE TABLE orders_2024 PARTITION OF orders
  FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');

-- Drop old partitions when archiving
DROP TABLE orders_2020;
```

### Archive Storage

Choose appropriate storage for archived data.

**Storage Options:**
- Separate database for archives
- Compressed backup files
- Cloud object storage (S3, Azure Blob)
- Tape backup for long-term storage

**Archive Access:**
- Read-only access when needed
- Separate connection credentials
- Lower priority query execution
- Documented retrieval process

## Data Safety Checklist

Before executing any data modification operation:

**Pre-Operation:**
- [ ] Backup created and verified
- [ ] Query tested with SELECT
- [ ] WHERE clause verified
- [ ] Transaction started (if appropriate)
- [ ] Row count estimated
- [ ] Operation documented

**During Operation:**
- [ ] Progress monitored
- [ ] Performance impact assessed
- [ ] Errors logged
- [ ] Can pause if needed

**Post-Operation:**
- [ ] Changes verified
- [ ] Transaction committed
- [ ] Documentation updated
- [ ] Stakeholders notified
- [ ] Backup retained until validated

**Rollback Ready:**
- [ ] Rollback procedure documented
- [ ] Rollback tested (if critical)
- [ ] Rollback authority designated
- [ ] Decision criteria defined

## Summary

Safe data management requires discipline, planning, and robust procedures. Always backup before changes, test operations thoroughly, use transactions appropriately, and validate results carefully. By following these best practices, you can confidently manage data using WhoDB while minimizing risk of data loss or corruption. Remember that the time invested in proper planning and testing is always less than the time required to recover from data disasters.

---


# best-practices/query-optimization.mdx
## SQL Query Optimization

Master SQL query optimization techniques to improve database performance and reduce execution time

# SQL Query Optimization

Query optimization is fundamental to database performance. A well-optimized query can execute thousands of times faster than an inefficient one. This comprehensive guide covers practical techniques, real-world examples, and common pitfalls to avoid.

Use the EXPLAIN command to analyze query execution plans before optimizing. Understanding how your database executes queries is the first step to optimization.

## Understanding Query Execution Plans

### Using EXPLAIN

The EXPLAIN command shows how your database will execute a query. This is your most powerful diagnostic tool.

**Basic EXPLAIN Usage:**

```
EXPLAIN SELECT * FROM users WHERE user_id = 42;
```

**PostgreSQL EXPLAIN with Analysis:**

```
EXPLAIN ANALYZE SELECT * FROM orders
WHERE created_at > '2024-01-01'
ORDER BY total DESC;
```

**Interpreting Output:**

Look for these performance indicators:
- Seq Scan: Full table scan (slow for large tables)
- Index Scan: Using an index (usually fast)
- Filter: Rows being eliminated during scan
- Sort: Sorting operation (can be expensive)
- Hash Join: Hash-based join (efficient)
- Nested Loop: Loop-based join (slower with large datasets)

A Seq Scan on a large table without a WHERE clause is almost always a problem. Add appropriate indexes or improve query selectivity.

## Indexing Strategies

### Creating Effective Indexes

**Single Column Index (Most Common):**

```
CREATE INDEX idx_users_email ON users(email);
```

**Composite Index (Multiple Columns):**

```
CREATE INDEX idx_orders_customer_date
ON orders(customer_id, created_at DESC);
```

**Unique Index:**

```
CREATE UNIQUE INDEX idx_users_username
ON users(username);
```

**Partial Index (Index subset of data):**

```
CREATE INDEX idx_active_orders
ON orders(customer_id)
WHERE status = 'active';
```

Composite indexes should order columns: equality conditions first, then range conditions, then sort conditions. This maximizes index effectiveness.

### Index Column Order Matters

```
-- GOOD: customer_id (equality), created_at (range)
CREATE INDEX idx_orders_cust_date
ON orders(customer_id, created_at);

-- Will use index for queries like:
SELECT * FROM orders
WHERE customer_id = 123
AND created_at > '2024-01-01';

-- SLOW: Wrong order wastes index potential
CREATE INDEX idx_orders_date_cust
ON orders(created_at, customer_id);
```

## Query Pattern Optimization

### Pattern 1: Avoid SELECT *

**Inefficient:**

```
SELECT * FROM users
WHERE status = 'active'
LIMIT 10;
```

**Optimized:**

```
SELECT user_id, email, name, status
FROM users
WHERE status = 'active'
LIMIT 10;
```

The optimized version reduces data transfer and allows index-only scans on the selected columns.

### Pattern 2: Use WHERE Before HAVING

**Inefficient:**

```
SELECT customer_id, COUNT(*) as order_count
FROM orders
GROUP BY customer_id
HAVING COUNT(*) > 5;
```

**Optimized:**

```
SELECT customer_id, COUNT(*) as order_count
FROM orders
WHERE created_at > '2024-01-01'
GROUP BY customer_id
HAVING COUNT(*) > 5;
```

Filter rows before grouping to reduce the dataset processed by the aggregation.

### Pattern 3: Use IN for Multiple Values

**Inefficient:**

```
SELECT * FROM users
WHERE status = 'active'
OR status = 'pending'
OR status = 'review';
```

**Optimized:**

```
SELECT * FROM users
WHERE status IN ('active', 'pending', 'review');
```

The IN operator is more efficient and often uses better execution plans.

### Pattern 4: BETWEEN for Range Queries

**Inefficient:**

```
SELECT * FROM transactions
WHERE amount >= 100
AND amount <= 500;
```

**Optimized:**

```
SELECT * FROM transactions
WHERE amount BETWEEN 100 AND 500;
```

BETWEEN often generates better index utilization for range queries.

### Pattern 5: Use UNION Instead of OR for Complex Conditions

**Potentially Inefficient:**

```
SELECT * FROM orders
WHERE customer_id = 123
OR product_id = 456
OR status = 'high-priority';
```

**Potentially Faster:**

```
SELECT * FROM orders WHERE customer_id = 123
UNION
SELECT * FROM orders WHERE product_id = 456
UNION
SELECT * FROM orders WHERE status = 'high-priority';
```

UNION allows each part to use different indexes. Use UNION ALL if duplicates are acceptable (faster).

## Join Optimization

### Pattern 6: Join with Indexed Foreign Keys

**Inefficient (No Index):**

```
SELECT o.order_id, c.customer_name, o.total
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE o.created_at > '2024-01-01';
```

**Optimized (With Index):**

```
CREATE INDEX idx_orders_customer_id ON orders(customer_id);
CREATE INDEX idx_customers_id ON customers(customer_id);

SELECT o.order_id, c.customer_name, o.total
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE o.created_at > '2024-01-01';
```

Always index foreign key columns used in joins.

### Pattern 7: Join Order Matters

**Inefficient Order:**

```
SELECT o.order_id, c.customer_name, p.product_name
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
JOIN products p ON o.product_id = p.product_id
WHERE c.status = 'vip';
```

**Optimized Order:**

```
SELECT o.order_id, c.customer_name, p.product_name
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
JOIN products p ON o.product_id = p.product_id
WHERE c.status = 'vip';
```

Start with the most filtered table first. This reduces the number of rows in subsequent joins.

### Pattern 8: LEFT JOIN with NULL Filter

**Inefficient (Still uses LEFT JOIN):**

```
SELECT u.user_id, u.email, l.login_count
FROM users u
LEFT JOIN login_logs l ON u.user_id = l.user_id
WHERE l.login_id IS NOT NULL;
```

**Optimized (Switch to INNER JOIN):**

```
SELECT u.user_id, u.email, l.login_count
FROM users u
INNER JOIN login_logs l ON u.user_id = l.user_id;
```

If you're filtering out NULL values, use INNER JOIN instead.

## Aggregation Optimization

### Pattern 9: Aggregate with GROUP BY Efficiently

**Inefficient:**

```
SELECT customer_id, COUNT(*) as order_count
FROM orders
GROUP BY customer_id;
```

**Optimized (Add Index):**

```
CREATE INDEX idx_orders_customer_id ON orders(customer_id);

SELECT customer_id, COUNT(*) as order_count
FROM orders
GROUP BY customer_id;
```

### Pattern 10: Subquery Optimization with Common Table Expressions

**Inefficient (Correlated Subquery):**

```
SELECT u.user_id, u.email,
  (SELECT COUNT(*) FROM orders o WHERE o.customer_id = u.user_id) as order_count
FROM users u
WHERE (SELECT COUNT(*) FROM orders o WHERE o.customer_id = u.user_id) > 3;
```

**Optimized (CTE):**

```
WITH user_orders AS (
  SELECT customer_id, COUNT(*) as order_count
  FROM orders
  GROUP BY customer_id
)
SELECT u.user_id, u.email, uo.order_count
FROM users u
JOIN user_orders uo ON u.user_id = uo.customer_id
WHERE uo.order_count > 3;
```

CTEs make the query clearer and prevent repetitive subquery execution.

## Avoiding Common Performance Mistakes

### Mistake 1: Functions in WHERE Clauses

**Slow (Cannot use index):**

```
SELECT * FROM users
WHERE UPPER(email) = 'USER@EXAMPLE.COM';
```

**Fast (Can use index):**

```
SELECT * FROM users
WHERE email = 'user@example.com';
```

Functions on indexed columns prevent index usage. Process data application-side when possible.

### Mistake 2: Implicit Type Conversion

**Slow (String compared to number):**

```
SELECT * FROM users
WHERE user_id = '123';
```

**Fast (Proper type matching):**

```
SELECT * FROM users
WHERE user_id = 123;
```

Type mismatches force conversions that bypass indexes.

### Mistake 3: LIKE with Leading Wildcard

**Very Slow (No index use):**

```
SELECT * FROM products
WHERE product_name LIKE '%laptop%';
```

**Faster (Prefix search):**

```
SELECT * FROM products
WHERE product_name LIKE 'laptop%';
```

**Fastest (Exact/Index search):**

```
SELECT * FROM products
WHERE product_name = 'laptop';
```

Leading wildcards prevent index usage. Consider full-text search for text matching.

### Mistake 4: NOT IN with NULL Values

**Problematic:**

```
SELECT * FROM orders
WHERE customer_id NOT IN (
  SELECT customer_id FROM vip_customers WHERE vip_customers.status IS NULL
);
```

**Fixed:**

```
SELECT * FROM orders
WHERE customer_id NOT IN (
  SELECT customer_id FROM vip_customers WHERE status IS NOT NULL
);
```

NOT IN returns NULL if any subquery value is NULL, causing the entire condition to be NULL.

### Mistake 5: Unnecessary DISTINCT

**Inefficient (Extra processing):**

```
SELECT DISTINCT customer_id
FROM orders
WHERE status = 'completed';
```

**Optimized (If duplicates aren't possible):**

```
SELECT customer_id
FROM orders
WHERE status = 'completed';
```

Only use DISTINCT when necessary. It requires sorting or hashing.

## Advanced Optimization Techniques

Cache frequently accessed data:

```
-- Create a summary table for reporting
CREATE TABLE daily_sales_summary AS
SELECT DATE(created_at) as sale_date, SUM(total) as daily_total
FROM orders
WHERE created_at > CURRENT_DATE - INTERVAL '30 days'
GROUP BY DATE(created_at);

-- Now query the summary instead of raw data
SELECT * FROM daily_sales_summary WHERE sale_date > '2024-01-01';
```

Materialized views or summary tables reduce computation for expensive queries.

For very large tables, partitioning improves performance:

```
-- PostgreSQL: Partition by date range
CREATE TABLE orders_partitioned (
  order_id SERIAL,
  customer_id INT,
  total DECIMAL,
  created_at TIMESTAMP
) PARTITION BY RANGE (YEAR(created_at));

CREATE TABLE orders_2024 PARTITION OF orders_partitioned
  FOR VALUES FROM (2024) TO (2025);
```

Partitioning allows faster queries by limiting data scans to relevant partitions.

In read-heavy scenarios, denormalization can improve performance:

```
-- Instead of joining every time
SELECT o.order_id, c.customer_name, o.total
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id;

-- Store customer_name in orders table
ALTER TABLE orders ADD COLUMN customer_name VARCHAR(255);

-- Update on customer insert/change (handled by trigger)
SELECT o.order_id, o.customer_name, o.total
FROM orders o;
```

Trade storage and update complexity for faster reads.

Reuse database connections:

```
Connection Pool Settings:
- Min connections: 5
- Max connections: 20
- Connection timeout: 30 seconds
- Idle timeout: 300 seconds
```

Connection pooling reduces overhead for repeated queries.

## Performance Testing Workflow

Run the current query and note execution time and resource usage.

```
EXPLAIN ANALYZE SELECT * FROM orders WHERE status = 'pending';
```

Look at the EXPLAIN output for Seq Scan, high costs, or poor index usage.

"Adding an index on status column will improve performance"

```
CREATE INDEX idx_orders_status ON orders(status);
```

```
EXPLAIN ANALYZE SELECT * FROM orders WHERE status = 'pending';
```

Compare to baseline. Measure execution time, plan cost, and rows examined.

Keep records of what worked and what didn't for future reference.

## Optimization Checklist

- [ ] Run EXPLAIN ANALYZE on the slow query
- [ ] Confirm the query is actually slow in production
- [ ] Check current indexes on involved tables
- [ ] Review table row counts and data distribution
- [ ] Have a backup of the database

- [ ] Add missing indexes on WHERE, JOIN, and ORDER BY columns
- [ ] Remove functions from WHERE clauses
- [ ] Replace OR conditions with IN
- [ ] Move filter conditions to WHERE before HAVING
- [ ] Use UNION for complex OR conditions
- [ ] Check join order
- [ ] Replace LEFT JOIN with INNER JOIN when applicable
- [ ] Use CTEs for complex subqueries
- [ ] Reduce SELECT columns to only needed ones
- [ ] Add LIMIT to prevent full table scans

- [ ] Re-run EXPLAIN ANALYZE to verify improvement
- [ ] Test query on production data volume
- [ ] Monitor query performance in production
- [ ] Check index size and storage impact
- [ ] Document the optimization for team reference
- [ ] Clean up abandoned indexes

## Related Topics

Overall database performance techniques

PostgreSQL-specific optimization tips

MySQL-specific optimization tips

Learn query writing in WhoDB

## Summary

SQL query optimization combines art and science. Use EXPLAIN to understand execution plans, create strategic indexes, avoid common pitfalls, and test changes methodically. Even small optimizations compound when queries run thousands of times daily. Start with the highest-impact changes and work systematically through the optimization checklist.

You now have a comprehensive understanding of SQL query optimization techniques and patterns. Apply these principles to transform slow queries into fast, efficient ones.

---


# best-practices/ai-usage.mdx
## AI Chat Assistant Best Practices

Comprehensive guidance for optimal and safe use of WhoDB's AI Chat Assistant

# AI Chat Assistant Best Practices

The AI Chat Assistant transforms database interaction from technical SQL writing to natural conversation. This guide provides comprehensive best practices for using the AI assistant effectively, safely, and efficiently in production environments.

Effective AI assistant usage combines clear communication, security awareness, and strategic provider selection

## Understanding AI-Powered Database Interaction

The AI Chat Assistant is fundamentally different from traditional database tools. Rather than writing SQL directly, you describe what you want in natural language, and the AI generates appropriate queries based on your database schema.

### How AI Assistants Work

The AI assistant analyzes your complete database schema, including tables, columns, data types, and relationships.

Your question is processed to understand intent, entities, conditions, and desired operations.

Based on schema and intent, the AI generates database-specific SQL optimized for your database type.

WhoDB executes the query and presents results, which become part of conversation context.

### Key Differences from Traditional SQL

| Aspect | Traditional SQL | AI Assistant |
|--------|----------------|-------------|
| **Input Method** | Write exact syntax | Describe desired outcome |
| **Schema Knowledge** | Must memorize or reference | Automatically aware |
| **Error Handling** | Syntax errors require fixes | Rephrase in natural language |
| **Learning Curve** | Steep for beginners | Accessible immediately |
| **Precision** | Exact control | Interpretation required |
| **Speed** | Fast for experts | Fast for everyone |

## Query Formulation Best Practices

Effective communication with the AI assistant follows specific patterns that produce accurate, efficient results.

### Be Specific and Explicit

Vague questions produce unreliable results. Specificity ensures the AI understands your exact intent.

**Good Examples:**
```
Show me all records from the users table
Count orders in the orders table
Display products from the inventory.products table
```

**Avoid:**
```
Show me the data
Get everything
Display records
```

When table names might be ambiguous, include schema names: `test_schema.users`

**Good Examples:**
```
Show user_id, email, and created_at from users
Display product names and prices
Get order totals and statuses
```

**Avoid:**
```
Show some user information
Display product details
Get order stuff
```

Explicit column names help the AI generate precise SELECT statements.

**Good Examples:**
```
Show orders from the last 7 days
Display users created after 2024-01-01
Find logs between 2024-01-01 and 2024-01-15
```

**Avoid:**
```
Show recent orders
Display new users
Find old logs
```

Use specific dates or clear relative ranges (last 7 days, this month, last year).

**Good Examples:**
```
Show users where status is active and email_verified is true
Find products where price is greater than 100 and stock is less than 10
Display orders where total exceeds 500 and shipping_country is USA
```

**Avoid:**
```
Show active users
Find expensive products
Display big orders
```

Explicitly state field names, comparison operators, and values.

### Provide Context

Context helps the AI understand your intent and generate more accurate queries.

**Include Business Context:**
```
Show revenue by product category for the last quarter (for quarterly report)
Find users who haven't logged in for 90 days (for cleanup campaign)
Display top 10 customers by total order value (for rewards program)
```

**Mention Expected Results:**
```
Show all orders (expecting about 1000 records)
Count active subscriptions (should be around 500)
Display failed payment attempts today (usually less than 50)
```

Expected results help you quickly identify when queries return unexpected data.

### Use Proper Database Terminology

Use terminology appropriate to your database type.

**Correct Terms:**
- Tables (not collections)
- Rows (not documents)
- Columns (not fields)
- JOIN operations
- WHERE clauses
- Indexes

**Example:**
```
Join the orders table with customers table on customer_id and show customer names with their order totals
```

**MongoDB Terms:**
- Collections (not tables)
- Documents (not rows)
- Fields (not columns)
- Aggregation pipelines
- Match stages

**Example:**
```
Aggregate users collection grouped by email domain with count
```

**Redis Terms:**
- Keys
- Values
- Sets
- Hashes
- Sorted sets

**Example:**
```
Get all keys matching pattern user:*
```

### Start Simple, Then Refine

Build complex queries through iterative refinement rather than trying to get everything perfect in one question.

```
Show me all orders
```

Review the structure and available data.

```
Just orders from the last 30 days
```

The AI understands you're refining the previous query.

```
Group those by customer
```

Continues building on previous context.

```
Show total order value for each customer
```

Further refines the analysis.

```
Sort by total value descending
```

Final refinement to see top customers.

This iterative approach is faster and more reliable than trying to construct complex queries in a single request.

## Safety and Security Best Practices

Using AI assistants safely requires understanding what data is shared, potential risks, and protective measures.

### Understand Data Sharing

Different AI providers have different data handling policies.

Your database schema structure and query text are sent to AI providers. However, actual data values and query results are not transmitted.

**What Gets Sent to AI Providers:**
- Your natural language questions
- Database table names and schemas
- Column names and data types
- Database type (PostgreSQL, MySQL, etc.)
- Previous conversation context

**What Does NOT Get Sent:**
- Actual row data from your database
- Query result contents
- Stored data values
- Connection credentials

**For Maximum Privacy:**
- Use Ollama (local models) for complete data isolation
- Avoid mentioning sensitive values in questions
- Use generic terms instead of revealing schema names

### Verify Before Modifying Data

Always review and verify before confirming data modification operations.

Before clicking Confirm on any INSERT, UPDATE, or DELETE operation:

**Checklist:**
- [ ] Correct table is targeted
- [ ] WHERE clause is present and accurate
- [ ] Values are correct and properly formatted
- [ ] Estimated row count matches expectations
- [ ] No unintended side effects

**Example Review:**
```
DELETE FROM users WHERE last_login < '2020-01-01'
```

Ask yourself:
- Is the date correct?
- Does this match my intent?
- How many rows will this affect?
- Do I have a backup?

Always verify which records will be affected before modifying them.

**Two-Step Pattern:**

**Step 1 - Verify:**
```
Show me all users where last_login is before 2020-01-01
```

Review the results carefully. Count the records. Verify these are the records you want to modify.

**Step 2 - Modify:**
```
Delete all users where last_login is before 2020-01-01
```

This two-step approach prevents accidental data loss.

For critical operations, test the query in a development environment before running in production.

**Development Testing Workflow:**
1. Connect to development database
2. Ask the AI assistant to generate the query
3. Review the generated SQL
4. Execute and verify results
5. Copy the verified SQL to production (via Scratchpad)
6. Execute in production during appropriate window

This ensures the AI generates correct SQL for your specific schema before affecting production data.

### Use Read-Only Users When Possible

For data exploration and analysis tasks, connect with read-only database credentials.

**PostgreSQL Read-Only User:**
```
CREATE ROLE readonly_user WITH LOGIN PASSWORD 'secure_password';
GRANT CONNECT ON DATABASE mydb TO readonly_user;
GRANT USAGE ON SCHEMA public TO readonly_user;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT SELECT ON TABLES TO readonly_user;
```

**MySQL Read-Only User:**
```
CREATE USER 'readonly_user'@'%' IDENTIFIED BY 'secure_password';
GRANT SELECT ON mydb.* TO 'readonly_user'@'%';
FLUSH PRIVILEGES;
```

**Benefits:**
- Prevents accidental data modification
- Safe for exploration and learning
- Appropriate for analyst and reporting use cases
- Eliminates need for confirmation dialogs

Use read-only credentials for 80% of your database work. Only use write credentials when actually modifying data.

### Backup Before Bulk Operations

Before executing bulk modifications, ensure current backups exist.

**Pre-Operation Backup Checklist:**
- [ ] Recent full backup exists (within 24 hours)
- [ ] Backup has been tested and can be restored
- [ ] Backup includes all affected tables
- [ ] Restoration procedure is documented
- [ ] Backup location is accessible

**Quick Backup Commands:**

PostgreSQL:
```
pg_dump -h localhost -U username -d database -t table_name > backup_$(date +%Y%m%d_%H%M%S).sql
```

MySQL:
```
mysqldump -h localhost -u username -p database table_name > backup_$(date +%Y%m%d_%H%M%S).sql
```

### Review Generated SQL

The AI assistant shows generated SQL before execution. Use this visibility to verify correctness.

**SQL Review Checklist:**

**For SELECT Queries:**
- [ ] Correct tables referenced
- [ ] Appropriate JOIN conditions
- [ ] WHERE filters match intent
- [ ] Column selection is complete
- [ ] No expensive operations (LIKE, complex functions) on large tables

**For UPDATE Queries:**
- [ ] WHERE clause is present and correct
- [ ] SET values are appropriate
- [ ] No syntax errors
- [ ] Estimated affected rows is reasonable

**For DELETE Queries:**
- [ ] WHERE clause is present (unless intentionally deleting all)
- [ ] Correct table targeted
- [ ] Deletion won't violate foreign key constraints
- [ ] Backup exists

**For INSERT Queries:**
- [ ] All required columns included
- [ ] Values match column data types
- [ ] No duplicate key violations expected

## Provider Selection Strategy

Choosing the right AI provider for each situation optimizes cost, performance, privacy, and accuracy.

### When to Use Each Provider

**Best For:**
- General-purpose queries across all database types
- Fast response requirements
- Users without local AI infrastructure
- Production environments with moderate query volume

**Optimal Scenarios:**
- Quick data exploration
- Ad-hoc reporting
- Team members learning SQL
- Standard CRUD operations

**Cost Optimization:**
- Use GPT-3.5 Turbo for simple queries (lower cost)
- Reserve GPT-4 for complex multi-table operations
- Start new conversations when switching topics (reduces context cost)

**Example Cost Analysis:**
- Simple query (10 queries/day): ~$3-5/month
- Moderate usage (50 queries/day): ~$15-25/month
- Heavy usage (200 queries/day): ~$60-100/month

**Best For:**
- Complex analytical queries
- Large database schemas (100+ tables)
- Long conversation contexts
- Sophisticated reasoning requirements

**Optimal Scenarios:**
- Multi-step analysis building on previous results
- Complex JOIN operations across many tables
- Ambiguous questions requiring clarification
- Databases with intricate relationships

**Model Selection:**
- **Claude 3.5 Sonnet**: Best balance for most use cases
- **Claude 3 Opus**: Maximum capability for most complex scenarios
- **Claude 3 Haiku**: Cost-effective for simpler queries

**When Claude Excels:**
- Queries involving 5+ table joins
- Conversations with 30+ messages
- Complex aggregations with multiple grouping levels
- Schemas with hundreds of tables

**Best For:**
- Privacy-sensitive environments
- Regulated industries (healthcare, finance)
- Air-gapped or offline deployments
- Unlimited usage without API costs
- Development and learning

**Optimal Scenarios:**
- Sensitive data that cannot leave your infrastructure
- High-volume query workloads
- Cost-sensitive operations
- Complete data sovereignty requirements

**Model Recommendations:**
- **Llama 3.1 (8B)**: Best general-purpose local model
- **CodeLlama**: Optimized for SQL generation
- **Mistral (7B)**: Fast responses, good for simple queries
- **Llama 3.1 (70B)**: Maximum local accuracy (requires 64GB RAM)

**Trade-offs:**
- Slower than cloud providers (2-10 seconds vs. 1-2 seconds)
- Lower accuracy than GPT-4 or Claude Opus
- Requires local hardware investment
- Complete privacy and zero ongoing costs

### Cost Optimization Strategies

GPT-3.5 Turbo costs 10x less than GPT-4 while handling simple queries equally well

Long conversations consume more tokens. Start fresh when switching topics

Shorter questions and responses reduce token usage and costs

Ollama eliminates per-query costs for high-volume usage

**Cost Comparison Example:**

Query load: 50 queries per day

| Provider | Model | Monthly Cost | Response Time | Privacy |
|----------|-------|-------------|---------------|---------|
| OpenAI | GPT-3.5 Turbo | $10-15 | 1-2 sec | External |
| OpenAI | GPT-4 | $60-100 | 3-5 sec | External |
| Anthropic | Claude 3.5 Sonnet | $30-45 | 2-4 sec | External |
| Ollama | Llama 3.1 (8B) | $0 | 5-10 sec | Complete |

### Privacy Considerations

Choose providers based on data sensitivity and organizational policies.

**Highly Sensitive:**
- Personal health information (PHI)
- Financial records
- Trade secrets
- Personally identifiable information (PII)

**Action:** Use Ollama exclusively

**Moderately Sensitive:**
- Internal business data
- Customer information (non-PII)
- Analytics data

**Action:** Review provider terms of service, consider Ollama or ensure provider compliance

**Public or Non-Sensitive:**
- Public datasets
- Demonstration databases
- Educational use cases

**Action:** Any provider acceptable

Consult with:
- Information security team
- Data governance team
- Legal/compliance department
- Privacy officer

Verify alignment with:
- Data residency requirements
- Third-party data processing policies
- Regulatory compliance (GDPR, HIPAA, PCI DSS)
- Industry-specific requirements

**For External Providers:**
- Document approved use cases
- Train users on privacy boundaries
- Monitor for policy violations
- Establish escalation procedures

**For Local Models:**
- Document installation and configuration
- Maintain model versions
- Monitor resource usage
- Plan capacity for user growth

Schema names and table names are sent to external AI providers. Avoid using sensitive or revealing names if privacy is critical.

## Performance Optimization

Optimize AI assistant performance through efficient query patterns and conversation management.

### Efficient Query Patterns

Request aggregated data rather than retrieving all rows.

**Efficient:**
```
Count users by email domain
Show average order value by month
Get total revenue by product category
```

**Inefficient:**
```
Show me all users (then manually count by domain)
Display all orders (then calculate average)
Get all products with their sales (then sum revenue)
```

Let the database perform aggregations rather than retrieving large datasets.

Request only necessary data for large tables.

**Efficient:**
```
Show top 100 users by registration date
Display 50 most recent orders
Get 25 highest-value products
```

**Inefficient:**
```
Show me all users (millions of rows)
Display all orders from all time
Get every product in inventory
```

Use LIMIT clauses for exploratory queries on large tables.

Structure queries to leverage existing indexes.

**Index-Friendly:**
```
Find users where user_id equals 12345
Show orders where order_date is 2024-01-15
Get products where sku is ABC-123
```

**Index-Inefficient:**
```
Find users where email contains gmail
Show orders where YEAR(order_date) equals 2024
Get products where LOWER(name) like '%phone%'
```

Ask about indexed columns: "What indexes exist on the orders table?"

Ensure JOINs have proper conditions.

**Efficient:**
```
Join orders with customers on customer_id and show customer names with order totals
```

**Inefficient:**
```
Show all combinations of orders and customers
```

The AI generally avoids this, but verify JOIN conditions in the generated SQL.

### Managing Conversation Context

Long conversations accumulate context that slows response times and increases costs.

**Optimal Conversation Length:**
- **Short Conversations (1-10 messages)**: Fastest, lowest cost
- **Medium Conversations (10-30 messages)**: Still efficient
- **Long Conversations (30-50 messages)**: Noticeable slowdown
- **Very Long Conversations (50+ messages)**: Significantly slower, higher cost

**When to Start New Conversations:**

When switching to a completely different database area or analysis focus, start fresh.

**Example:**
- Old conversation: Analyzing user signups
- New topic: Investigating order fulfillment issues

**Action:** Click "New Chat" button

When queries take noticeably longer to respond (>5 seconds for simple questions).

**Symptom:** Loading indicators persist longer than usual

**Action:** Save important queries to Scratchpad, then start new chat

When the AI references incorrect previous context or misunderstands follow-up questions.

**Example:** You ask about "users" but the AI references "products" from earlier

**Action:** Start new conversation with clear, explicit questions

Some models have context limits. Approaching limits degrades quality.

**GPT-3.5 Turbo:** 30+ messages may approach limits
**GPT-4:** 100+ messages
**Claude Models:** 200+ messages

**Action:** Monitor conversation length and start fresh proactively

### Conversation Management Best Practices

Move important queries to Scratchpad before starting new chat

"Show top 10" instead of "Can you please show me the top 10 results"

Fresh context prevents confusion and improves accuracy

Watch for performance degradation as indicator to start fresh

## Collaboration and Documentation

Build organizational knowledge by documenting and sharing effective query patterns.

### Saving Useful Queries to Scratchpad

Preserve valuable queries for reuse and team sharing.

Queries worth saving:
- Regular reporting queries
- Complex analytical queries you'll repeat
- Well-crafted queries that were hard to formulate
- Queries that revealed useful insights
- Templates for common operations

Hover over query results and click the Scratchpad icon (command line symbol).

Choose or create an appropriately named page.

In Scratchpad, add comments explaining:
- What the query does
- When to use it
- Any important caveats
- Expected result count or format

```
-- Monthly Revenue Report
-- Use at end of month for executive summary
-- Returns revenue by product category with YoY comparison
-- Expected: ~12 categories with revenue figures

SELECT ...
```

Create Scratchpad pages organized by:
- **Reporting Queries**: Regular reports and dashboards
- **Analysis Templates**: Patterns for common analysis tasks
- **Data Quality**: Validation and integrity checks
- **Maintenance**: Cleanup and optimization queries
- **Troubleshooting**: Diagnostic queries for common issues

### Sharing Query Patterns with Team

Build a team knowledge base of effective query patterns.

**Query Library Structure:**

**By Department:**
- Sales Queries
- Marketing Analytics
- Customer Support Queries
- Operations Reports

**By Frequency:**
- Daily Reports
- Weekly Analysis
- Monthly Summaries
- Quarterly Reviews

**By Complexity:**
- Simple Lookups (for beginners)
- Intermediate Analysis
- Advanced Multi-Table Queries
- Expert-Level Operations

**Documentation Template:**

```
-- QUERY NAME: Customer Lifetime Value by Segment
-- AUTHOR: Jane Smith
-- DATE: 2024-01-15
-- FREQUENCY: Monthly
-- DEPARTMENT: Marketing
--
-- DESCRIPTION:
-- Calculates total revenue per customer segment for
-- customer acquisition cost analysis
--
-- USAGE:
-- Run on first business day of month
-- Export results to marketing dashboard
--
-- EXPECTED RESULTS:
-- 5-8 customer segments with revenue totals
-- Usually between $50K-$500K per segment
--
-- NOTES:
-- Excludes test accounts (customer_id < 1000)
-- Uses previous month's complete data

SELECT ...
```

### Building a Query Knowledge Base

Record natural language patterns that work well with your specific database.

**Pattern Library Example:**

| Task | Effective Phrasing | Notes |
|------|-------------------|-------|
| User Analysis | "Show users where registration_source is X grouped by country" | Always specify registration_source values |
| Revenue Reports | "Calculate total revenue by product_category for last N days" | Use specific day counts, not "recent" |
| Data Quality | "Find records in TABLE where COLUMN is null or empty" | Be explicit about null vs. empty string |
| Performance | "Show top 100 records from LARGE_TABLE ordered by timestamp desc" | Always limit large table queries |

Share these patterns with team members to accelerate onboarding.

Document common issues and their solutions.

**Common Issue Templates:**

**Issue:** "AI returns wrong table"
**Solution:** "Always specify schema name: schema_name.table_name"

**Issue:** "Query times out on large table"
**Solution:** "Add WHERE clause to filter by date: 'last 30 days' or 'after 2024-01-01'"

**Issue:** "JOIN returns unexpected results"
**Solution:** "Be explicit about JOIN conditions: 'join orders with customers on customer_id'"

**Issue:** "Aggregation query is slow"
**Solution:** "Verify indexes exist on grouped columns: 'show indexes on table_name'"

Help the AI and team members by maintaining clear schema documentation.

**Document in Database:**
- Add comments to tables describing their purpose
- Add column comments explaining non-obvious fields
- Document relationships and foreign keys
- Maintain up-to-date ER diagrams

**PostgreSQL Example:**
```
COMMENT ON TABLE users IS 'Customer user accounts with authentication';
COMMENT ON COLUMN users.email_verified IS 'True if email verification completed';
COMMENT ON COLUMN users.last_login IS 'UTC timestamp of most recent successful login';
```

Well-documented schemas help the AI generate more accurate queries.

## Error Handling and Recovery

Understand common mistakes and recovery strategies.

### Common Mistakes to Avoid

**Mistake:**
```
Delete all users where status is inactive
```

Executed directly in production without verification.

**Prevention:**
1. Test in development database first
2. Use SELECT to verify before DELETE
3. Check row count before confirming
4. Ensure backup exists

**Recovery:**
If executed accidentally, restore from most recent backup.

**Mistake:**
```
Update all users to set role to admin
```

Missing WHERE clause affects all records.

**Prevention:**
- Always specify which records to modify
- Review confirmation dialog carefully
- Watch for "This will affect ALL records" warnings
- Use SELECT first to verify target records

**Recovery:**
If backup exists, restore affected table. Otherwise, manually identify and correct affected records.

**Mistake:**
```
Show me the data
```

Too vague, AI must guess intent.

**Prevention:**
- Name specific tables and columns
- Provide context and expected results
- Use precise terminology
- Be explicit about conditions

**Better:**
```
Show user_id, email, and created_at from users table where created_at is in the last 7 days
```

**Mistake:**
Clicking Confirm without reviewing the SQL statement in the confirmation dialog.

**Prevention:**
- Always read generated SQL before confirming
- Verify table names, WHERE clauses, and values
- Check estimated row counts
- Cancel and rephrase if anything looks wrong

**Impact:**
Unreviewed SQL might target wrong tables, use incorrect filters, or affect unintended records.

**Mistake:**
Continuing conversations for 50+ messages, assuming the AI remembers early context accurately.

**Prevention:**
- Start new conversations when switching topics
- Be explicit in follow-up questions
- Reference specific previous results
- Watch for signs of context confusion

**Impact:**
Long contexts lead to misunderstandings, slower responses, and incorrect query generation.

### Troubleshooting Approach

When queries don't work as expected, follow a systematic troubleshooting process.

**Symptoms:**
- Wrong data returned
- Error message displayed
- Empty result set when expecting data
- Query times out
- Incorrect aggregation results

**Initial Assessment:**
- What did you ask for?
- What did you actually receive?
- What error message appeared?
- Does the generated SQL match your intent?

Click the code view toggle to see the generated SQL.

**Check for:**
- Correct table and column names
- Appropriate WHERE conditions
- Proper JOIN conditions
- Expected aggregations
- Reasonable LIMIT clauses

**Compare against intent:**
- Does the SQL represent what you asked?
- Are there missing or extra conditions?
- Are JOINs correct?

If the query is complex, break it into smaller parts.

**Original Complex Question:**
```
Show me total revenue by product category for customers in California who made purchases in the last quarter, grouped by month
```

**Simplified Steps:**
```
1. Show me all orders from the last quarter
2. Now filter those to only California customers
3. Join with products to get categories
4. Calculate total revenue by category and month
```

Incremental refinement is more reliable than complex single questions.

Ensure the data you're querying actually exists.

**Verification Queries:**
```
Count records in the users table
Show me a sample of 5 records from orders
What columns exist in the products table
```

Empty result sets might indicate:
- Table is empty
- Filters are too restrictive
- Wrong table referenced
- Data is in different schema

Verify table and column names match your schema.

**Schema Verification:**
```
What tables exist in this database
Show me the structure of the users table
What columns exist in the orders table
```

Common issues:
- Table name typos
- Wrong schema referenced
- Column name variations (user_id vs. userId vs. id)

If the AI misunderstood, rephrase your question with more explicit details.

**Original (vague):**
```
Show user data
```

**Rephrased (explicit):**
```
Show user_id, email, first_name, last_name, and created_at from the users table for all active users
```

**Add examples:**
```
Show users created after January 1, 2024 (format: 2024-01-01)
```

### Recovery Strategies

For incorrect UPDATE operations, run compensating query to restore values

For significant data loss, restore affected tables from recent backup

If using Scratchpad with transactions, ROLLBACK before COMMIT

For small-scale errors, manually correct affected records

## Production Environment Guidelines

Using AI assistants in production requires additional discipline and procedures.

### Testing Queries

Never execute untested queries directly in production.

Connect to development or staging database first.

Generate and test queries in non-production environment.

Execute generated query and verify:
- Returns expected data
- Performance is acceptable
- No unintended side effects
- Handles edge cases correctly

Assess potential production impact:
- How many rows will be affected?
- Will this lock tables?
- Could this impact application performance?
- What's the rollback plan?

Move verified SQL to production:
- Copy SQL from Scratchpad
- Execute during appropriate window
- Monitor execution closely
- Verify results immediately

### Change Management

Follow established change management procedures for data modifications.

**Pre-Change Checklist:**
- [ ] Change request documented
- [ ] Stakeholder approval obtained
- [ ] Testing completed in non-production
- [ ] Backup verified and accessible
- [ ] Maintenance window scheduled (if needed)
- [ ] Rollback procedure documented
- [ ] Team members notified

**During Change:**
- [ ] Execute during scheduled window
- [ ] Monitor execution progress
- [ ] Watch for errors or unexpected behavior
- [ ] Verify results match expectations
- [ ] Document any deviations

**Post-Change:**
- [ ] Verify change completed successfully
- [ ] Update documentation
- [ ] Notify stakeholders of completion
- [ ] Retain backup for appropriate period
- [ ] Archive change records

### Audit Requirements

Maintain audit trails for compliance and troubleshooting.

**What to Log:**
- Natural language questions asked
- Generated SQL statements
- Execution timestamps
- Database user credentials used
- Affected row counts
- Success or failure status
- Error messages (if any)

**WhoDB Automatic Logging:**
WhoDB logs all executed queries with timestamps and user attribution.

**Additional Audit Measures:**
- Enable database-level query logging
- Review audit logs regularly
- Alert on suspicious patterns
- Retain logs per compliance requirements
- Protect logs from tampering

**Compliance Considerations:**
- GDPR: Log data access to personal information
- HIPAA: Track all PHI access
- SOX: Document all financial data queries
- PCI DSS: Log access to cardholder data

## Learning and Improvement

Use the AI assistant as a learning tool to improve SQL skills.

### Using AI to Learn SQL

The AI assistant is an excellent SQL tutor.

```
Explain how this query works
Why did you use LEFT JOIN instead of INNER JOIN
What does GROUP BY do in this query
```

The AI can explain SQL concepts in context.

```
Is there a more efficient way to write this query
Show me another way to achieve the same result
What are the trade-offs between these two approaches
```

Learn multiple solutions to the same problem.

```
How do window functions work in PostgreSQL
Show me an example of a CTE (common table expression)
What are the benefits of using indexes
```

Use the AI to explore advanced SQL features.

```
How can I make this query faster
What indexes would help this query perform better
Is this query using indexes efficiently
```

Learn performance optimization techniques.

### Building Query Skills

Begin with basic SELECT queries and gradually increase complexity.

**Progression:**
1. Simple SELECT from single table
2. SELECT with WHERE conditions
3. SELECT with ORDER BY and LIMIT
4. SELECT with aggregations (COUNT, SUM, AVG)
5. JOIN two tables
6. Complex multi-table JOINs
7. Subqueries and CTEs
8. Window functions and advanced features

Don't rush to complex queries. Master each level before advancing.

Write SQL manually, then ask the AI to generate equivalent query.

**Learning Exercise:**
1. Write a query manually in Scratchpad
2. Ask AI to generate same query using natural language
3. Compare the two approaches
4. Identify differences and improvements
5. Understand why AI chose its approach

This reveals patterns and techniques you might not have considered.

Don't just use queries blindly. Study them to understand patterns.

**What to Look For:**
- How does AI structure JOIN conditions?
- What aliasing patterns does it use?
- How does it handle date filtering?
- What aggregation patterns appear?
- How are subqueries structured?

Copy interesting patterns into your personal SQL knowledge base.

Understanding reasoning improves your SQL knowledge.

**Examples:**
```
Why did you use HAVING instead of WHERE
Why is this subquery necessary
Why did you choose this JOIN type
Why use COALESCE here
```

The AI can explain the reasoning behind SQL choices.

## Anti-Patterns (What NOT to Do)

Avoid these common anti-patterns that lead to problems.

### Do's and Don'ts

| Don't | Do | Reason |
|-------|-----|--------|
| ❌ "Show data" | ✅ "Show user_id, email from users table" | Vagueness leads to incorrect results |
| ❌ Execute without reviewing SQL | ✅ Always review confirmation dialogs | Catch errors before execution |
| ❌ Delete without SELECT first | ✅ SELECT, review, then DELETE | Verify targets before modification |
| ❌ Use write credentials for exploration | ✅ Use read-only credentials when possible | Prevent accidental modifications |
| ❌ Trust AI blindly | ✅ Verify results make sense | AI can generate incorrect queries |
| ❌ Continue 50+ message conversations | ✅ Start fresh after 20-30 messages | Context degrades over time |
| ❌ Skip backups before bulk operations | ✅ Always backup before modifications | Enable recovery from mistakes |
| ❌ Use production for testing | ✅ Test in development first | Protect production data |
| ❌ Ignore performance of queries | ✅ Add LIMIT clauses and filters | Prevent slow or expensive queries |
| ❌ Mention sensitive values in questions | ✅ Use generic terms | Protect privacy with external providers |

### Critical Anti-Patterns

These anti-patterns can cause serious data loss or security issues

**Anti-Pattern:**
Clicking Confirm on modification dialogs without reading the SQL.

**Impact:**
- Incorrect data modifications
- Accidental deletion of wrong records
- Updates to unintended tables
- Bulk changes affecting entire tables

**Correct Approach:**
Always read the complete SQL statement in the confirmation dialog. Verify table names, WHERE clauses, and values before confirming.

**Anti-Pattern:**
```
Delete users
```

Without specifying which users, or clicking confirm despite missing WHERE clause.

**Impact:**
All records deleted from table.

**Correct Approach:**
```
Delete users where user_id equals 12345
```

Always specify exact criteria. If you see "This will affect ALL records," cancel unless you truly intend to delete everything.

**Anti-Pattern:**
"Let me try this in production to see if it works."

**Impact:**
- Corrupted production data
- Service disruptions
- Data loss requiring restore operations
- Customer impact

**Correct Approach:**
Always test queries in development environment first. Only execute in production after thorough testing and verification.

**Anti-Pattern:**
Continuing single conversation for 100+ messages, assuming AI remembers everything accurately.

**Impact:**
- Misunderstood follow-up questions
- Incorrect table references
- Confused context leading to wrong queries
- Very slow response times

**Correct Approach:**
Start new conversations every 20-30 messages, especially when switching topics. Be explicit in follow-up questions rather than relying on distant context.

**Anti-Pattern:**
"AI generated it, so it must be correct."

**Impact:**
- Executing incorrect queries
- Missing data quality issues
- Accepting suboptimal performance
- Not learning underlying SQL

**Correct Approach:**
Always verify results make logical sense. Review generated SQL. Cross-check aggregation results. Treat AI as a helpful assistant, not an infallible oracle.

## Quick Reference Checklist

Use this checklist for every AI assistant session.

### Before Starting

- [ ] Connected to correct database (dev vs. prod)
- [ ] Using appropriate credentials (read-only for exploration)
- [ ] AI provider configured and working
- [ ] Understanding of data sensitivity level
- [ ] Backup exists if modifying data

### During Query Formulation

- [ ] Question is specific with table/column names
- [ ] Context provided where helpful
- [ ] Terminology appropriate to database type
- [ ] Expected results mentioned
- [ ] Starting simple before adding complexity

### Before Confirmation

- [ ] Reviewed generated SQL completely
- [ ] Verified correct table targeted
- [ ] Checked WHERE clause accuracy
- [ ] Estimated affected rows is reasonable
- [ ] Backup exists (for modifications)

### After Execution

- [ ] Results reviewed and make sense
- [ ] Row count matches expectations
- [ ] No unexpected errors
- [ ] Performance was acceptable
- [ ] Important queries saved to Scratchpad

### Conversation Management

- [ ] Conversation length is reasonable (\

Review AI Chat Assistant capabilities and features

Configure OpenAI, Anthropic, or Ollama for your needs

Learn effective techniques for data retrieval

Understand safe data modification with AI assistance

Master multi-turn conversations and context management

Comprehensive security practices for database management

## Summary

Effective AI Chat Assistant usage combines clear communication, security awareness, strategic provider selection, and systematic verification. Always be specific in questions, review generated SQL before execution, use read-only credentials for exploration, and backup before modifications. Choose AI providers based on your privacy, cost, and performance requirements. Build team knowledge by documenting effective query patterns and sharing learnings.

The AI assistant is a powerful tool that makes databases accessible to everyone while maintaining safety through confirmation workflows and visibility into generated SQL. By following these best practices, you can leverage AI assistance confidently while protecting your data and building your SQL skills.

Combine AI assistance with human judgment for optimal database management—the AI generates queries efficiently, and you verify they're correct before execution

---


# best-practices/postgresql.mdx
## PostgreSQL Best Practices

PostgreSQL-specific best practices for optimal database design, performance, and reliability

# PostgreSQL Best Practices

PostgreSQL is a powerful, feature-rich database system. Leveraging its advanced capabilities while following best practices ensures reliable, performant, and maintainable databases. This guide covers PostgreSQL-specific optimization techniques and patterns.

PostgreSQL's advanced features like JSON/JSONB, array types, and window functions provide capabilities that can dramatically improve application design when used properly.

## Schema Design

### Using Schemas for Organization

PostgreSQL schemas provide a logical namespace for tables, functions, and other objects. Use them to organize complex databases.

**Creating and Organizing Schemas:**

```
-- Create separate schemas for different concerns
CREATE SCHEMA public;
CREATE SCHEMA audit;
CREATE SCHEMA reporting;
CREATE SCHEMA api;

-- Create tables in specific schemas
CREATE TABLE public.users (
  user_id SERIAL PRIMARY KEY,
  email VARCHAR(255) UNIQUE NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE audit.user_changes (
  change_id BIGSERIAL PRIMARY KEY,
  user_id INTEGER REFERENCES public.users(user_id),
  changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Set search path to control default schema
SET search_path TO public, audit, pg_public;
```

Benefits of schema organization:
- Logical separation of concerns
- Simplified access control per schema
- Easier migrations and maintenance
- Better naming flexibility

### Using JSONB for Semi-Structured Data

JSONB (binary JSON) is PostgreSQL's powerful semi-structured data type. It's more efficient than TEXT JSON and supports advanced queries.

**JSONB Storage:**

```
CREATE TABLE products (
  product_id SERIAL PRIMARY KEY,
  name VARCHAR(255),
  metadata JSONB,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO products (name, metadata) VALUES (
  'Laptop',
  '{
    "brand": "TechCorp",
    "specs": {
      "cpu": "Intel i7",
      "ram": "16GB",
      "storage": "512GB SSD"
    },
    "colors": ["Silver", "Black", "Gold"],
    "price": 1299.99
  }'::JSONB
);
```

**Querying JSONB:**

```
-- Extract top-level values
SELECT metadata->>'brand' as brand
FROM products
WHERE product_id = 1;

-- Extract nested values
SELECT metadata->'specs'->>'cpu' as cpu
FROM products
WHERE product_id = 1;

-- Query by nested value
SELECT * FROM products
WHERE metadata->'specs'->>'ram' = '16GB';

-- Array operations
SELECT * FROM products
WHERE metadata->'colors' @> '"Silver"'::JSONB;

-- JSONB contains operator
SELECT * FROM products
WHERE metadata @> '{"brand": "TechCorp"}'::JSONB;
```

Use ->> (text) for text extraction and -> (JSONB) when you need to chain further operations. Create indexes on frequently queried JSONB paths for performance.

**JSONB Indexing:**

```
-- General JSONB index
CREATE INDEX idx_products_metadata
ON products USING GIN (metadata);

-- Index specific path
CREATE INDEX idx_products_brand
ON products USING GIN (metadata->'specs');

-- Functional index for specific extraction
CREATE INDEX idx_products_cpu
ON products ((metadata->'specs'->>'cpu'));
```

## Performance Optimization

### Index Strategies

PostgreSQL offers multiple index types optimized for different query patterns.

**B-Tree Index (Default):**

```
CREATE INDEX idx_users_email ON users(email);

-- Composite index for WHERE and ORDER BY
CREATE INDEX idx_orders_customer_date
ON orders(customer_id, created_at DESC);
```

**GiST Index (Geometric/Full-Text):**

```
CREATE INDEX idx_locations_geom
ON locations USING GiST(coordinates);

CREATE INDEX idx_documents_search
ON documents USING GiST(to_tsvector('english', content));
```

**GIN Index (JSON/Arrays):**

```
CREATE INDEX idx_products_tags
ON products USING GIN(tags);

CREATE INDEX idx_products_metadata
ON products USING GIN(metadata);
```

**BRIN Index (Block Range Index - for large tables):**

```
-- Excellent for very large tables with natural ordering
CREATE INDEX idx_events_timestamp
ON events USING BRIN(created_at);
```

### VACUUM and ANALYZE

PostgreSQL uses MVCC (Multi-Version Concurrency Control), which creates dead rows. Regular maintenance is essential.

**Manual VACUUM:**

```
-- Standard vacuum
VACUUM ANALYZE users;

-- Full vacuum (locks table, use offline)
VACUUM FULL users;

-- Verbose output
VACUUM VERBOSE users;

-- Analyze without vacuuming
ANALYZE users;
```

**Auto-VACUUM Configuration:**

```
-- Set in postgresql.conf
autovacuum = on
autovacuum_naptime = 10s
autovacuum_vacuum_threshold = 50
autovacuum_vacuum_scale_factor = 0.1

-- Check settings
SELECT * FROM pg_settings WHERE name LIKE '%autovacuum%';
```

**Monitor Vacuum Activity:**

```
-- Check last vacuum/analyze times
SELECT schemaname, tablename, last_vacuum, last_autovacuum
FROM pg_stat_user_tables;

-- Monitor dead tuples
SELECT schemaname, tablename,
  n_dead_tup,
  ROUND(n_dead_tup::numeric / NULLIF(n_live_tup, 0) * 100, 2) as dead_ratio
FROM pg_stat_user_tables
WHERE n_live_tup > 0
ORDER BY n_dead_tup DESC;
```

If dead tuple ratio exceeds 50%, manual VACUUM may be needed. High dead tuple counts slow queries significantly.

### Table Partitioning

Partitioning large tables improves query performance and simplifies maintenance.

**Range Partitioning (By Date):**

```
CREATE TABLE orders (
  order_id BIGSERIAL,
  customer_id INT,
  total DECIMAL,
  created_at DATE,
  PRIMARY KEY (order_id, created_at)
) PARTITION BY RANGE (YEAR(created_at));

CREATE TABLE orders_2024 PARTITION OF orders
  FOR VALUES FROM (2024) TO (2025);

CREATE TABLE orders_2025 PARTITION OF orders
  FOR VALUES FROM (2025) TO (2026);

-- Queries on 2024 data only scan orders_2024
SELECT * FROM orders WHERE created_at >= '2024-01-01' AND created_at < '2024-06-01';
```

**List Partitioning (By Category):**

```
CREATE TABLE user_logs (
  log_id BIGSERIAL PRIMARY KEY,
  user_id INT,
  log_type VARCHAR(50),
  message TEXT
) PARTITION BY LIST (log_type);

CREATE TABLE user_logs_login PARTITION OF user_logs
  FOR VALUES IN ('login', 'login_failed');

CREATE TABLE user_logs_activity PARTITION OF user_logs
  FOR VALUES IN ('create', 'update', 'delete');
```

## Advanced Features

### Window Functions

Window functions perform calculations across rows without collapsing data.

**Common Window Functions:**

```
-- Row numbering
SELECT
  order_id,
  customer_id,
  total,
  ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY created_at) as order_seq
FROM orders;

-- Running total
SELECT
  order_id,
  total,
  SUM(total) OVER (ORDER BY created_at) as running_total
FROM orders;

-- Rank with gaps
SELECT
  product_id,
  sales,
  RANK() OVER (ORDER BY sales DESC) as sales_rank
FROM products;

-- Dense rank (no gaps)
SELECT
  product_id,
  sales,
  DENSE_RANK() OVER (ORDER BY sales DESC) as sales_rank
FROM products;

-- Lead/lag values
SELECT
  order_id,
  total,
  LAG(total) OVER (ORDER BY created_at) as prev_total,
  LEAD(total) OVER (ORDER BY created_at) as next_total
FROM orders;

-- Percentage in group
SELECT
  category,
  COUNT(*) as count,
  ROUND(100.0 * COUNT(*) / SUM(COUNT(*)) OVER (), 2) as percentage
FROM products
GROUP BY category;
```

### Common Table Expressions (CTEs)

CTEs make complex queries readable and maintainable.

**Recursive CTE:**

```
-- Generate hierarchy
WITH RECURSIVE org_hierarchy AS (
  SELECT employee_id, name, manager_id, 1 as level
  FROM employees
  WHERE manager_id IS NULL

  UNION ALL

  SELECT e.employee_id, e.name, e.manager_id, h.level + 1
  FROM employees e
  JOIN org_hierarchy h ON e.manager_id = h.employee_id
)
SELECT * FROM org_hierarchy
ORDER BY level, name;
```

**Multiple CTEs:**

```
WITH monthly_sales AS (
  SELECT DATE_TRUNC('month', created_at)::DATE as month, SUM(total) as sales
  FROM orders
  GROUP BY 1
),
yearly_sales AS (
  SELECT DATE_TRUNC('year', created_at)::DATE as year, SUM(total) as sales
  FROM orders
  GROUP BY 1
)
SELECT
  ms.month,
  ms.sales as monthly,
  ys.sales as yearly,
  ROUND(100.0 * ms.sales / ys.sales, 2) as pct_of_year
FROM monthly_sales ms
JOIN yearly_sales ys ON DATE_TRUNC('year', ms.month)::DATE = ys.year;
```

### Full-Text Search

PostgreSQL offers powerful full-text search capabilities.

**Creating Full-Text Index:**

```
CREATE TABLE articles (
  article_id SERIAL PRIMARY KEY,
  title VARCHAR(255),
  content TEXT,
  search_vector TSVECTOR
);

-- Generate search vector
UPDATE articles
SET search_vector = to_tsvector('english', title || ' ' || content);

-- Create index for performance
CREATE INDEX idx_articles_search ON articles USING GIN(search_vector);
```

**Querying with Full-Text Search:**

```
-- Simple search
SELECT * FROM articles
WHERE search_vector @@ to_tsquery('english', 'database & optimization');

-- Ranking results
SELECT article_id, ts_rank(search_vector, query) as relevance
FROM articles, to_tsquery('english', 'performance') as query
WHERE search_vector @@ query
ORDER BY relevance DESC;
```

## Data Integrity

### Constraints

PostgreSQL constraints ensure data integrity at the database level.

**Comprehensive Constraint Example:**

```
CREATE TABLE users (
  user_id SERIAL PRIMARY KEY,
  email VARCHAR(255) UNIQUE NOT NULL,
  username VARCHAR(50) UNIQUE NOT NULL,
  age INT CHECK (age >= 18 AND age <= 150),
  status VARCHAR(20) DEFAULT 'active',
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Foreign key constraint
CREATE TABLE orders (
  order_id SERIAL PRIMARY KEY,
  user_id INT NOT NULL REFERENCES users(user_id) ON DELETE CASCADE,
  total DECIMAL(10,2) CHECK (total > 0),
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Exclude constraint (no overlapping time ranges)
CREATE TABLE bookings (
  booking_id SERIAL PRIMARY KEY,
  room_id INT,
  check_in TIMESTAMP,
  check_out TIMESTAMP,
  EXCLUDE USING GIST (room_id WITH =, tsrange(check_in, check_out) WITH &&)
);
```

## Connection Management

### Connection Pooling

PostgreSQL benefits from connection pooling for high-concurrency applications.

**PgBouncer Configuration:**

```
[databases]
myapp = host=localhost port=5432 dbname=myapp

[pgbouncer]
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 25
min_pool_size = 10
reserve_pool_size = 5
reserve_pool_timeout = 3
```

**Connection String:**

```
postgresql://user:password@pgbouncer-host:6432/myapp
```

## Monitoring and Maintenance

```
-- Find slow queries
SELECT query, calls, total_time, mean_time
FROM pg_stat_statements
ORDER BY mean_time DESC
LIMIT 10;

-- Monitor active queries
SELECT pid, usename, query, state
FROM pg_stat_activity
WHERE state = 'active';
```

```
-- Find largest tables
SELECT schemaname, tablename,
  ROUND(pg_total_relation_size(schemaname||'.'||tablename) / 1024.0 / 1024.0) as size_mb
FROM pg_tables
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
```

```
-- Unused indexes
SELECT schemaname, tablename, indexname
FROM pg_stat_user_indexes
WHERE idx_scan = 0
ORDER BY pg_relation_size(indexrelid) DESC;
```

PostgreSQL replication ensures high availability:

```
-- Enable replication in postgresql.conf
wal_level = replica
max_wal_senders = 10

-- Create replication slot
SELECT pg_create_physical_replication_slot('standby_slot');
```

## Related Topics

SQL query optimization techniques

General database performance guide

Safe data management practices

Database security guidelines

## Summary

PostgreSQL is a sophisticated database system with powerful features for performance, data integrity, and advanced querying. Proper schema design, strategic indexing, regular maintenance, and thoughtful use of advanced features create reliable, high-performance databases. Focus on understanding your workload and applying the appropriate PostgreSQL features to optimize for your specific use cases.

You now have practical knowledge of PostgreSQL-specific best practices to design and manage efficient, reliable databases.

---


# best-practices/mysql.mdx
## MySQL Best Practices

MySQL-specific best practices for optimal performance, reliability, and database design

# MySQL Best Practices

MySQL is the world's most popular open-source relational database. Optimizing MySQL databases requires understanding its unique characteristics, storage engines, and configuration options. This guide covers practical MySQL-specific best practices.

Understanding the differences between InnoDB and MyISAM, and choosing the right storage engine, is fundamental to MySQL optimization.

## Storage Engines

### Choosing the Right Engine

MySQL supports multiple storage engines. InnoDB is the default and recommended for most applications.

**InnoDB (Recommended):**

```
CREATE TABLE users (
  user_id INT AUTO_INCREMENT PRIMARY KEY,
  email VARCHAR(255) UNIQUE NOT NULL,
  name VARCHAR(255),
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
```

InnoDB features:
- ACID compliance with transactions
- Foreign key constraints
- Crash recovery
- Row-level locking (better for concurrent access)
- Good for write-heavy and mixed workloads

**MyISAM (Legacy):**

```
CREATE TABLE logs (
  log_id BIGINT AUTO_INCREMENT PRIMARY KEY,
  message TEXT,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
```

MyISAM characteristics:
- Table-level locking (poor concurrency)
- Fast for read-only operations
- No ACID compliance
- No foreign key support
- Only use for read-only archive tables

**Converting Tables:**

```
-- Convert MyISAM to InnoDB
ALTER TABLE legacy_table ENGINE=InnoDB;

-- Check current engine
SELECT TABLE_NAME, ENGINE FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'myapp';
```

Never use MyISAM for production databases. Only use InnoDB unless you have specific read-only use cases.

## Character Sets and Collations

### Proper Character Set Configuration

Character set mismatches cause data corruption and encoding issues.

**Correct Setup:**

```
-- Database level
CREATE DATABASE myapp
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;

-- Table level
CREATE TABLE users (
  user_id INT PRIMARY KEY,
  email VARCHAR(255) NOT NULL,
  name VARCHAR(255),
  bio TEXT
) CHARACTER SET utf8mb4
  COLLATE utf8mb4_unicode_ci;

-- Column level
CREATE TABLE products (
  product_id INT PRIMARY KEY,
  name VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
  description LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin
);
```

**Character Set Selection:**

- **utf8mb4**: Full Unicode support (emoji, international characters)
- **utf8**: Legacy (max 3 bytes per character)
- **latin1**: Western European only
- **ascii**: English only

Always use utf8mb4 for new projects. It supports all Unicode characters including emoji and costs minimal performance overhead.

**Collation Selection:**

- **utf8mb4_unicode_ci**: Case-insensitive, accent-insensitive (general purpose)
- **utf8mb4_bin**: Binary comparison (case-sensitive, accent-sensitive)
- **utf8mb4_general_ci**: Fast but less accurate for some languages

**Check Current Settings:**

```
-- Database level
SHOW CREATE DATABASE myapp;

-- Table level
SHOW CREATE TABLE users;

-- Server level
SHOW VARIABLES LIKE 'character_set_%';
SHOW VARIABLES LIKE 'collation_%';
```

## Indexing Strategy

### Index Types and Usage

MySQL supports multiple index types optimized for different query patterns.

**Basic Index Types:**

```
-- Single column index
CREATE INDEX idx_users_email ON users(email);

-- Composite index (order matters!)
CREATE INDEX idx_orders_customer_date
ON orders(customer_id, created_at DESC);

-- Unique index
CREATE UNIQUE INDEX idx_users_username ON users(username);

-- Primary key (special unique index)
ALTER TABLE users ADD PRIMARY KEY (user_id);

-- Full-text index
CREATE FULLTEXT INDEX idx_articles_content
ON articles(title, content);
```

**Index Performance Analysis:**

```
-- Check if index is used
EXPLAIN SELECT * FROM users WHERE email = 'user@example.com';

-- See index statistics
SELECT * FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_SCHEMA = 'myapp' AND TABLE_NAME = 'users';

-- Find unused indexes
SELECT OBJECT_SCHEMA, OBJECT_NAME, COUNT_READ, COUNT_WRITE
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE COUNT_READ = 0 AND OBJECT_SCHEMA != 'mysql'
ORDER BY COUNT_WRITE DESC;
```

### Column Selection for Indexes

**Good Index Candidates:**

```
-- Frequently used in WHERE clauses
CREATE INDEX idx_orders_status ON orders(status);

-- Used in JOIN conditions
CREATE INDEX idx_orders_customer_id ON orders(customer_id);
FOREIGN KEY (customer_id) REFERENCES customers(customer_id)

-- Used in ORDER BY
CREATE INDEX idx_products_category_price
ON products(category, price DESC);

-- Selective columns (good cardinality)
CREATE INDEX idx_users_country ON users(country);
```

**Avoid Indexing:**

```
-- Too many NULL values
CREATE TABLE problems (
  id INT,
  description TEXT,
  INDEX idx_description (description)  -- Bad if mostly NULL
);

-- Very low cardinality
CREATE TABLE issues (
  issue_id INT,
  is_active BOOLEAN,
  INDEX idx_is_active (is_active)  -- Only 2 values, poor selectivity
);

-- Frequently updated columns
CREATE TABLE users (
  user_id INT,
  login_count INT,
  last_login TIMESTAMP,
  INDEX idx_last_login (last_login)  -- Updates break index efficiency
);
```

## Performance Optimization

### Query Optimization

**Common Optimization Patterns:**

```
-- Use LIMIT to restrict results
SELECT * FROM orders
WHERE customer_id = 123
LIMIT 100;

-- Use covering indexes
CREATE INDEX idx_users_covering
ON users(email, created_at);

SELECT email, created_at FROM users
WHERE email = 'user@example.com';

-- Avoid SELECT *
SELECT id, name, email FROM users WHERE active = 1;

-- Use IDs for joins instead of text fields
SELECT o.order_id, c.name, o.total
FROM orders o
JOIN customers c ON o.customer_id = c.id
WHERE o.created_at > DATE_SUB(NOW(), INTERVAL 30 DAY);
```

### Connection Pooling

MySQL benefits significantly from connection pooling.

**Configuration:**

```
-- Check connection settings
SHOW VARIABLES LIKE 'max_connections';
SHOW VARIABLES LIKE '%timeout%';

-- Typical production settings in my.cnf
max_connections = 500
max_allowed_packet = 256M
wait_timeout = 28800
interactive_timeout = 28800
```

**Connection Pool Implementation:**

Most applications use connection pooling libraries:
- For Node.js: `mysql2/promise` with pooling
- For Python: `mysql-connector-python`
- For PHP: `mysqli` with persistent connections
- For Java: HikariCP

### Partitioning for Large Tables

Partitioning improves query performance on large tables.

**Range Partitioning:**

```
CREATE TABLE orders (
  order_id INT,
  customer_id INT,
  total DECIMAL(10,2),
  order_date DATE
)
PARTITION BY RANGE (YEAR(order_date)) (
  PARTITION p2023 VALUES LESS THAN (2024),
  PARTITION p2024 VALUES LESS THAN (2025),
  PARTITION p2025 VALUES LESS THAN (2026),
  PARTITION pmax VALUES LESS THAN MAXVALUE
);
```

**List Partitioning:**

```
CREATE TABLE users (
  user_id INT,
  region VARCHAR(50),
  name VARCHAR(255)
)
PARTITION BY LIST (region) (
  PARTITION north VALUES IN ('CA', 'OR', 'WA'),
  PARTITION south VALUES IN ('TX', 'FL', 'GA'),
  PARTITION east VALUES IN ('NY', 'MA', 'PA'),
  PARTITION other VALUES IN (DEFAULT)
);
```

## Data Integrity

### Constraints and Relationships

**Comprehensive Constraint Example:**

```
CREATE TABLE customers (
  customer_id INT AUTO_INCREMENT PRIMARY KEY,
  email VARCHAR(255) UNIQUE NOT NULL,
  name VARCHAR(255) NOT NULL,
  country_code VARCHAR(2),
  age INT CHECK (age >= 18),
  status ENUM('active', 'inactive', 'suspended') DEFAULT 'active',
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  INDEX idx_email (email),
  INDEX idx_status (status)
) ENGINE=InnoDB;

CREATE TABLE orders (
  order_id INT AUTO_INCREMENT PRIMARY KEY,
  customer_id INT NOT NULL,
  order_date DATE NOT NULL,
  total DECIMAL(10,2) CHECK (total > 0),
  status ENUM('pending', 'completed', 'cancelled') DEFAULT 'pending',
  FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
    ON DELETE CASCADE
    ON UPDATE CASCADE,
  INDEX idx_customer_id (customer_id),
  INDEX idx_order_date (order_date)
) ENGINE=InnoDB;
```

Use FOREIGN KEY constraints to ensure referential integrity. Cascade deletes/updates simplify data cleanup but use carefully.

### Transactions

MySQL transactions ensure ACID compliance with InnoDB.

**Transaction Example:**

```
START TRANSACTION;

-- Transfer money between accounts
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;

INSERT INTO transaction_log (from_account, to_account, amount)
VALUES (1, 2, 100);

COMMIT;
-- Or ROLLBACK if error occurs
```

**Transaction Isolation Levels:**

```
-- Strict isolation
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

-- Default (good balance)
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

-- Minimal locking
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- Fastest but risky
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
```

## Monitoring and Maintenance

### Regular Maintenance

**Table Optimization:**

```
-- Optimize individual table
OPTIMIZE TABLE users;

-- Optimize all tables in database
OPTIMIZE TABLE users, orders, products;

-- Check for corruption
CHECK TABLE users;
REPAIR TABLE users;

-- Analyze for query optimization
ANALYZE TABLE users;
```

**Monitor Performance:**

```
-- Active queries
SHOW PROCESSLIST;

-- Kill long-running query
KILL 12345;  -- Use process ID from PROCESSLIST

-- Database stats
SHOW STATUS;

-- Engine statistics
SHOW ENGINE INNODB STATUS;
```

## Replication and High Availability

### Master-Slave Replication

Setup replication for data redundancy and read scaling.

**Master Configuration (my.cnf):**

```
[mysqld]
server-id = 1
log_bin = mysql-bin
binlog_format = ROW
relay-log = mysql-relay-bin
relay-log-index = mysql-relay-bin.index
master-user = replication
master-password = securepass
```

**Slave Configuration (my.cnf):**

```
[mysqld]
server-id = 2
log_bin = mysql-bin
relay-log = mysql-relay-bin
relay-log-index = mysql-relay-bin.index
```

**Setup Replication:**

```
-- On Master
CREATE USER 'replication'@'slave_host' IDENTIFIED BY 'password';
GRANT REPLICATION SLAVE ON *.* TO 'replication'@'slave_host';
FLUSH PRIVILEGES;

-- Get master status
SHOW MASTER STATUS;

-- On Slave
CHANGE MASTER TO
  MASTER_HOST='master_host',
  MASTER_USER='replication',
  MASTER_PASSWORD='password',
  MASTER_LOG_FILE='mysql-bin.000001',
  MASTER_LOG_POS=12345;

START SLAVE;
SHOW SLAVE STATUS;
```

## Security Best Practices

```
-- Create application user with minimal privileges
CREATE USER 'appuser'@'app_host' IDENTIFIED BY 'strong_password';
GRANT SELECT, INSERT, UPDATE ON myapp.* TO 'appuser'@'app_host';
FLUSH PRIVILEGES;

-- Remove unnecessary default users
DELETE FROM mysql.user WHERE User='';
FLUSH PRIVILEGES;

-- Change root password
ALTER USER 'root'@'localhost' IDENTIFIED BY 'new_password';
```

```
-- Bind to specific IP only
SHOW VARIABLES LIKE 'bind_address';

-- Use SSL connections
SHOW VARIABLES LIKE 'have_ssl';

-- Require SSL for specific user
CREATE USER 'secure_user'@'%' IDENTIFIED BY 'password' REQUIRE SSL;
```

```
-- Enable general query log (development only, high overhead)
SET GLOBAL general_log = 'ON';
SET GLOBAL log_output = 'TABLE';

-- Check slow queries
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 2;

-- View slow queries
SELECT * FROM mysql.slow_log;
```

```
# Full backup
mysqldump --all-databases --single-transaction > backup_$(date +%Y%m%d).sql

# With binlog position for point-in-time recovery
mysqldump --all-databases --single-transaction --master-data=2 > backup.sql

# Compressed backup
mysqldump myapp | gzip > backup_myapp_$(date +%Y%m%d).sql.gz
```

## Related Topics

SQL query optimization techniques

General database performance guide

Safe data management practices

Database security guidelines

## Summary

MySQL success depends on understanding storage engines, proper character set configuration, strategic indexing, and diligent maintenance. InnoDB is the right choice for nearly all applications, utf8mb4 is the right character set, and regular optimization keeps performance optimal. Combine these practices with proper monitoring and you'll maintain reliable, performant MySQL databases.

You now have comprehensive knowledge of MySQL-specific best practices to design and manage efficient, reliable databases.

---


# best-practices/mongodb.mdx
## MongoDB Best Practices

MongoDB-specific best practices for effective document design, performance, and reliability

# MongoDB Best Practices

MongoDB is a powerful NoSQL database that enables flexible, schema-less data modeling. However, flexibility requires discipline. This guide covers practical MongoDB best practices for optimal performance, data integrity, and maintainability.

MongoDB's strength is flexibility, but that requires thoughtful design. Structure your documents considering your query patterns and data access needs.

## Document Design

### Schema Design Principles

While MongoDB is schema-less, good document design is critical.

**Flat vs. Nested Structure:**

```
// GOOD: Nested structure for related data
{
  "_id": ObjectId("..."),
  "userId": 123,
  "email": "user@example.com",
  "profile": {
    "firstName": "John",
    "lastName": "Doe",
    "avatar": "https://..."
  },
  "settings": {
    "notifications": true,
    "theme": "dark",
    "language": "en"
  },
  "createdAt": ISODate("2024-01-01"),
  "updatedAt": ISODate("2024-10-31")
}

// AVOID: Too flat, harder to manage
{
  "_id": ObjectId("..."),
  "userId": 123,
  "email": "user@example.com",
  "firstName": "John",
  "lastName": "Doe",
  "avatar": "https://...",
  "notifications": true,
  "theme": "dark",
  "language": "en",
  "createdAt": ISODate("2024-01-01")
}
```

**Embedding vs. Referencing:**

```
// EMBEDDING: For frequently accessed related data
db.orders.insertOne({
  _id: ObjectId("..."),
  orderNumber: "ORD-001",
  customer: {
    id: ObjectId("..."),
    name: "John Doe",
    email: "john@example.com",
    address: {
      street: "123 Main St",
      city: "New York",
      state: "NY",
      zip: "10001"
    }
  },
  items: [
    {
      productId: ObjectId("..."),
      productName: "Laptop",
      quantity: 1,
      price: 999.99
    }
  ],
  total: 999.99,
  createdAt: ISODate("2024-10-31")
});

// REFERENCING: For separate concerns, or to avoid duplication
db.orders.insertOne({
  _id: ObjectId("..."),
  orderNumber: "ORD-001",
  customerId: ObjectId("customer123"),
  items: [ObjectId("item1"), ObjectId("item2")],
  total: 999.99,
  createdAt: ISODate("2024-10-31")
});

// Separate collection
db.customers.findOne({ _id: ObjectId("customer123") });
db.items.find({ _id: { $in: [ObjectId("item1"), ObjectId("item2")] } });
```

Embed data that is accessed together frequently. Reference data that changes independently or needs to be accessed separately.

### Array Handling

Arrays require careful consideration for performance and querying.

**Good Array Practices:**

```
// GOOD: Fixed, bounded arrays (manageable size)
db.users.insertOne({
  _id: ObjectId("..."),
  email: "user@example.com",
  favoriteProducts: [
    ObjectId("prod1"),
    ObjectId("prod2"),
    ObjectId("prod3")
  ]  // Known, small set of IDs
});

// GOOD: Timestamps for ordering
db.comments.insertOne({
  _id: ObjectId("..."),
  postId: ObjectId("..."),
  comments: [
    {
      author: "User1",
      text: "Great post!",
      createdAt: ISODate("2024-10-30T10:00:00Z")
    },
    {
      author: "User2",
      text: "Thanks for sharing",
      createdAt: ISODate("2024-10-30T11:00:00Z")
    }
  ]
});

// AVOID: Unbounded, continuously growing array
db.events.insertOne({
  _id: ObjectId("..."),
  // DON'T DO THIS - grows infinitely:
  allUserActions: []  // Could become huge
});

// BETTER: Use separate collection for unbounded data
db.userActions.insertMany([
  { userId: ObjectId("..."), action: "login", timestamp: ISODate("...") },
  { userId: ObjectId("..."), action: "view", timestamp: ISODate("...") }
]);
```

## Indexing Strategy

### Index Types and Creation

**Creating Indexes:**

```
// Single field index
db.users.createIndex({ email: 1 });

// Descending order
db.orders.createIndex({ createdAt: -1 });

// Compound index
db.orders.createIndex({ customerId: 1, createdAt: -1 });

// Index with options
db.users.createIndex(
  { email: 1 },
  { unique: true, name: "idx_users_email" }
);

// Sparse index (only indexes documents with field)
db.users.createIndex(
  { phone: 1 },
  { sparse: true }
);

// Text index (full-text search)
db.articles.createIndex(
  { title: "text", content: "text" }
);

// Geospatial index
db.locations.createIndex({ coordinates: "2dsphere" });

// TTL index (auto-delete old documents)
db.sessions.createIndex(
  { createdAt: 1 },
  { expireAfterSeconds: 3600 }  // Delete after 1 hour
);
```

**Analyzing Indexes:**

```
// View all indexes on collection
db.users.getIndexes();

// Get index statistics
db.users.aggregate([{ $indexStats: {} }]);

// Analyze query performance
db.users.explain("executionStats").find({ email: "user@example.com" });

// Remove unused index
db.users.dropIndex("idx_users_email");

// Rebuild indexes
db.users.reIndex();
```

Too many indexes slow writes (every INSERT/UPDATE must update all indexes). Balance read optimization with write performance.

### Query Pattern Optimization

**Using Explain to Optimize:**

```
// Check if index is used
db.orders.explain("executionStats").find({
  customerId: ObjectId("..."),
  createdAt: { $gt: ISODate("2024-01-01") }
});

// Look for:
// - executionStages.stage: "COLLSCAN" (bad, full collection scan)
// - executionStages.stage: "IXSCAN" (good, index scan)
// - executionStats.totalDocsExamined: X
// - executionStats.nReturned: Y
// - Ratio of examined to returned should be low
```

## Query Optimization

### Aggregation Pipeline

The aggregation pipeline is MongoDB's most powerful query tool.

**Aggregation Example:**

```
// Complex multi-stage aggregation
db.orders.aggregate([
  // Stage 1: Match (filter early)
  {
    $match: {
      createdAt: { $gte: ISODate("2024-01-01") },
      status: "completed"
    }
  },
  // Stage 2: Add computed fields
  {
    $addFields: {
      month: { $month: "$createdAt" },
      year: { $year: "$createdAt" }
    }
  },
  // Stage 3: Group and aggregate
  {
    $group: {
      _id: { month: "$month", year: "$year" },
      totalSales: { $sum: "$total" },
      avgOrder: { $avg: "$total" },
      orderCount: { $sum: 1 }
    }
  },
  // Stage 4: Sort results
  { $sort: { "_id.year": -1, "_id.month": -1 } },
  // Stage 5: Project (select fields)
  {
    $project: {
      _id: 0,
      month: "$_id.month",
      year: "$_id.year",
      totalSales: 1,
      avgOrder: { $round: ["$avgOrder", 2] },
      orderCount: 1
    }
  }
]);
```

### Common Query Patterns

**Pattern 1: Full-Text Search:**

```
// Create text index
db.articles.createIndex({ title: "text", content: "text" });

// Search
db.articles.find({
  $text: { $search: "mongodb database" }
});

// Search with relevance score
db.articles.find(
  { $text: { $search: "mongodb" } },
  { score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } });
```

**Pattern 2: Array Queries:**

```
// Find documents containing a value in an array
db.users.find({ favoriteProducts: ObjectId("prod123") });

// Query array element by position
db.comments.find({ "comments.0.author": "John" });

// Query with array size
db.users.find({ favoriteProducts: { $size: 5 } });

// Query with array element matching
db.comments.find({
  comments: { $elemMatch: { author: "John", likes: { $gt: 10 } } }
});
```

**Pattern 3: Date Range Queries:**

```
// Orders in last 7 days
db.orders.find({
  createdAt: {
    $gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)
  }
});

// Orders between dates
db.orders.find({
  createdAt: {
    $gte: ISODate("2024-01-01"),
    $lt: ISODate("2024-02-01")
  }
});
```

**Pattern 4: Conditional Updates:**

```
// Upsert (insert if not exists, update if exists)
db.users.updateOne(
  { email: "user@example.com" },
  {
    $set: {
      lastLogin: new Date(),
      status: "active"
    }
  },
  { upsert: true }
);

// Atomic operations
db.products.updateOne(
  { _id: ObjectId("prod123") },
  { $inc: { stockCount: -1 } }  // Decrement atomically
);

// Array operations
db.users.updateOne(
  { _id: ObjectId("user123") },
  { $push: { favoriteProducts: ObjectId("prod456") } }  // Add to array
);
```

## Data Integrity and Validation

### Schema Validation

MongoDB 3.6+ supports schema validation.

**Creating Schema Validation:**

```
// Create collection with schema validation
db.createCollection("users", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["email", "name"],
      properties: {
        _id: { bsonType: "objectId" },
        email: {
          bsonType: "string",
          pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
        },
        name: { bsonType: "string" },
        age: {
          bsonType: ["int", "long"],
          minimum: 18,
          maximum: 150
        },
        status: {
          enum: ["active", "inactive", "suspended"]
        },
        profile: {
          bsonType: "object",
          properties: {
            firstName: { bsonType: "string" },
            lastName: { bsonType: "string" }
          }
        }
      }
    }
  }
});

// Add validation to existing collection
db.runCommand({
  collMod: "orders",
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["customerId", "total"],
      properties: {
        customerId: { bsonType: "objectId" },
        total: {
          bsonType: "decimal",
          minimum: 0
        }
      }
    }
  }
});
```

### Transactions

MongoDB 4.0+ supports multi-document ACID transactions.

**Transaction Example:**

```
// Start session
const session = db.getMongo().startSession();

try {
  session.startTransaction();

  // Multiple operations
  db.accounts.updateOne(
    { _id: ObjectId("account1") },
    { $inc: { balance: -100 } },
    { session: session }
  );

  db.accounts.updateOne(
    { _id: ObjectId("account2") },
    { $inc: { balance: 100 } },
    { session: session }
  );

  db.transactions.insertOne(
    { from: ObjectId("account1"), to: ObjectId("account2"), amount: 100 },
    { session: session }
  );

  session.commitTransaction();
} catch (error) {
  session.abortTransaction();
  throw error;
} finally {
  session.endSession();
}
```

## Performance Best Practices

Large batch inserts are more efficient:

```
// INEFFICIENT: Individual inserts
for (let i = 0; i < 1000; i++) {
  db.users.insertOne({ name: `User${i}` });
}

// EFFICIENT: Batch insert
const docs = [];
for (let i = 0; i < 1000; i++) {
  docs.push({ name: `User${i}` });
}
db.users.insertMany(docs, { ordered: false });
```

```
// Using MongoDB Node.js driver
const client = new MongoClient(uri, {
  maxPoolSize: 50,
  minPoolSize: 10,
  waitQueueTimeoutMS: 10000,
  socketTimeoutMS: 45000
});
```

```
// Return only needed fields
db.users.find(
  { status: "active" },
  { projection: { _id: 1, email: 1, name: 1 } }
);

// Exclude large fields
db.articles.find(
  { category: "tech" },
  { projection: { content: 0 } }  // Exclude large content field
);
```

```
// Enable profiling
db.setProfilingLevel(1, { slowms: 100 });

// View profiled queries
db.system.profile.find().sort({ ts: -1 }).limit(5).pretty();

// Disable profiling
db.setProfilingLevel(0);

// Database statistics
db.stats();

// Collection statistics
db.users.stats();
```

## Replication and High Availability

### Replica Sets

Replica sets provide data redundancy and high availability.

**Replica Set Configuration:**

```
// Initiate replica set
rs.initiate({
  _id: "rs0",
  members: [
    { _id: 0, host: "mongo1:27017", priority: 2 },
    { _id: 1, host: "mongo2:27017", priority: 1 },
    { _id: 2, host: "mongo3:27017", priority: 1 }
  ]
});

// Check replica set status
rs.status();

// Add member to replica set
rs.add("mongo4:27017");

// Remove member
rs.remove("mongo4:27017");
```

## Common Mistakes to Avoid

### Mistake 1: Excessive Nesting

```
// AVOID: Too deeply nested
{
  level1: {
    level2: {
      level3: {
        level4: {
          level5: {
            data: "value"
          }
        }
      }
    }
  }
}

// BETTER: Flatter structure with reasonable nesting
{
  category: "value",
  subcategory: "value",
  data: "value"
}
```

### Mistake 2: Using Field Names as Data

```
// AVOID: Storing data in field names
{
  _id: ObjectId("..."),
  "user1": 100,
  "user2": 200,
  "user3": 150
}

// BETTER: Use arrays or separate collection
{
  _id: ObjectId("..."),
  scores: [
    { userId: "user1", score: 100 },
    { userId: "user2", score: 200 },
    { userId: "user3", score: 150 }
  ]
}
```

### Mistake 3: Missing Indexes on Query Fields

```
// SLOW: No index
db.orders.find({ customerId: ObjectId("...") });

// BETTER: Create index
db.orders.createIndex({ customerId: 1 });
db.orders.find({ customerId: ObjectId("...") });
```

## Related Topics

SQL query optimization techniques

General database performance guide

Safe data management practices

Database security guidelines

## Summary

MongoDB's flexibility is powerful, but it requires thoughtful design. Structure documents considering query patterns, use embedded vs. referenced data strategically, create appropriate indexes, and leverage the aggregation pipeline for complex queries. Schema validation and transactions add data integrity guarantees. When applied consistently, these practices result in high-performance, maintainable MongoDB databases.

You now have comprehensive knowledge of MongoDB best practices to design and manage efficient, reliable NoSQL databases.

---


# best-practices/collaboration.mdx
## Team Collaboration

Best practices for effective database team collaboration, query sharing, and knowledge management with WhoDB

# Team Collaboration

Effective database team collaboration improves productivity, reduces errors, and ensures institutional knowledge is preserved across your organization. This guide covers proven practices for sharing queries, establishing workflows, maintaining documentation, and building a collaborative database culture.

## Establishing Collaboration Workflows

### Define Team Roles and Responsibilities

Clear role definition prevents confusion and ensures accountability within your database team.

**Common Database Team Roles:**

- **Database Administrators**: Configuration, backups, performance tuning, security
- **Data Analysts**: Query writing, data exploration, reporting, insights
- **Data Engineers**: Pipeline development, data transformations, ETL workflows
- **Developers**: Application queries, integration patterns, data access layers
- **Data Scientists**: Complex analysis, machine learning data preparation, experimentation

**Role Definition Process:**

1. Document responsibilities for each role
2. Clarify decision-making authority
3. Define escalation paths
4. Establish on-call procedures
5. Create role-specific documentation

### Establish Query Review Processes

Code review practices adapted for SQL queries improve quality and prevent errors.

**Query Review Workflow:**

```
1. Developer writes query in WhoDB Scratchpad
2. Developer documents purpose and expected results
3. Peer review in team channel
4. Performance review by DBA
5. Approval and documentation
6. Integration into production systems
```

**Critical Query Reviews:**

These queries require extra scrutiny before execution:

- DELETE or UPDATE operations on production data
- Schema modifications
- New queries running on large datasets
- Queries accessing sensitive data
- Administrative operations

**Review Checklist:**

- [ ] Query logic is correct and clear
- [ ] WHERE clause prevents unintended data changes
- [ ] Indexes are properly utilized (checked via EXPLAIN)
- [ ] No unnecessary full table scans
- [ ] Result set is reasonably sized
- [ ] Query handles edge cases
- [ ] Variable names are meaningful
- [ ] Comments explain complex logic

### Regular Knowledge Sharing Meetings

Schedule recurring meetings to share discoveries and discuss database topics.

**Weekly Team Syncs (30-60 minutes):**

- Recent issues encountered
- Performance improvements made
- New query patterns discovered
- Upcoming maintenance windows
- Training topics

**Monthly Deep Dives (1-2 hours):**

- Complex query optimization case studies
- New features or tools evaluation
- Database version upgrades planning
- Capacity planning discussions
- Security reviews

**Quarterly Planning Sessions:**

- Technical roadmap
- Performance improvement initiatives
- Training and skill development
- Infrastructure upgrades
- Compliance and security updates

## Sharing and Documenting Queries

### Query Repository Best Practices

Centralize query storage for easy discovery and reuse.

**Repository Structure:**

```
queries/
├── analytics/
│   ├── daily_sales_summary.sql
│   ├── user_retention.sql
│   └── README.md
├── reporting/
│   ├── customer_invoices.sql
│   ├── inventory_status.sql
│   └── README.md
├── operational/
│   ├── data_cleanup.sql
│   ├── health_checks.sql
│   └── README.md
└── migrations/
    ├── 2024-01-migration.sql
    └── README.md
```

**Query Documentation Template:**

```
-- Query: Calculate Monthly Revenue by Region
-- Author: Analytics Team
-- Created: 2024-01-15
-- Last Updated: 2024-01-20
-- Purpose: Generate monthly revenue breakdown by sales region
-- Dependencies: sales, customers tables
-- Expected Runtime: < 5 seconds
-- Output Rows: ~50-100

-- Usage:
-- SELECT * FROM monthly_revenue_by_region WHERE month = '2024-01'

SELECT
  c.region,
  DATE_TRUNC('month', s.sale_date) as month,
  COUNT(*) as transaction_count,
  SUM(s.amount) as total_revenue
FROM sales s
JOIN customers c ON s.customer_id = c.id
WHERE s.sale_date >= '2024-01-01'
GROUP BY c.region, DATE_TRUNC('month', s.sale_date)
ORDER BY month DESC, total_revenue DESC;
```

### Query Tagging and Categorization

Use consistent tagging systems to make queries discoverable.

**Recommended Tags:**

- **Purpose**: `reporting`, `analysis`, `operational`, `diagnostic`, `testing`
- **Frequency**: `daily`, `weekly`, `monthly`, `adhoc`, `one-time`
- **Data Classification**: `public`, `internal`, `sensitive`, `pii`
- **Performance**: `light`, `medium`, `heavy`
- **Status**: `stable`, `experimental`, `deprecated`

**Example Tags:**

```
Query: user_signup_trends.sql
Tags: [reporting, weekly, public, light, stable]

Query: customer_lifetime_value.sql
Tags: [analysis, monthly, sensitive, medium, stable]

Query: data_migration_validation.sql
Tags: [operational, one-time, internal, heavy, experimental]
```

### Query Documentation Standards

Consistent documentation helps team members understand intent and usage.

Every query should begin with clear header information:
- Query name and purpose
- Author and creation date
- Last modified date and by whom
- Expected output and performance characteristics
- Any dependencies or prerequisites

Add comments for complex logic:
```
-- Filter to active users created in last 30 days
-- to calculate recent adoption metrics
SELECT u.id, u.email
FROM users u
WHERE u.active = true
  AND u.created_at >= NOW() - INTERVAL '30 days'
```

Document any parameters used:
```
-- Parameters:
-- @start_date: Beginning of reporting period (default: 30 days ago)
-- @end_date: End of reporting period (default: today)
-- @region: Sales region filter (default: NULL = all regions)

SELECT * FROM sales
WHERE sale_date BETWEEN @start_date AND @end_date
  AND (region = @region OR @region IS NULL)
```

Include concrete examples:
```
Usage Example:
  SELECT * FROM daily_sales
  WHERE region = 'NORTH' AND sale_date = '2024-01-20'

Expected Results:
  ~50-100 rows, columns: date, region, product, amount
```

## Collaborative Query Development

### Using Scratchpad for Team Queries

WhoDB's Scratchpad feature enables live collaborative query development.

**Collaborative Query Workflow:**

1. **Initiate**: Create new Scratchpad session and share link
2. **Iterate**: Team members write and refine queries together
3. **Test**: Run queries against development database
4. **Document**: Add comments and parameter explanations
5. **Review**: Perform peer code review
6. **Finalize**: Export and store in repository

**Live Collaboration Best Practices:**

- Use clear cell names: `user_analysis`, `sales_by_region`, `validation_checks`
- Add context comments between cells explaining flow
- Test incrementally before building large queries
- Save versions as you reach milestones
- Export final version to repository

**Scratchpad Cell Organization:**

```
Cell 1: Load and explore source tables
Cell 2: Data validation queries
Cell 3: Transformation logic
Cell 4: Join operations
Cell 5: Final result with aggregations
Cell 6: Quality checks and verification
```

### Pair Programming for Complex Queries

Two developers working together produce better queries than either alone.

**Pair Programming Session:**

- **Driver**: Writes the query
- **Navigator**: Reviews logic, suggests improvements, checks for errors
- **Roles rotate**: Switch every 15-20 minutes

**When to Use Pair Programming:**

- Complex multi-step queries
- First-time database interactions
- Performance-critical operations
- Queries on production data
- Knowledge transfer opportunities

**Session Template:**

```
Duration: 45-60 minutes
Setup:
  - Share screen or use collaborative editor
  - Both have read access to relevant tables
  - Development database selected

Progress:
  - Break problem into small steps
  - Alternate driver/navigator roles
  - Test frequently
  - Document decisions

Outcome:
  - Finalized, well-documented query
  - Both team members understand implementation
  - Production-ready documentation
```

## Version Control and Query History

### Using Query History Effectively

Track who ran what queries and when for accountability and learning.

**Query History Best Practices:**

- Review your recent queries for patterns and improvements
- Learn from colleagues' query approaches
- Identify frequently used patterns for documentation
- Spot potential performance issues
- Maintain audit trail for compliance

**Regular History Reviews:**

- Weekly: Check your own query history for refactoring opportunities
- Monthly: Team review of significant queries
- Quarterly: Identify patterns and establish standards

### Maintaining Query Versions

Track query evolution as requirements change.

**Versioning Approach:**

```
queries/sales_analysis/
├── customer_revenue_v1.sql     (initial version, 2024-01)
├── customer_revenue_v2.sql     (added region filter, 2024-02)
├── customer_revenue_v3.sql     (optimized indexes, 2024-03)
└── customer_revenue_current.sql (active version)
```

**Version Notes Template:**

```
Version 2.1 (2024-01-20)
Changes:
  - Added region parameter for filtering
  - Excluded test orders from calculations
  - Optimized join order

Performance:
  - Before: 12 seconds, 1M rows
  - After: 3 seconds, filtered to 50K rows

Breaking Changes: Region parameter now required
Migration Guide: Scripts defaulting region to 'ALL' if not specified
```

## Knowledge Documentation

### Creating Institutional Knowledge Base

Document database knowledge to prevent silos and enable team independence.

**Knowledge Base Structure:**

```
documentation/
├── schemas/
│   ├── sales_schema.md
│   ├── users_schema.md
│   └── products_schema.md
├── operational_runbooks/
│   ├── daily_maintenance.md
│   ├── data_refresh_procedure.md
│   └── incident_response.md
├── query_patterns/
│   ├── common_joins.md
│   ├── aggregation_patterns.md
│   └── performance_tips.md
└── team_guidelines/
    ├── naming_conventions.md
    ├── code_review_standards.md
    └── on_call_procedures.md
```

**Documentation Frequency:**

- Update immediately when processes change
- Quarterly review for accuracy
- Annual comprehensive audit
- Remove deprecated procedures

### Schema and Table Documentation

Each table should be documented with:
- Business purpose and usage
- Data ownership and stewardship
- Update frequency and mechanisms
- Retention policies
- Query examples

Example:
```
# Table: orders

**Purpose**: Tracks all customer orders and transactions

**Ownership**: Sales Operations Team

**Update Frequency**: Real-time as orders are placed

**Data Retention**: 7 years for compliance

**Typical Queries**:
- Revenue analysis by date range
- Customer order history
- Product sales patterns
```

Document each significant column:
- Business meaning and usage
- Data type and valid ranges
- Nullable status and defaults
- Relationships to other tables
- Update patterns

Example:
```
**Column: order_status**
- Type: VARCHAR(50)
- Valid Values: pending, processing, shipped, delivered, cancelled
- Nullable: No, defaults to 'pending'
- Last Updated: Updated via order_status_updated_at
- Usage: Filter queries for reporting, joins to status_history table
```

Document important business logic:
- Calculation definitions
- Data quality requirements
- Integrity constraints
- Edge cases and exceptions

Example:
```
**Revenue Calculation Rule**:
- Includes: Base price + taxes + shipping
- Excludes: Discounts, refunds, cancelled orders
- Edge Case: Partial refunds counted as 50% of order amount
- Updated: Quarterly review for accuracy
```

### Runbook Development

Create step-by-step procedures for common tasks.

**Daily Maintenance Runbook:**

```
# Daily Database Maintenance

## 1. Morning Health Check (8:00 AM)
- [ ] Check database connectivity
- [ ] Review error logs from overnight
- [ ] Verify backup completion
- [ ] Check disk space utilization

SQL: SELECT * FROM health_checks_daily;

## 2. Performance Monitoring (Hourly)
- [ ] Check query performance metrics
- [ ] Review active connection count
- [ ] Identify long-running queries

Query: SELECT * FROM slow_queries WHERE duration_seconds > 60;

## 3. Data Refresh (2:00 PM)
- [ ] Run materialized view refresh
- [ ] Update cache tables
- [ ] Verify data freshness

Procedure: CALL refresh_analytics_views();

## 4. Evening Cleanup (5:00 PM)
- [ ] Archive old logs
- [ ] Cleanup temporary tables
- [ ] Verify backup for next day

Query: DELETE FROM temp_tables WHERE created_at < NOW() - INTERVAL '1 day';
```

### Troubleshooting Guides

Document solutions to common problems for faster resolution.

**Troubleshooting Guide Template:**

```
# Connection Timeout Issues

## Problem
Queries timeout when accessing production database

## Symptoms
- Query hangs after 30 seconds
- Connection closed by server
- Appears to work on dev database

## Root Causes
1. Network latency between locations
2. Query performance degradation
3. Connection pool exhaustion
4. Network firewall rules

## Diagnostic Steps
1. Check query plan: EXPLAIN ANALYZE query;
2. Verify network: ping -c 4 db-host
3. Check connections: SELECT COUNT(*) FROM pg_stat_activity;
4. Review error logs for specific messages

## Solutions
1. **Network Issue**: Contact network team, use VPN
2. **Slow Query**: Optimize using EXPLAIN, add indexes
3. **Connection Pool**: Increase max_connections in config
4. **Firewall**: Verify IP whitelist with security team

## Prevention
- Monitor query performance regularly
- Maintain connection pool metrics
- Test queries on production dataset sizes
- Document known problematic queries
```

## Building Collaborative Culture

### Code of Conduct for Database Teams

Establish expectations for respectful, productive collaboration.

- Questions are encouraged, never criticized
- Mistakes are learning opportunities
- Different experience levels valued
- Diverse perspectives improve decisions

- Share what you learn with the team
- Attend industry conferences and training
- Experiment with new techniques (safely)
- Document new discoveries

- Take responsibility for your work
- Help colleagues when they need support
- Improve documentation when you find gaps
- Contribute to team knowledge base

- Document your decisions and reasoning
- Ask for help early before problems escalate
- Provide context in code reviews
- Share status updates proactively

### Mentorship and Training

Structured knowledge transfer ensures team growth and capability building.

**Mentorship Program:**

- **New Team Members**: Assign senior mentor for first 3 months
- **Skill Development**: Monthly training on emerging technologies
- **Cross-Training**: Rotate team members to learn different areas
- **Certifications**: Support relevant database certifications

**Training Session Topics:**

- Advanced query optimization techniques
- New database version features
- Security and compliance requirements
- Performance tuning methodologies
- Disaster recovery procedures

### Celebrating Successes

Recognition builds team morale and reinforces positive behaviors.

**Recognition Ideas:**

- Monthly "Query of the Month" for elegant solutions
- Performance improvement awards
- Documentation excellence recognition
- Problem-solving recognition
- Team member spotlights

**Example Recognition:**

```
Query of the Month: January 2024
Selected: customer_segmentation_analysis.sql

Submitted by: Sarah Chen
Reason: Optimized complex join from 45 seconds to 2 seconds
        using strategic column ordering and indexes

Impact: Revenue analysis reports now run 20x faster,
        enabling real-time dashboard updates

Team Recognition: Shared in monthly newsletter and team meeting
Benefit: Query archived as reference pattern for team learning
```

## Collaboration Tools Integration

### Connecting WhoDB to Communication Platforms

Share query results and insights seamlessly with the team.

**Query Sharing Workflow:**

1. Generate results in WhoDB
2. Export data (CSV, Excel, JSON)
3. Share directly in Slack/Teams channel
4. Include query notes and context
5. Link back to permanent query repository

**Communication Best Practices:**

- Share query, not just results
- Include interpretation of findings
- Highlight any unexpected data patterns
- Provide timeline for data freshness
- Document any limitations or assumptions

## Collaboration Checklist

**Team Setup:**

- [ ] Database team roles clearly defined
- [ ] Query review process documented
- [ ] Regular meeting schedule established
- [ ] Query repository structure created
- [ ] Documentation templates prepared

**Daily Collaboration:**

- [ ] Complex queries are peer reviewed
- [ ] Query history reviewed for opportunities
- [ ] Blockers discussed with team promptly
- [ ] Documentation updated when processes change
- [ ] Questions asked rather than assumed

**Knowledge Management:**

- [ ] Schema documentation is current
- [ ] Runbooks are available and tested
- [ ] Troubleshooting guides cover common issues
- [ ] Query patterns documented and shared
- [ ] Team training schedule maintained

**Culture:**

- [ ] New team members have assigned mentor
- [ ] Success is celebrated and recognized
- [ ] Mistakes treated as learning opportunities
- [ ] Continuous improvement is encouraged
- [ ] Knowledge sharing is valued and rewarded

## Summary

Effective team collaboration transforms database management from a siloed function into a collective capability. By establishing clear workflows, documenting thoroughly, sharing knowledge generously, and building a culture of respect and continuous learning, your database team becomes more resilient, productive, and capable of handling increasingly complex requirements. The time invested in collaboration practices pays dividends through reduced errors, faster problem resolution, and improved institutional knowledge. WhoDB's collaborative features make it easier than ever to build a strong, unified database team.

---


# best-practices/access-control.mdx
## Database Access Control

Best practices for database access control, role-based access, read-only users, and security policies with WhoDB

# Database Access Control

Proper access control is fundamental to database security. This guide covers implementing role-based access, managing read-only users, establishing connection profiles, and enforcing security policies to protect your databases while enabling productive work.

## Access Control Fundamentals

### Principle of Least Privilege

Grant users only the minimum permissions necessary to perform their job functions.

**Least Privilege Benefits:**

- Reduces blast radius of compromised credentials
- Minimizes accidental data damage
- Simplifies security audits
- Improves compliance posture
- Enables precise accountability tracking

**Implementation Process:**

1. Document each user's actual job requirements
2. Grant read-only access initially
3. Expand permissions only when justified
4. Review periodically and remove unused access
5. Audit changes regularly

**Dangerous Anti-Patterns:**

- Sharing credentials between team members
- Using administrative accounts for routine work
- Granting broad permissions "just in case"
- Not reviewing access after role changes
- Maintaining access after team transitions

### Database User Categories

Design your access control strategy around user categories.

**Purpose**: Data exploration, reporting, analysis

**Permissions**:
- SELECT on specific schemas
- View creation (read-only views)
- Query execution only

**Restrictions**:
- No INSERT, UPDATE, DELETE
- No DDL (CREATE, ALTER, DROP)
- No administrative functions
- Limited to development/reporting data

**Example (PostgreSQL)**:
```
CREATE ROLE analyst_readonly;
GRANT CONNECT ON DATABASE analytics TO analyst_readonly;
GRANT USAGE ON SCHEMA public TO analyst_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO analyst_readonly;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT SELECT ON TABLES TO analyst_readonly;

CREATE USER analyst_user WITH PASSWORD 'secure_password';
GRANT analyst_readonly TO analyst_user;
```

**Purpose**: Read and write application data in development/staging

**Permissions**:
- SELECT, INSERT, UPDATE on specific tables
- View creation
- Stored procedure execution
- Limited schema modifications in dev only

**Restrictions**:
- No production write access
- No administrative operations
- No user management
- No backup/restore operations

**Example (PostgreSQL)**:
```
CREATE ROLE developer_app;
GRANT CONNECT ON DATABASE dev_db TO developer_app;
GRANT USAGE ON SCHEMA public TO developer_app;
GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO developer_app;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO developer_app;
```

**Purpose**: ETL operations, data transformations, pipeline management

**Permissions**:
- Full access to staging schemas
- SELECT on source tables
- Full access to transformation tables
- Scheduled job execution
- Read-only access to production source data

**Restrictions**:
- No direct production data modifications
- No user management
- No security configuration changes
- No backup operations

**Example (PostgreSQL)**:
```
CREATE ROLE data_engineer;
GRANT CONNECT ON DATABASE analytics TO data_engineer;
GRANT USAGE ON SCHEMA public, staging TO data_engineer;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO data_engineer;
GRANT ALL ON ALL TABLES IN SCHEMA staging TO data_engineer;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA staging TO data_engineer;
```

**Purpose**: System administration, maintenance, emergency operations

**Permissions**:
- Full database access
- User and role management
- Backup and recovery operations
- Configuration management
- Performance tuning and monitoring

**Restrictions**:
- MFA required
- Limited to administrative tasks only
- Comprehensive audit logging mandatory
- Change approval process required
- Time-limited elevated sessions

**Purpose**: Automated processes, scheduled jobs, application runtime

**Permissions**:
- Only necessary for application function
- Environment-specific (dev, staging, prod)
- Limited to required tables and operations

**Restrictions**:
- No interactive use
- No shared with developers
- Rotated regularly
- Monitored for suspicious activity
- No elevated privileges

## Role-Based Access Control (RBAC)

### Designing Your RBAC Schema

Establish a systematic approach to managing roles and permissions.

**RBAC Structure Example:**

```
Database: production
├── Organization Role
│   ├── department_sales_read
│   ├── department_marketing_read
│   └── department_finance_read
├── Function Role
│   ├── analyst_read
│   ├── developer_app
│   ├── engineer_etl
│   └── admin_full
└── Data Role
    ├── can_access_customer_pii
    ├── can_access_financial
    ├── can_access_internal_only
    └── can_modify_production
```

**Naming Conventions:**

```
Format: [environment]_[function]_[permission]

Examples:
- dev_analyst_read          (development, analytics, read-only)
- prod_app_readwrite        (production, application, read+write)
- staging_engineer_full     (staging, data engineering, full access)
- prod_admin_emergency      (production, admin, emergency access)
```

### Role Hierarchy

Organize roles hierarchically to simplify administration.

**Hierarchy Example:**

```
admin_full
├── developer_app
│   ├── analyst_read
│   └── user_basic
├── engineer_etl
│   ├── analyst_read
│   └── user_basic
└── support_tier1
    └── user_basic
```

**Benefits:**

- Inheriting permissions reduces duplication
- Changes to parent roles propagate automatically
- Clear permission hierarchy understood by teams
- Easier onboarding and offboarding

### Implementing RBAC

**Step 1: Create roles**
```
CREATE ROLE analyst_read;
CREATE ROLE developer_app;
CREATE ROLE engineer_etl;
CREATE ROLE admin_full;
```

**Step 2: Define permissions**
```
-- Read-only analyst role
GRANT CONNECT ON DATABASE myapp TO analyst_read;
GRANT USAGE ON SCHEMA public TO analyst_read;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO analyst_read;

-- Developer role (inherits analyst permissions + write)
GRANT analyst_read TO developer_app;
GRANT INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO developer_app;
```

**Step 3: Create users and assign roles**
```
CREATE USER alice WITH PASSWORD 'secure_password';
GRANT analyst_read TO alice;

CREATE USER bob WITH PASSWORD 'secure_password';
GRANT developer_app TO bob;
```

**Step 4: Set default permissions**
```
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT SELECT ON TABLES TO analyst_read;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT SELECT, INSERT, UPDATE ON TABLES TO developer_app;
```

**Step 1: Create users with roles**
```
CREATE USER 'analyst'@'%' IDENTIFIED BY 'secure_password';
CREATE USER 'developer'@'%' IDENTIFIED BY 'secure_password';
CREATE USER 'admin'@'%' IDENTIFIED BY 'secure_password';
```

**Step 2: Grant role-based permissions**
```
-- Analyst: read-only
GRANT SELECT ON myapp.* TO 'analyst'@'%';

-- Developer: read and write
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'developer'@'%';

-- Admin: full access
GRANT ALL PRIVILEGES ON myapp.* TO 'admin'@'%' WITH GRANT OPTION;
```

**Step 3: Apply changes**
```
FLUSH PRIVILEGES;
```

**Step 4: Set default role**
```
ALTER USER 'developer'@'%' DEFAULT ROLE 'developer_app';
```

**Step 1: Create roles with specific privileges**
```
db.createRole({
  role: "analyst_readonly",
  privileges: [
    {
      resource: { db: "analytics", collection: "" },
      actions: ["find", "listCollections"]
    }
  ],
  roles: []
})
```

**Step 2: Create users with role assignment**
```
db.createUser({
  user: "analyst_user",
  pwd: "secure_password",
  roles: [
    { role: "analyst_readonly", db: "analytics" }
  ]
})
```

**Step 3: Verify role permissions**
```
db.getRole("analyst_readonly", { showPrivileges: true })
```

## Read-Only Access Management

### Creating Safe Read-Only Users

Read-only users are essential for non-destructive database access.

**Read-Only User Best Practices:**

- Use for analysts, consultants, auditors
- Default to read-only, expand only when justified
- Combine with IP restrictions and time-based limits
- Monitor for suspicious activity patterns
- Rotate credentials regularly

### Read-Only Verification

Verify that read-only access truly prevents modifications. Some database configurations can accidentally grant write access through views or functions.

**Verification Queries:**

```
-- PostgreSQL: Verify user has no write permissions
SELECT grantee, privilege_type
FROM information_schema.role_table_grants
WHERE table_name='your_table'
  AND grantee='analyst_user';

-- Should return: SELECT only, not INSERT, UPDATE, DELETE
```

```
-- MySQL: Check user privileges
SHOW GRANTS FOR 'analyst_user'@'%';

-- Verify only SELECT is granted
```

```
-- MongoDB: List user roles and privileges
db.getUser("analyst_user")
// Verify role contains only read actions
```

### View-Based Read-Only Access

Use views to provide controlled, read-only access to specific data.

**Sensitive Data Masking View:**

```
-- Hide sensitive columns for read-only users
CREATE VIEW customers_redacted AS
SELECT
  id,
  name,
  city,
  country,
  '***' as email,           -- Mask email
  '***' as phone,           -- Mask phone
  FALSE as is_premium       -- Hide business logic
FROM customers;

-- Grant read-only access to view only
GRANT SELECT ON customers_redacted TO analyst_readonly;
```

**Time-Series Data View:**

```
-- Provide read-only access to recent data only
CREATE VIEW recent_transactions AS
SELECT *
FROM transactions
WHERE created_at >= NOW() - INTERVAL '90 days';

-- Grant access to view, not underlying table
GRANT SELECT ON recent_transactions TO analyst_readonly;
REVOKE SELECT ON transactions FROM analyst_readonly;
```

## Connection Profile Management

### Establishing Connection Profiles

WhoDB connection profiles securely store database credentials and settings.

**Connection Profile Best Practices:**

- Use environment-specific profiles (dev, staging, prod)
- Never hardcode credentials in configuration files
- Rotate credentials regularly
- Use least privilege database users
- Enable SSL/TLS for all connections
- Document connection purpose and usage

### Organizing Connection Profiles

Use consistent naming for easy identification:

Format: `[environment]-[database-type]-[region]-[purpose]`

Examples:
- `dev-postgres-us-east-analytics`
- `prod-mysql-us-west-app`
- `staging-postgres-eu-reporting`
- `dev-mongodb-local-testing`

Keep profiles organized by environment:

```
Development
├── dev-postgres-local
├── dev-mysql-docker
└── dev-mongodb-local

Staging
├── staging-postgres-rds
├── staging-mysql-rds
└── staging-mongodb-atlas

Production
├── prod-postgres-primary
├── prod-postgres-replica
├── prod-mysql-primary
└── prod-mongodb-cluster
```

Use color coding to prevent accidental production operations:

- **Red**: Production databases (extreme caution)
- **Yellow**: Staging databases (caution, test first)
- **Green**: Development/test databases (safe for experimentation)
- **Blue**: Read-only replicas (safe for queries)

In WhoDB:
- Assign colors to connection profiles
- Always verify color before executing modifications
- Train team on color meanings

Configure security parameters in each profile:

```
Host: db.example.com
Port: 5432
Database: myapp
Username: app_user
SSL Mode: require (or verify-full)
Timeout: 30 seconds
Pool Size: 10

Notes:
- Uses IAM authentication with assumed role
- Read replica: only for reporting queries
- Backup location: us-east-1a
- VPN required: Yes
```

### Credential Rotation

Regular credential rotation is essential for security. Credentials should be rotated at least quarterly, immediately after employee departure, and after any suspected breach.

**Credential Rotation Process:**

```
Step 1: Prepare new credentials
├── Generate new password (16+ chars, mixed case, symbols)
├── Test in development environment first
└── Verify old credentials still work (for rollback)

Step 2: Update database
├── Create new user with same permissions
├── Verify new user can connect
└── Keep old user active temporarily

Step 3: Update applications
├── Update connection string in WhoDB
├── Verify all services connect successfully
├── Monitor logs for connection errors

Step 4: Verify and cleanup
├── Confirm all services use new credentials
├── Run security audit
├── Remove old credentials
└── Document rotation date and approver
```

**Rotation Schedule:**

```
Production Databases:  Quarterly + immediate after separation
Staging Databases:     Semi-annually + after breach suspicion
Development Databases: Annually + after onboarding
Service Accounts:      Quarterly + after vulnerability scan
```

## Security Policies

### Data Classification

Classify data by sensitivity to guide access control decisions.

**Data Classification Levels:**

| Level | Examples | Access | Encryption | Auditing |
|-------|----------|--------|------------|----------|
| **Public** | Product catalog, public documentation | Everyone | Optional | Optional |
| **Internal** | Sales reports, team info | Employees | Recommended | Recommended |
| **Sensitive** | Customer data, health records | Department specific | Required | Required |
| **Restricted** | Passwords, API keys, PII | Minimal, need-based | Required | Required |

**Classification Process:**

1. Audit all tables and columns
2. Document sensitivity level
3. Define access restrictions
4. Implement technical controls
5. Review and update annually

**Example Classification:**

```
Table: users
├── id: PUBLIC
├── name: INTERNAL
├── email: SENSITIVE (PII)
├── password_hash: RESTRICTED
└── ssn: RESTRICTED (highly sensitive)
```

### Implementing Access Policies

Restrict access to sensitive columns:

```
-- PostgreSQL: Create role without access to sensitive columns
CREATE ROLE analyst_no_pii;
GRANT SELECT (id, name, city, country) ON customers TO analyst_no_pii;

-- Not SELECT *, only specified columns
-- Excludes email, phone, ssn
```

PostgreSQL Row-Level Security (RLS):

```
-- Enable RLS on table
ALTER TABLE customers ENABLE ROW LEVEL SECURITY;

-- Analysts can only see customers in their region
CREATE POLICY analyst_access_by_region ON customers
  FOR SELECT
  USING (region = current_setting('app.user_region'));

-- Set user's region
SET app.user_region = 'NORTH_AMERICA';
```

Restrict access by time:

```
-- PostgreSQL: Create role valid only during business hours
CREATE ROLE developer_business_hours;

-- Check access time before granting connection
CREATE OR REPLACE FUNCTION check_business_hours()
RETURNS BOOLEAN AS $$
BEGIN
  RETURN EXTRACT(HOUR FROM CURRENT_TIME) BETWEEN 9 AND 18
    AND EXTRACT(DOW FROM CURRENT_DATE) BETWEEN 1 AND 5;
END;
$$ LANGUAGE plpgsql;
```

Restrict connections by IP:

```
Connection Profile: prod-postgres-primary
├── Host: db.prod.example.com
├── Allowed IPs:
│   ├── 10.0.1.0/24 (office network)
│   ├── 10.0.2.0/24 (VPN network)
│   └── 203.0.113.45/32 (admin home)
└── Blocked IPs: 0.0.0.0/0 (deny all by default)
```

### Audit Logging

Enable comprehensive audit logging for all database access. Audit logs are critical for security investigations, compliance audits, and incident response.

**What to Log:**

- User login/logout events
- Query execution (SELECT, INSERT, UPDATE, DELETE)
- Schema modifications
- Security policy changes
- Failed authentication attempts
- Administrative operations

**PostgreSQL Audit Configuration:**

```
-- Install pgAudit extension
CREATE EXTENSION pgaudit;

-- Log all write operations
ALTER SYSTEM SET pgaudit.log = 'write, ddl';

-- Log which tables accessed
ALTER SYSTEM SET pgaudit.log_relation = on;

-- Log statement details
ALTER SYSTEM SET pgaudit.log_statement = off;
ALTER SYSTEM SET pgaudit.log_statement_once = off;

-- Reload configuration
SELECT pg_reload_conf();
```

**Audit Log Review Process:**

```
Daily:
  - Check for failed authentication attempts
  - Review administrative operations
  - Monitor unusual access patterns

Weekly:
  - Analyze access by user and role
  - Identify overprivileged accounts
  - Review data exports

Monthly:
  - Comprehensive access review
  - Compliance verification
  - Detection of suspicious patterns

Quarterly:
  - Formal access audit
  - Recertification of access
  - Policy effectiveness review
```

## Managing Access Lifecycle

### User Onboarding

- [ ] Database account created
- [ ] Appropriate role assigned
- [ ] Temporary password issued securely
- [ ] Connection profile provided
- [ ] Security policy overview provided
- [ ] WhoDB access granted

- [ ] User confirmed connection works
- [ ] Permissions verified correct
- [ ] Training completed on security policies
- [ ] First queries documented
- [ ] Questions answered

- [ ] Permissions still appropriate for role
- [ ] Access usage review
- [ ] Feedback on onboarding process
- [ ] Additional training needs identified
- [ ] Permanent password set

### Access Reviews

**Process:**
1. Generate user list with assigned roles
2. Send to department managers for verification
3. Collect feedback on access appropriateness
4. Identify and remove unnecessary access
5. Document review and approvals
6. Archive for compliance

**Template:**
```
User: alice@example.com
Current Role: analyst_readonly
Department: Sales
Manager: Bob Johnson

Questions:
- Does this user still need database access? YES / NO
- Is the role appropriate? YES / NO / NEEDS_UPGRADE / NEEDS_DOWNGRADE
- Any concerns? ___________________

Recommendation: APPROVE / REVOKE / MODIFY
```

When user changes roles:
- [ ] Remove old role permissions
- [ ] Document date of change
- [ ] Assign new role permissions
- [ ] Verify appropriate access
- [ ] Document business justification
- [ ] Notify security team

Inactive accounts should be disabled:

```
Audit Query (PostgreSQL):
SELECT usename, valuntil, last_login
FROM pg_user
LEFT JOIN pg_stat_user_tables ON 1=1
WHERE last_login < NOW() - INTERVAL '90 days'
  OR last_login IS NULL;

Action:
- Disable account after 90 days inactivity
- Archive after 1 year
- Delete after 2 years (per retention policy)
```

### User Offboarding

Prompt offboarding is critical when employees leave. Delayed credential removal represents a significant security risk.

**Offboarding Checklist:**

```
Effective Date: [departure date]
Employee: [name]

Immediate (Day 0):
  [ ] Disable database user account
  [ ] Revoke all role memberships
  [ ] Disable SSH keys if applicable
  [ ] Remove VPN access
  [ ] Notify security team

Within 24 Hours:
  [ ] Confirm account is disabled
  [ ] Check for running queries/sessions
  [ ] Audit recent query history
  [ ] Document access used over final period

Within 7 Days:
  [ ] Archive credentials securely
  [ ] Document any outstanding work
  [ ] Transfer owned queries to team
  [ ] Update access documentation
  [ ] File security incident if needed
```

## Access Control Checklist

**Initial Setup:**

- [ ] Database roles defined for each user type
- [ ] Least privilege principle implemented
- [ ] Read-only users created for analysts
- [ ] Connection profiles configured securely
- [ ] SSL/TLS enabled for all connections

**Role Management:**

- [ ] RBAC hierarchy established
- [ ] Default permissions set
- [ ] New table permissions automated
- [ ] Role documentation complete
- [ ] Service accounts use least privilege

**Security Policies:**

- [ ] Data classified by sensitivity
- [ ] Column-level security implemented
- [ ] Row-level security configured
- [ ] IP restrictions enforced
- [ ] Audit logging enabled

**Ongoing Maintenance:**

- [ ] Quarterly access reviews scheduled
- [ ] Credential rotation calendar maintained
- [ ] Dormant accounts identified monthly
- [ ] Audit logs reviewed regularly
- [ ] Policy violations investigated

**Incident Response:**

- [ ] Breach response procedures documented
- [ ] Escalation path defined
- [ ] Audit log preservation process established
- [ ] Forensic analysis capabilities in place
- [ ] Communication template prepared

## Summary

Robust access control requires careful planning, consistent implementation, and ongoing maintenance. By implementing role-based access, managing credentials securely, defining clear policies, and conducting regular reviews, you create a strong security posture that protects your databases while enabling productive work. WhoDB's connection profile management and audit capabilities make it straightforward to implement these best practices. Remember that access control is not a one-time setup—it requires continuous attention as your organization evolves, team members change, and new threats emerge.

---


# best-practices/documentation.mdx
## Database Documentation

Best practices for effective database documentation including schema docs, query documentation, and operational runbooks

# Database Documentation

Comprehensive, well-maintained documentation is the foundation of effective database management. This guide covers best practices for documenting schemas, queries, and operational procedures to ensure knowledge is preserved, accessible, and usable across your organization.

## Schema Documentation

### Table Documentation

Every table should have clear, complete documentation explaining its purpose and structure.

**Table Documentation Template:**

```
# Table: customers

## Business Purpose
Stores core customer information for all accounts in the system.
Used for billing, communications, reporting, and customer service.

## Data Ownership
- Owner: Sales Operations Team
- Stakeholders: Finance, Customer Success, Marketing
- Contact: sales-ops@example.com

## Update Frequency
- Real-time: Customer created/updated through web app
- Batch: Imported from legacy system (monthly reconciliation)
- Last Updated: [system generated timestamp]

## Data Retention Policy
- Active customers: Retained indefinitely
- Inactive customers: Retained for 7 years (compliance requirement)
- Deleted records: Soft delete only, never permanently removed

## Typical Usage
- Active user base: ~500K records
- Monthly growth: 2-3%
- Historical data since: 2015-01-01

## Related Tables
- orders (customer has many orders)
- billing_addresses (customer has multiple)
- support_tickets (customer related)
- analytics_customer_fact (denormalized reporting)

## Known Issues
- Phone number format inconsistent (not validated at input)
- Legacy data includes some null email addresses (~5%)
- Duplicate email entries possible in data from 2015-2016

## Access Restrictions
- PII columns: Restricted to authorized roles
- Email/phone: Masked for read-only users
- Production data: Not available in development environment
```

### Column Documentation

Document exact data types and constraints:

```
### Column: customer_id
- Type: BIGINT
- Constraint: PRIMARY KEY, NOT NULL, AUTO INCREMENT
- Default: Auto-generated by database
- Range: 1 to 9223372036854775807
```

Explain what the column represents in business terms:

```
### Column: customer_tier
- Type: VARCHAR(50)
- Business Meaning: Customer subscription level affecting pricing and features
- Valid Values:
  - 'free': Trial users with basic access
  - 'standard': Paid monthly subscription
  - 'professional': Annual commitment, priority support
  - 'enterprise': Custom agreement, dedicated support
- Default: 'free'
- Can be NULL: No
```

Describe how and when data is updated:

```
### Column: last_login_at
- Type: TIMESTAMP WITH TIME ZONE
- Updated: Every time user logs in (application layer)
- How: Application sets to CURRENT_TIMESTAMP
- Update Frequency: Variable (depends on user activity)
- Time Zone: UTC, always
- Can be NULL: Yes (user never logged in)
- Used For: Account activity tracking, identifying inactive users
```

Document foreign keys and relationships:

```
### Column: primary_address_id
- Type: BIGINT
- Foreign Key: references addresses.id
- Relationship: Many customers → One address
- Can be NULL: Yes (address not yet provided)
- Orphan Handling: ON DELETE SET NULL (address deletion)
- Used For: Billing and shipping addresses
- Index: Yes (frequently joined)
```

Flag sensitive columns clearly:

```
### Column: credit_card_last_4
- Type: VARCHAR(4)
- Sensitivity: RESTRICTED (PII)
- Masking: Masked as '****' for non-admin users
- Encryption: At rest (encrypted column)
- Audit: All access logged and monitored
- Compliance: PCI DSS requirement
- Usage: Payment processing, fraud detection
```

Document calculated or derived columns:

```
### Column: account_age_days
- Type: INTEGER
- Calculated: EXTRACT(DAY FROM CURRENT_DATE - created_at)
- Refresh: Real-time calculation on query
- Used For: Reporting, segmentation
- Performance: Indexed view for frequent queries
- Edge Case: Null if created_at is null
```

### Schema Diagrams

Visual representations help communicate complex relationships.

**Entity Relationship Diagram (ERD) Documentation:**

```
customers (1) ──── (many) orders
     │
     ├── (1) ──── (many) billing_addresses
     │
     ├── (1) ──── (many) support_tickets
     │
     └── (1) ──── (many) activity_logs

orders (1) ──── (many) order_items
  │
  └── (many) ──── (1) products

Key:
- customers.id is PRIMARY KEY
- orders.customer_id FOREIGN KEY
- Each relationship documented with ON DELETE policy
```

**Using WhoDB Graph View:**

- Visualize schema relationships
- Export diagram for documentation
- Reference for new team members
- Identify missing relationships

## Query Documentation

### Query Repository Structure

Organize queries for easy discovery and maintenance.

**Repository Organization:**

```
queries/
├── README.md                          (overview and guidelines)
├── analytics/
│   ├── README.md
│   ├── revenue_by_region.sql
│   ├── customer_cohort_analysis.sql
│   ├── monthly_metrics.sql
│   └── .versions/
│       ├── monthly_metrics_v1.sql
│       └── monthly_metrics_v2.sql
├── reporting/
│   ├── README.md
│   ├── sales_dashboard_data.sql
│   ├── customer_insights.sql
│   └── inventory_status.sql
├── operational/
│   ├── README.md
│   ├── health_checks.sql
│   ├── data_cleanup.sql
│   └── orphaned_records_finder.sql
└── monitoring/
    ├── README.md
    ├── slow_queries.sql
    ├── connection_pool_status.sql
    └── table_growth_trends.sql
```

### Query Documentation Standard

Every query should start with complete metadata:

```
-- ============================================================================
-- Query: Daily Revenue Summary by Region
-- Purpose: Generate daily revenue breakdown for finance team reporting
-- ============================================================================
-- Author: Analytics Team (analytics@example.com)
-- Created: 2024-01-15
-- Last Updated: 2024-01-20 by Sarah Chen
-- Version: 2.0
--
-- Description:
--   Calculates total daily revenue by sales region for executive dashboard.
--   Includes transaction counts and average order values.
--   Updated to exclude test orders and refunded transactions.
--
-- Frequency: Daily (1 PM UTC)
-- Expected Runtime: 8-12 seconds on production
-- Output Rows: ~50-100 (one per region per day)
--
-- Dependencies:
--   - tables: sales_transactions, customers, products
--   - functions: get_fiscal_date()
--   - materialized views: mv_customer_segments (refreshed daily)
--
-- Historical Changes:
--   v2.0 (2024-01-20): Excluded test orders, optimized joins
--   v1.5 (2024-01-10): Added return order filtering
--   v1.0 (2024-01-01): Initial version
-- ============================================================================
```

Document all query parameters:

```
-- PARAMETERS
-- ============================================================================
-- @start_date     [REQUIRED]  Start of reporting period (YYYY-MM-DD)
-- @end_date       [REQUIRED]  End of reporting period (YYYY-MM-DD)
-- @region_filter  [OPTIONAL]  Specific region or NULL for all (default: NULL)
-- @exclude_test   [OPTIONAL]  Exclude test transactions (default: true)
--
-- USAGE EXAMPLES:
--   SELECT * FROM daily_revenue_by_region
--   WHERE date BETWEEN '2024-01-01' AND '2024-01-31'
--     AND (@region_filter IS NULL OR region = @region_filter)
--
-- PERFORMANCE NOTES:
--   - Add index on (sales_date, region) for production
--   - Query is slowest first week of month (full historical scan)
-- ============================================================================
```

Explain important business rules:

```
-- BUSINESS RULES
-- ============================================================================
-- Revenue Definition:
--   - Includes: Base product price + applicable taxes + shipping
--   - Excludes: Discounts applied at checkout, refunds
--   - Partial Refunds: Counted as 50% of original order amount
--   - Early Payment Discount: Applied in revenue (not excluded)
--
-- Test Data Handling:
--   - Test orders identified by customer email: *@test-internal.com
--   - Typically 10-15 test orders per day, excluded by default
--
-- Regional Assignment:
--   - Based on customer.billing_region (NOT shipping_region)
--   - "UNASSIGNED" region for international orders without region
-- ============================================================================
```

Include optimization and performance information:

```
-- PERFORMANCE CHARACTERISTICS
-- ============================================================================
-- Query Plan:
--   1. Index scan on sales_transactions(sales_date, region)
--   2. Hash join to customers on customer_id
--   3. Filter and aggregation (streaming)
--
-- Performance Baseline:
--   - Full year query (365 days): 12 seconds
--   - Single month query: 2 seconds
--   - Single day query: 0.2 seconds
--
-- Potential Issues:
--   - Slow if transaction volume grows > 2M records/day
--   - Consider partitioning by month if 1M+ rows
--   - Disk I/O intensive during backup windows
--
-- Optimization Recommendations:
--   - Filter by date first (use index)
--   - Avoid ORDER BY on large sets (client-side sorting)
--   - Use LIMIT for testing before full run
-- ============================================================================
```

Track query evolution:

```
-- CHANGE LOG
-- ============================================================================
-- v2.0 (2024-01-20) - Sarah Chen
--   - Excluded test orders from calculations
--   - Fixed double-counting of multi-region orders
--   - Performance improved from 25s to 8s
--   - Breaking Change: Now requires @start_date parameter
--
-- v1.5 (2024-01-10) - Alex Rodriguez
--   - Added return order filtering (exclude from revenue)
--   - New column: refund_rate calculation
--
-- v1.0 (2024-01-01) - Analytics Team
--   - Initial implementation
--   - Baseline: 45 seconds for full year
-- ============================================================================
```

Cross-reference related queries:

```
-- RELATED QUERIES
-- ============================================================================
-- Similar Queries:
--   - monthly_revenue_summary.sql (aggregated monthly view)
--   - customer_lifetime_value.sql (per-customer revenue)
--   - regional_performance_analysis.sql (detailed regional breakdown)
--
-- Dependent Queries:
--   - executive_dashboard.sql (uses this for KPI dashboard)
--   - finance_reconciliation.sql (uses for monthly close)
--
-- Successor Queries:
--   - daily_revenue_by_region_v3.sql (planned enhancement)
-- ============================================================================
```

### Quick Reference for Query Users

Help users understand appropriate use cases:

```
When to Use:
- Daily revenue reporting for finance
- Regional performance comparisons
- Month-over-month trends
- Executive dashboard KPI generation

When NOT to Use:
- Real-time revenue tracking (data delayed 1 hour)
- Detailed transaction-level analysis
- Customer-specific revenue analysis
- Refund/return detailed tracking
```

Document pitfalls users encounter:

```
Common Mistakes:
- Forgetting to set @start_date and @end_date
  → Results will be empty or only show current day

- Filtering on @region_filter = 'north'
  → Region names are uppercase: 'NORTH'
  → Use NULL to get all regions, not 'ALL'

- Running full year without LIMIT
  → Runs for 30+ seconds, may timeout
  → First run-through on range, then full query

- Assuming NULL values are zero
  → Missing data in some regions shows NULL
  → Must handle in reporting tool or use COALESCE
```

Include solutions to common issues:

```
Query Times Out:
  Problem: Query exceeds 30 second timeout
  Solution: Add date filters for smaller date range
  Example: Use single month instead of full year

Query Returns Unexpected Results:
  Problem: Row count doesn't match previous runs
  Solution: Check if test orders included
  Debug: Add WHERE exclude_test = false to see test data

Query Uses Wrong Index:
  Problem: Query runs slower than expected
  Solution: Run ANALYZE on tables first
  Force Index: Add FORCE INDEX (idx_sales_date_region)
```

## Operational Runbooks

### Creating Runbooks for Common Tasks

Runbooks are step-by-step procedures for recurring operational tasks.

**Runbook Template:**

```
# Runbook: Monthly Data Refresh Procedure

## Overview
This runbook describes the monthly data refresh process that imports external data,
validates integrity, and updates reporting tables.

**Frequency**: Monthly, first Tuesday at 2 AM UTC
**Duration**: 15-45 minutes
**Owner**: Data Engineering Team

## Pre-Execution Checklist
- [ ] Scheduled backup completed successfully
- [ ] Development/staging environment available for testing
- [ ] All team members notified of execution
- [ ] Rollback plan reviewed and tested
- [ ] External data source confirmed available
- [ ] Database resources monitored and available

## Step-by-Step Procedure

### Phase 1: Preparation (5 minutes)
1. Connect to production database as data_engineer role
2. Create backup of staging schema:
   ```
   CREATE SCHEMA staging_backup_$(date +%Y%m%d) AS SELECT * FROM staging;
   ```
3. Verify disk space > 50GB available:
   ```
   SELECT pg_database_size('myapp') as current_size;
   ```
4. Check for long-running queries:
   ```
   SELECT pid, usename, query, query_start
   FROM pg_stat_activity
   WHERE state = 'active'
     AND query_start < NOW() - INTERVAL '5 minutes'
   ORDER BY query_start;
   ```

### Phase 2: Data Import (10-20 minutes)
1. Download external data file from SFTP
   ```
   sftp -i key.pem user@sftp.example.com
   cd /exports
   get customer_data_$(date +%Y%m%d).csv
   ```
2. Load data into staging table:
   ```
   COPY staging.customer_import
   FROM '/tmp/customer_data_20240115.csv'
   WITH (FORMAT csv, HEADER true, DELIMITER ',');
   ```
3. Monitor import progress:
   ```
   SELECT COUNT(*) as imported_count FROM staging.customer_import;
   ```

### Phase 3: Data Validation (10-15 minutes)
1. Run validation checks:
   ```
   CALL staging.validate_imported_data();
   ```
2. Compare row counts:
   ```
   SELECT
     (SELECT COUNT(*) FROM staging.customer_import) as imported,
     (SELECT COUNT(*) FROM staging.customer_current) as previous;
   ```
3. Verify data quality:
   ```
   SELECT * FROM staging.data_quality_report
   WHERE validation_status = 'FAILED';
   ```

### Phase 4: Integration (5-10 minutes)
1. Begin transaction:
   ```
   BEGIN;
   ```
2. Backup current production table:
   ```
   CREATE TABLE customer_backup_$(date +%Y%m%d) AS
   SELECT * FROM customer;
   ```
3. Swap staging data to production:
   ```
   DELETE FROM customer;
   INSERT INTO customer SELECT * FROM staging.customer_import;
   ```
4. Update refresh timestamp:
   ```
   UPDATE database_metadata
   SET last_refresh = CURRENT_TIMESTAMP
   WHERE table_name = 'customer';
   ```
5. Commit changes:
   ```
   COMMIT;
   ```

### Phase 5: Verification (5 minutes)
1. Verify production data integrity:
   ```
   SELECT * FROM public.integrity_check_results();
   ```
2. Run dependent reporting queries:
   ```
   SELECT COUNT(*) FROM daily_revenue_summary;
   ```
3. Check for orphaned records:
   ```
   SELECT COUNT(*) FROM customer c
   LEFT JOIN orders o ON c.id = o.customer_id
   WHERE o.id IS NULL AND o.id IS NOT NULL;
   ```

### Phase 6: Post-Execution (5 minutes)
1. Archive import file
2. Notify stakeholders of successful completion
3. Monitor application logs for errors
4. Retain backup for 7 days minimum
5. Document actual execution time

## Success Criteria
- [ ] Row count matches expected import count (within 5%)
- [ ] All data quality checks pass
- [ ] Production queries run in baseline time
- [ ] No errors in application logs
- [ ] Stakeholders confirm data appears correct

## Troubleshooting

### Issue: Data import hangs
**Solution**: Check disk space, increase work_mem parameter
```
SET work_mem = '512MB';
```

### Issue: Data validation fails
**Solution**: Compare with previous month's data
```
SELECT * FROM staging.validation_errors
ORDER BY error_type, error_count DESC;
```

### Issue: Need to rollback
**Solution**: Restore from backup table
```
BEGIN;
DELETE FROM customer;
INSERT INTO customer SELECT * FROM customer_backup_20240115;
COMMIT;
```

## Post-Incident Review
Document any issues that occurred and actions taken.
Template:
- What went wrong?
- Root cause?
- How was it resolved?
- Preventive measures for future?
- Lessons learned?
```

## Health Check Runbook

### Daily Health Checks

```
# Daily Health Check Runbook

**Time**: 8 AM UTC each business day
**Duration**: 10-15 minutes
**Responsible**: On-call DBA

## Checks to Perform

### 1. Database Connectivity
```
SELECT version();
SELECT current_database();
```
Expected: Connection successful, production database active

### 2. Disk Space Monitoring
```
SELECT
  datname,
  pg_size_pretty(pg_database_size(datname)) as size
FROM pg_database
WHERE datname NOT IN ('postgres', 'template0', 'template1');
```
Action: Alert if usage > 80% of available space

### 3. Connection Pool Status
```
SELECT
  COUNT(*) as total_connections,
  state,
  COUNT(*) as count
FROM pg_stat_activity
GROUP BY state;
```
Expected: Active connections < 50, Idle connections < 10

### 4. Recent Errors
```
SELECT COUNT(*) FROM database_logs
WHERE level = 'ERROR'
  AND created_at > NOW() - INTERVAL '24 hours';
```
Action: Investigate if error count > baseline

### 5. Backup Verification
```
ls -lh /backups/latest/
```
Expected: Latest backup completed in last 26 hours
```

## Incident Response Runbook

### Database Down Investigation

```
# Incident: Database Unreachable

**Severity**: CRITICAL
**Impact**: No database access, application offline
**Time to Resolution Target**: 15 minutes

## Immediate Actions (First 5 minutes)

1. [ ] Confirm database is actually down
   ```
   telnet db-host 5432
   pg_isready -h db-host
   ```

2. [ ] Check connectivity from multiple locations

3. [ ] Check service status
   ```
   systemctl status postgresql
   sudo ps aux | grep postgres
   ```

4. [ ] Check logs
   ```
   tail -f /var/log/postgresql/postgresql.log
   ```

5. [ ] Notify team immediately
   - Post in incident channel
   - Alert on-call manager
   - Update status page

## Investigation (5-15 minutes)

- [ ] Check system resources (CPU, memory, disk)
- [ ] Review recent connections
- [ ] Check for locks or hanging transactions
- [ ] Review recent changes
- [ ] Contact hosting provider if infrastructure issue

## Recovery Steps

- [ ] Restart PostgreSQL service
- [ ] Verify replication status (if applicable)
- [ ] Verify backups integrity
- [ ] Test connectivity

## Post-Incident

- [ ] Document timeline of events
- [ ] Identify root cause
- [ ] Create preventive measures
- [ ] Schedule follow-up review
```

## Documentation Maintenance

### Keeping Documentation Current

Outdated documentation is worse than no documentation. Establish regular review processes to keep all documentation accurate and current.

**Documentation Review Schedule:**

```
Monthly:
  - Query documentation (update query versions)
  - Performance baselines (compare actual vs documented)
  - Runbook procedures (verify still accurate)

Quarterly:
  - Schema documentation (verify all tables documented)
  - Role documentation (check if roles still exist)
  - Access policies (verify implementations)

Annually:
  - Comprehensive documentation audit
  - Retire obsolete queries and procedures
  - Update historical information
  - Verify links and cross-references
```

### Documentation Tools and Templates

**File Organization:**

```
docs/
├── README.md              (overview, table of contents)
├── schemas/
│   ├── schema_overview.md
│   ├── entity_relationships.md
│   └── tables/
│       ├── customers.md
│       ├── orders.md
│       └── products.md
├── queries/
│   ├── query_guide.md
│   └── library/
├── procedures/
│   ├── daily_maintenance.md
│   ├── monthly_refresh.md
│   ├── incident_response.md
│   └── disaster_recovery.md
└── standards/
    ├── naming_conventions.md
    ├── documentation_standards.md
    └── security_policies.md
```

## Documentation Checklist

**Schema Documentation:**

- [ ] Every table has documented purpose
- [ ] Every significant column is documented
- [ ] Foreign keys explained with relationships
- [ ] Data retention policies documented
- [ ] Update frequency specified
- [ ] Access restrictions noted
- [ ] Known issues documented
- [ ] Related tables cross-referenced

**Query Documentation:**

- [ ] Purpose and business logic clear
- [ ] Parameters documented with examples
- [ ] Expected runtime and row count documented
- [ ] Performance baseline established
- [ ] Change history maintained
- [ ] Related queries cross-referenced
- [ ] Common mistakes documented
- [ ] Troubleshooting guide provided

**Operational Documentation:**

- [ ] Daily health check procedure documented
- [ ] Monthly maintenance procedure documented
- [ ] Emergency response procedures ready
- [ ] Disaster recovery tested
- [ ] Escalation procedures defined
- [ ] Contact information current
- [ ] Success criteria clear
- [ ] Troubleshooting steps included

**Ongoing Maintenance:**

- [ ] Documentation reviewed monthly
- [ ] Links and references updated
- [ ] Obsolete procedures removed
- [ ] New procedures added promptly
- [ ] Team knowledge captured
- [ ] Runbooks tested regularly
- [ ] Feedback incorporated
- [ ] Version history maintained

## Summary

Comprehensive documentation transforms database management from tribal knowledge into repeatable, scalable practice. Well-documented schemas help new team members understand data structure quickly. Documented queries enable safe code reuse and prevent duplicated effort. Operational runbooks ensure consistency and reduce incident response time. By investing in thorough documentation and maintaining it diligently, you create an information resource that multiplies team productivity and ensures continuity even as team members change. WhoDB's ability to export queries and track history makes it an excellent foundation for building and maintaining your documentation repository.

---


# resources/faq.mdx
## Frequently Asked Questions

Find answers to commonly asked questions about WhoDB

# Frequently Asked Questions

Find answers to the most commonly asked questions about WhoDB. If you can't find what you're looking for, check our [Troubleshooting Guide](/resources/troubleshooting) or reach out to our community.

## Installation & Setup

WhoDB can be installed in multiple ways depending on your environment:

**Docker (Recommended)**
```
docker run -it -p 8080:8080 clidey/whodb
```

**Docker Compose**
```
version: "3.8"
services:
  whodb:
    image: clidey/whodb
    ports:
      - "8080:8080"
```

**Binary Installation**
Download the latest binary from the [GitHub Releases](https://github.com/clidey/whodb) page and run it directly.

**Desktop Application**
Download the desktop application for Windows, macOS, or Linux from our website.

Docker is the recommended installation method as it provides the most consistent experience across platforms

WhoDB is designed to be lightweight and run on minimal resources:

**Minimum Requirements:**
- CPU: 1 core
- RAM: 512 MB
- Storage: 50 MB for the application
- OS: Linux, macOS, Windows, or any Docker-compatible platform

**Recommended Requirements:**
- CPU: 2 cores or more
- RAM: 1 GB or more
- Storage: 100 MB or more
- OS: Latest version of your operating system

**Browser Requirements:**
- Chrome 90+
- Firefox 88+
- Safari 14+
- Edge 90+

WhoDB runs entirely in your browser, so a modern, up-to-date browser is essential for the best experience

Yes, WhoDB can run as a standalone binary without Docker. Download the appropriate binary for your platform from the GitHub Releases page and execute it directly.

```
# Linux/macOS
./whodb

# Windows
whodb.exe
```

The binary includes everything needed to run WhoDB, including the embedded frontend.

The standalone binary is a single file under 50MB, making it easy to deploy and manage

**Docker:**
```
docker pull clidey/whodb:latest
docker stop whodb
docker rm whodb
docker run -it -p 8080:8080 clidey/whodb
```

**Docker Compose:**
```
docker-compose pull
docker-compose up -d
```

**Binary:**
Download the latest binary from GitHub Releases and replace your existing binary.

**Desktop Application:**
The desktop app will notify you when updates are available and can auto-update.

Always backup your configuration and connection profiles before updating

WhoDB runs on port 8080 by default. You can change this by:

**Docker:**
```
docker run -it -p 3000:8080 clidey/whodb
```

**Environment Variable:**
```
PORT=3000 whodb
```

**Docker Compose:**
```
environment:
  - PORT=3000
ports:
  - "3000:3000"
```

Make sure your chosen port is not already in use by another application

Yes, you can run multiple instances of WhoDB simultaneously by using different ports:

```
docker run -it -p 8080:8080 --name whodb1 clidey/whodb
docker run -it -p 8081:8080 --name whodb2 clidey/whodb
```

This is useful when you want to:
- Connect to different databases in separate instances
- Provide isolated environments for different teams
- Test different WhoDB versions side by side

Each instance maintains its own connection history and settings

## Database Connections

**Community Edition (CE):**
- PostgreSQL
- MySQL
- MariaDB
- SQLite3
- MongoDB
- Redis
- Elasticsearch
- ClickHouse

**Enterprise Edition (EE):**
All CE databases plus:
- Oracle Database
- Microsoft SQL Server
- Amazon DynamoDB
- Amazon Athena
- Snowflake
- And more

See our [Supported Databases](/resources/supported-databases) page for detailed version compatibility.

The Community Edition covers the most popular open-source databases, while Enterprise Edition adds support for commercial and cloud-native databases

Access WhoDB at http://localhost:8080

Choose your database type from the dropdown menu

Fill in the host, port, username, password, and database name

Set SSL, SSH tunneling, or connection pooling options if needed

Click "Connect" to establish the connection

Connection details are saved locally in your browser for quick reconnection

Yes, WhoDB can connect to any database accessible from your network:

**Direct Connection:**
Simply use the remote host's IP address or hostname:
```
Host: 192.168.1.100 or db.example.com
Port: 5432
```

**SSH Tunnel:**
For secure connections through SSH:
```
SSH Host: bastion.example.com
SSH Port: 22
SSH User: ubuntu
Database Host: localhost (from SSH host perspective)
Database Port: 5432
```

**SSL/TLS:**
Enable SSL for encrypted connections:
```
Enable SSL: Yes
SSL Mode: require/verify-ca/verify-full
SSL Certificate: (optional)
```

Always use secure connections (SSL/SSH) when connecting to databases over public networks

When running WhoDB in Docker, "localhost" refers to the container itself, not your host machine. Use one of these approaches:

**On macOS/Windows:**
```
Host: host.docker.internal
```

**On Linux:**
```
docker run -it -p 8080:8080 --network host clidey/whodb
```

**Using Docker Network:**
```
version: "3.8"
services:
  postgres:
    image: postgres:15
    networks:
      - whodb-network
  whodb:
    image: clidey/whodb
    networks:
      - whodb-network
networks:
  whodb-network:
```

Then use the service name as the host:
```
Host: postgres
Port: 5432
```

Docker networking can be complex. See our [Docker integration guide](/resources/integrations/docker) for detailed examples

Yes, WhoDB automatically saves your connection history in your browser's local storage. When you return to the login page, you can select from your recent connections.

Features:
- Automatically saves connection details (except passwords for security)
- Quick reconnect from connection history
- Edit saved connections
- Delete old connections
- Export/import connection profiles

For security reasons, passwords are never saved. You'll need to re-enter them when reconnecting

WhoDB supports several authentication methods:

**Username/Password:**
Standard authentication for most databases

**Certificate-Based Authentication:**
For databases requiring client certificates:
- Upload your client certificate
- Provide certificate key
- Add CA certificate if needed

**SSH Key Authentication:**
For SSH tunnel connections:
- Provide your SSH private key
- Support for encrypted keys with passphrase

**Environment Variables:**
For automated deployments:
```
docker run -e DB_HOST=... -e DB_USER=... -e DB_PASS=... clidey/whodb
```

Check your database provider's documentation for specific authentication requirements

**Local SQLite File:**
```
docker run -it -p 8080:8080 -v /path/to/db:/db clidey/whodb
```

Then connect with:
```
Database Type: SQLite3
Database Path: /db/your-database.db
```

**Without Docker:**
Simply specify the full path to your SQLite file:
```
Database Path: /Users/username/data/app.db
```

SQLite files must be accessible to the WhoDB process. Use volume mounts for Docker deployments

## Features & Usage

Yes, WhoDB provides comprehensive data editing capabilities:

**Add New Rows:**
- Click "Add Row" button
- Fill in the form fields
- Submit to insert

**Edit Existing Rows:**
- Right-click any row
- Select "Edit Row"
- Modify values
- Save changes

**Delete Rows:**
- Right-click any row
- Select "Delete Row"
- Confirm deletion

**Inline Editing:**
- Double-click any cell
- Edit value directly
- Press Enter to save

All data modifications are immediate and cannot be undone. Always backup before making bulk changes

WhoDB supports multiple export formats:

Choose what to export:
- All data in the table
- Selected rows only
- Filtered/searched results

Available formats:
- CSV (Comma-separated values)
- Excel (.xlsx)
- JSON
- SQL INSERT statements

Set format-specific options:
- Delimiter (for CSV)
- Include headers
- Date format
- Encoding

Click "Export" to download the file

Large exports may take some time. WhoDB will show a progress indicator for operations over 10,000 rows

The Scratchpad is WhoDB's powerful query interface, similar to Jupyter notebooks for databases:

**Features:**
- Write and execute SQL queries
- Syntax highlighting and auto-completion
- Multiple independent cells
- Query history tracking
- Result visualization
- Export query results

**Use Cases:**
- Ad-hoc data analysis
- Testing complex queries
- Creating data reports
- Debugging application queries
- Learning SQL

**Keyboard Shortcuts:**
- `Cmd/Ctrl + Enter`: Execute query
- `Cmd/Ctrl + N`: New cell
- `Cmd/Ctrl + D`: Duplicate cell
- `Cmd/Ctrl + S`: Save query

Use multiple cells to organize related queries and execute them independently

The Graph view creates an interactive visualization of your database schema:

**What It Shows:**
- Tables as nodes
- Foreign key relationships as edges
- Primary keys and indexes
- Column data types
- Relationship cardinality

**Interactions:**
- Click and drag to pan
- Mouse wheel to zoom
- Click nodes to see details
- Click edges to see relationship details
- Fit view to center all nodes

**Benefits:**
- Understand complex schemas quickly
- Identify relationship patterns
- Find orphaned tables
- Document database structure
- Plan schema changes

Graph visualization works best with databases that have defined foreign key relationships

Yes, WhoDB includes a mock data generator:

Navigate to the table you want to populate

Click "Generate Mock Data" button

- Number of rows (up to 10,000)
- Mode: Append or Overwrite
- Auto-generate based on column types

Click "Generate" to create the data

**Supported Data Types:**
- Text: Names, emails, addresses, paragraphs
- Numbers: Integers, decimals, ranges
- Dates: Past, future, relative dates
- Booleans: True/false values
- UUIDs: Valid unique identifiers
- JSON: Nested objects

Overwrite mode will delete all existing data in the table before inserting mock data

WhoDB executes queries in the context provided by your database connection:

**Auto-Commit Mode:**
Most operations execute immediately without explicit transactions:
- Single row inserts
- Single row updates
- Single row deletes
- SELECT queries

**Manual Transactions:**
For complex operations, use transaction commands in the Scratchpad:
```
BEGIN;
UPDATE users SET status = 'active' WHERE created_at < '2024-01-01';
UPDATE orders SET processed = true WHERE user_id IN (SELECT id FROM users WHERE status = 'active');
COMMIT;
```

**Rollback on Error:**
If a query fails, previous successful operations in the same transaction are rolled back automatically.

Use explicit transactions in Scratchpad for multi-step operations that should succeed or fail together

Yes, WhoDB fully supports multiple schemas:

**PostgreSQL:**
- Select schema from sidebar
- View all schemas in database
- Query across schemas using qualified names

**MySQL:**
- Each database acts as a schema
- Switch between databases
- Cross-database queries supported

**MongoDB:**
- Collections organized by database
- Switch between databases
- Query multiple databases

Schema support varies by database type. Check our [Supported Databases](/resources/supported-databases) page for details

WhoDB provides multiple search capabilities:

**Quick Search:**
- Type in the search box to search all visible columns
- Real-time filtering
- Highlights matching text

**WHERE Conditions:**
- Complex filtering with SQL operators
- Multiple conditions with AND/OR
- Operators: `=`, `!=`, `>`, `<`, `>=`, `<=`, `LIKE`, `IN`

**Full-Text Search:**
- Available for supported databases
- Search across all text columns
- Relevance ranking

**Advanced Query:**
- Use Scratchpad for complex searches
- JOIN multiple tables
- Aggregate and group results

Combine WHERE conditions with sorting for powerful data exploration

## AI & Chat Features

WhoDB integrates with multiple AI providers:

**Ollama (Local):**
```
# Install Ollama from https://ollama.com
ollama pull llama3.1
```
WhoDB auto-detects Ollama on localhost:11434

**OpenAI:**
```
docker run -e WHODB_OPENAI_API_KEY=your-key clidey/whodb
```

**Anthropic:**
```
docker run -e WHODB_ANTHROPIC_API_KEY=your-key clidey/whodb
```

Once configured, the Chat feature appears in the sidebar.

Ollama runs entirely on your machine, keeping your data private. Cloud providers (OpenAI/Anthropic) send queries to their APIs

The AI assistant can help with:

**Query Generation:**
- "Show me all users created in the last 30 days"
- "Find the top 10 products by sales"
- "Get average order value by month"

**Data Analysis:**
- "What's the distribution of user ages?"
- "Are there any anomalies in the sales data?"
- "Summarize the orders table"

**Schema Understanding:**
- "What tables are related to users?"
- "Explain the relationship between orders and customers"
- "What are the primary keys in this database?"

**Optimization:**
- "How can I make this query faster?"
- "What indexes should I add?"
- "Why is this query slow?"

The AI has full context of your database schema, so ask natural questions without specifying table names

**Ollama (Local):**
- Runs entirely on your machine
- No data leaves your computer
- Complete privacy

**OpenAI/Anthropic:**
- Query text and schema information are sent to their APIs
- Actual data values are not sent unless explicitly mentioned in your question
- Protected by API provider's privacy policies

**What's Sent:**
- Table and column names
- Data types and relationships
- Your question text
- Generated SQL queries

**What's NOT Sent:**
- Actual row data
- Connection credentials
- User passwords
- Database contents

If you're working with sensitive data, use Ollama for complete privacy

By default, the AI assistant can:
- Generate SELECT queries (read-only)
- Suggest INSERT/UPDATE/DELETE queries
- Explain query logic

The AI will **not** automatically execute modification queries. You must:
1. Review the generated query
2. Copy it to Scratchpad
3. Manually execute it

This safety mechanism prevents accidental data loss or corruption

## Security & Privacy

WhoDB implements multiple security measures:

**Connection Security:**
- SSL/TLS support for database connections
- SSH tunneling support
- Certificate-based authentication
- Encrypted credentials storage

**Application Security:**
- No data persistence on server
- All data processed in-memory
- Secure session management
- XSS and CSRF protection

**Deployment Security:**
- Docker security best practices
- Minimal attack surface
- Regular security updates
- Dependency scanning

WhoDB is open source, allowing security audits by the community

WhoDB follows these security principles:

**Connection Passwords:**
- Never saved permanently
- Stored only in browser session
- Cleared when you close the browser
- Not logged or persisted

**Re-authentication:**
- Required each time you connect
- Connection history saved without passwords
- Secure credential entry

Never commit connection strings with passwords to version control or share them in screenshots

WhoDB can be used in production with proper precautions:

**Recommended Practices:**
- Use read-only database accounts when possible
- Deploy behind authentication (OAuth, SSO)
- Limit network access with firewalls
- Enable SSL/TLS for all connections
- Regular security updates
- Audit logs for all operations

**Use Cases:**
- Development and staging environments
- Read-only production access for support teams
- Database administration with proper access controls
- Data analysis and reporting

Never expose WhoDB directly to the public internet without authentication and access controls

**Network Security:**
```
# Docker Compose with network isolation
version: "3.8"
services:
  whodb:
    image: clidey/whodb
    networks:
      - internal
networks:
  internal:
    driver: bridge
```

**Reverse Proxy with Authentication:**
```
location /whodb {
    auth_basic "WhoDB Access";
    auth_basic_user_file /etc/nginx/.htpasswd;
    proxy_pass http://localhost:8080;
}
```

**Firewall Rules:**
```
# Only allow specific IPs
iptables -A INPUT -p tcp --dport 8080 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 8080 -j DROP
```

**Container Security:**
```
# Run as non-root user
docker run --user 1000:1000 clidey/whodb
```

Combine multiple security layers for defense in depth

WhoDB logging behavior:

**Application Logs:**
- Connection attempts (without passwords)
- Error messages
- Performance metrics
- Feature usage (anonymous)

**Query Logs:**
- Not logged by WhoDB server
- Saved in browser history (client-side only)
- Can be cleared by user
- Never transmitted to external servers

**Database Logs:**
- Your database may log queries independently
- Check your database's logging configuration
- Not controlled by WhoDB

Browser history is local to your machine and not shared with WhoDB servers

## Pricing & Licensing

WhoDB offers two editions:

**Community Edition (CE):**
- Completely free and open source
- Supports 8 popular databases
- All core features included
- No usage limits
- MIT License

**Enterprise Edition (EE):**
- Commercial license
- Additional database support
- Advanced features
- Priority support
- Custom deployments

The Community Edition is suitable for most users and projects

**Community Edition:**
- PostgreSQL, MySQL, MariaDB, SQLite
- MongoDB, Redis, Elasticsearch, ClickHouse
- All basic features
- Community support
- Open source

**Enterprise Edition:**
- All CE databases plus:
  - Oracle Database
  - Microsoft SQL Server
  - Amazon DynamoDB
  - Amazon Athena
  - Snowflake
  - And more
- Advanced visualizations
- Enhanced UI components
- Priority support
- Commercial license
- Custom integrations

Start with Community Edition and upgrade to Enterprise when you need additional database support

**Community Edition:**
- Yes, with MIT License
- Use for commercial projects
- No attribution required
- Modify and distribute freely
- No warranty provided

**Enterprise Edition:**
- Requires commercial license
- Contact sales for pricing
- Includes support and updates
- Custom license terms available

Check the LICENSE file in the repository for full legal terms

**Community Edition:**
- GitHub Issues for bug reports
- GitHub Discussions for questions
- Community Discord/Slack
- Documentation and guides

**Enterprise Edition:**
- Priority email support
- Dedicated Slack channel
- Custom onboarding
- SLA guarantees
- Direct engineer access

**Community Resources:**
- Documentation: https://docs.whodb.com
- GitHub: https://github.com/clidey/whodb
- Email: support@clidey.com

The community is active and responsive. Most questions get answered within 24 hours

## Performance & Optimization

WhoDB implements several performance optimizations:

**Lazy Loading:**
- Only loads visible rows
- Pagination with configurable page size
- Virtual scrolling for large result sets

**Query Optimization:**
- Automatic LIMIT clause addition
- Index usage hints
- Query result caching

**Frontend Performance:**
- Table virtualization
- Debounced search
- Optimized rendering

**Best Practices:**
- Use WHERE conditions to filter data
- Limit result sets to necessary columns
- Add database indexes for frequently queried columns
- Use pagination instead of loading all rows

For tables with millions of rows, always use WHERE conditions and LIMIT clauses

Common causes and solutions:

**Missing Indexes:**
```
-- Check if columns used in WHERE have indexes
CREATE INDEX idx_users_email ON users(email);
```

**Large Result Sets:**
```
-- Add LIMIT clause
SELECT * FROM orders LIMIT 1000;
```

**Complex JOINs:**
- Reduce number of joined tables
- Use indexes on join columns
- Filter early with WHERE conditions

**Network Latency:**
- Use connection pooling
- Enable compression
- Deploy WhoDB closer to database

See our [Performance Tuning Guide](/resources/performance-tuning) for detailed optimization strategies.

Use the Scratchpad to run EXPLAIN on slow queries to understand execution plans

Yes, configure connection pooling:

```
environment:
  - DB_MAX_CONNECTIONS=100
  - DB_MAX_IDLE_CONNECTIONS=10
  - DB_CONNECTION_MAX_LIFETIME=3600
```

**Parameters:**
- `DB_MAX_CONNECTIONS`: Maximum total connections
- `DB_MAX_IDLE_CONNECTIONS`: Idle connection pool size
- `DB_CONNECTION_MAX_LIFETIME`: Connection lifetime in seconds

Be careful not to exceed your database server's connection limits

WhoDB implements selective caching:

**Cached:**
- Schema metadata
- Table lists
- Column definitions
- Foreign key relationships

**Not Cached:**
- Query results
- Table data
- Row counts
- User modifications

**Cache Duration:**
- Schema cache: 5 minutes
- Manual refresh available
- Automatically refreshed on schema changes

Caching reduces database load while ensuring data freshness

## Migration & Integration

Yes, WhoDB is compatible with existing workflows:

**From phpMyAdmin:**
- Use same MySQL credentials
- Import existing connection configs
- Similar interface layout
- See our [Migration Guide](/guides/migrating-from-phpmyadmin)

**From pgAdmin:**
- Use same PostgreSQL credentials
- Import server configurations
- Query tool is similar to Scratchpad
- See our [Migration Guide](/guides/migrating-from-pgadmin)

**From DBeaver:**
- Export connection configs
- Import into WhoDB
- Migrate saved queries
- Use WhoDB as lighter alternative

WhoDB can run alongside existing tools during your transition period

Yes, WhoDB works well in automated environments:

**Docker in CI/CD:**
```
# GitHub Actions example
- name: Run database tests
  run: |
    docker run -d -p 8080:8080 clidey/whodb
    # Run your tests
```

**Automated Testing:**
- Spin up WhoDB in test environments
- Run data validation queries
- Export test results
- Generate reports

**Database Migrations:**
- Verify schema changes
- Test migrations
- Validate data integrity

See our [CI/CD Integration Guide](/resources/integrations/ci-cd) for detailed examples.

WhoDB's lightweight design makes it perfect for temporary test environments

WhoDB provides several backup options:

**Export Table Data:**
1. Navigate to the table
2. Click "Export"
3. Choose SQL format
4. Download INSERT statements

**Export Schema:**
- Use Scratchpad to export DDL:
```
-- PostgreSQL
pg_dump --schema-only database_name

-- MySQL
mysqldump --no-data database_name
```

**Automated Backups:**
```
# Script to export all tables
for table in $(whodb-cli list-tables); do
  whodb-cli export $table > backup_$table.sql
done
```

WhoDB exports are suitable for small to medium datasets. Use database-native tools for large-scale backups

Yes, WhoDB can integrate with monitoring systems:

**Prometheus Metrics:**
```
# Coming in future releases
whodb_queries_total
whodb_connection_errors_total
whodb_query_duration_seconds
```

**Logging Integration:**
```
environment:
  - LOG_LEVEL=info
  - LOG_FORMAT=json
```

**Health Checks:**
```
curl http://localhost:8080/health
```

See our [Monitoring Integration Guide](/resources/integrations/monitoring) for detailed setup.

Health check endpoints are useful for load balancer configuration

## Troubleshooting

**Checklist:**
1. Verify connection details (host, port, username, password)
2. Check database is running and accessible
3. Verify firewall rules allow connection
4. Test connection from command line
5. Check SSL/TLS requirements
6. Verify user permissions

**Common Issues:**
- Wrong port number (PostgreSQL: 5432, MySQL: 3306)
- Localhost vs host.docker.internal in Docker
- SSL required but not enabled
- User doesn't have remote access permissions

See our [Troubleshooting Guide](/resources/troubleshooting) for detailed solutions.

Use `telnet host port` or `nc -zv host port` to test basic network connectivity

**Checklist:**
1. Verify WhoDB is running: `docker ps` or process check
2. Check port mapping is correct
3. Try accessing http://localhost:8080
4. Check firewall allows local connections
5. Verify no other service is using port 8080
6. Check browser console for errors

**Common Solutions:**
```
# Check if port is in use
lsof -i :8080

# Kill process using the port
kill -9 

# Use different port
docker run -p 3000:8080 clidey/whodb
```

Some corporate networks block certain ports. Try using standard ports like 80 or 443

**Common Causes:**

**Syntax Errors:**
- Check SQL syntax for your database
- Different databases have different syntax
- Use database-specific functions

**Permission Issues:**
```
-- Check your permissions
SHOW GRANTS; -- MySQL
\dp -- PostgreSQL
```

**Connection Timeout:**
- Increase timeout settings
- Check network stability
- Verify database is responsive

**Resource Limits:**
- Query too complex
- Result set too large
- Memory constraints

Copy the error message and search it along with your database type for specific solutions

Search [GitHub Issues](https://github.com/clidey/whodb/issues) for similar reports

- WhoDB version
- Database type and version
- Operating system
- Browser type and version
- Steps to reproduce
- Error messages

Open a new issue on GitHub with detailed information

Include screenshots, logs, and configuration (without sensitive data)

The more details you provide, the faster we can resolve the issue

## Still Have Questions?

If you couldn't find the answer you're looking for:

Detailed solutions for common issues

Error messages and their solutions

Ask the community

Email us for help

---


# resources/troubleshooting.mdx
## Troubleshooting Guide

Solutions for common WhoDB issues including connection problems, performance issues, and UI problems

# Troubleshooting Guide

This guide helps you resolve common issues when using WhoDB. Follow the step-by-step solutions to diagnose and fix problems.

## Connection Issues

### Symptoms
- Error: "Connection refused"
- Error: "Connection timeout"
- WhoDB displays "Unable to connect" message

### Step-by-Step Solution

Check if the database service is running on the target server:

**PostgreSQL:**
```
pg_isready -h hostname -p 5432
```

**MySQL:**
```
mysqladmin -h hostname -u username ping
```

**MongoDB:**
```
mongosh --eval "db.adminCommand('ping')"
```

Test basic network connectivity:

**macOS/Linux:**
```
telnet hostname 5432
nc -zv hostname 5432
```

**Windows:**
```
telnet hostname 5432
```

If connection is refused, the host or port is incorrect.

Double-check:
- **Host**: Correct hostname or IP address
- **Port**: Default ports (PostgreSQL: 5432, MySQL: 3306, MongoDB: 27017)
- **Database name**: Correct for SQL databases
- **Username/Password**: Correct credentials

**Docker users**: If running WhoDB in Docker and connecting to localhost, use `host.docker.internal` instead.

Ensure firewall allows outbound connections:

**Linux/macOS:**
```
# Check if port is open
lsof -i :5432

# Allow port through firewall
sudo ufw allow 5432/tcp
```

**Windows:**
Check Windows Defender Firewall settings for the application.

If the database is slow to respond, increase timeout:

```
environment:
  - DB_CONNECTION_TIMEOUT=30
  - DB_QUERY_TIMEOUT=60
```

### Common Scenarios

**"Can't connect to local server"**
- Check if database service is running
- Verify correct port for the service

**"No route to host"**
- Network connectivity issue
- Check VPN or network configuration
- Verify hostname resolves: `ping hostname`

**"Connection reset by peer"**
- Database server rejected connection
- Check authentication credentials
- Verify user permissions

### Symptoms
- Error: "SSL certificate problem"
- Error: "SSL handshake failure"
- Connection works without SSL but fails with SSL enabled

### Step-by-Step Solution

Check your database configuration to determine if SSL is mandatory or optional. If optional, try connecting without SSL first.

```
openssl s_client -connect hostname:5432 -starttls postgres
```

This shows certificate details and any validation issues.

- **disable**: No SSL (local development only)
- **require**: SSL required, certificate not verified
- **verify-ca**: SSL required, CA verified
- **verify-full**: SSL required, CA and hostname verified

Start with "require" if unsure.

If using "verify-ca" or "verify-full", you may need to provide the CA certificate:

```
SSL Mode: verify-ca
SSL CA Certificate: /path/to/ca-cert.pem
```

Download the certificate from your database provider.

Verify the connection works outside WhoDB:

**PostgreSQL:**
```
psql -h hostname -U username -d database \
  -sslmode=require
```

**MySQL:**
```
mysql -h hostname -u username --ssl-mode=REQUIRED
```

If this fails, the SSL configuration is wrong.

### Common Scenarios

**Database requires SSL but WhoDB has it disabled**
- Enable SSL in connection settings
- Set appropriate SSL mode

**Self-signed certificate errors**
- Use SSL mode "require" to skip verification
- Or add the certificate to trusted store

**"Hostname doesn't match certificate"**
- Use SSL mode "verify-ca" instead of "verify-full"
- Or contact database provider for correct hostname

### Symptoms
- Error: "Authentication failed"
- Error: "Access denied for user"
- Error: "Invalid credentials"

### Step-by-Step Solution

Test credentials with the database client:

**PostgreSQL:**
```
psql -h hostname -U username -d database
```

**MySQL:**
```
mysql -h hostname -u username -p database
```

This confirms credentials are correct.

If password contains special characters, ensure they're entered correctly. Some characters may need escaping in connection strings.

Try:
- Removing special characters temporarily
- Using quotes around password
- URL-encoding the password

```
-- PostgreSQL: Check if user exists
SELECT * FROM pg_user WHERE usename = 'username';

-- MySQL: Check user and permissions
SELECT User, Host FROM mysql.user WHERE User='username';
SHOW GRANTS FOR 'username'@'hostname';
```

Ensure user has permission to connect to the specific database.

**PostgreSQL:**
```
GRANT CONNECT ON DATABASE database_name TO username;
```

**MySQL:**
```
GRANT ALL PRIVILEGES ON database.* TO 'username'@'hostname';
FLUSH PRIVILEGES;
```

Test connection with admin credentials to rule out permission issues. If admin works but your user doesn't, it's a permissions problem.

### Common Scenarios

**"Password authentication failed"**
- Verify exact password (case-sensitive)
- Confirm no extra spaces in password
- Check Caps Lock isn't enabled

**"User does not have permission"**
- User exists but lacks database access
- Grant appropriate permissions
- May need to connect to 'postgres' database first (PostgreSQL)

**"Too many connections"**
- Database connection limit reached
- Try connecting later
- Reduce concurrent connections

### Symptoms
- Error: "SSH connection failed"
- Error: "SSH key permission denied"
- Error: "SSH handshake timeout"

### Step-by-Step Solution

```
ssh -v username@ssh-host
```

The verbose output shows where connection fails.

- Username: Correct SSH username
- Password: Correct SSH password (if using password auth)
- Private key: Correct key file for SSH key auth

Test: `ssh username@ssh-host`

SSH keys must have correct permissions:

```
chmod 600 ~/.ssh/id_rsa
chmod 700 ~/.ssh
```

File must be readable only by the owner.

In WhoDB:
- SSH Host: ssh-tunnel.example.com
- SSH Port: 22 (usually)
- SSH Username: Your SSH username
- Database Host: localhost (from SSH perspective)
- Database Port: 5432

After connecting via SSH tunnel, the database appears as localhost.

```
ssh -L 5432:database-host:5432 username@ssh-host
```

Then connect WhoDB to localhost:5432 to verify tunnel works.

### Common Scenarios

**"Permission denied (publickey)"**
- SSH key not authorized on server
- Check ~/.ssh/authorized_keys on SSH server
- Verify correct key is being used

**"Connection refused"**
- SSH server not listening on port 22
- Firewall blocking SSH port
- SSH service not running

**"SSH tunnel times out"**
- SSH server unreachable
- Network firewall blocking connection
- Check intermediate routers/VPN

## Performance Issues

### Symptoms
- Queries take a long time to return results
- Table with millions of rows loads very slowly
- Export operation is slow

### Step-by-Step Solution

Use LIMIT to see if data is being returned quickly:

```
-- Test with small limit
SELECT * FROM large_table LIMIT 100;

-- If this is fast, the issue is result size
-- If this is slow, the issue is query execution
```

If LIMIT 100 is fast but full query is slow, you have too many results.

Instead of loading entire table:
```
-- Slow: Full table scan
SELECT * FROM orders;

-- Fast: Filtered results
SELECT * FROM orders WHERE created_at > NOW() - INTERVAL '30 days';
```

```
-- PostgreSQL: Check indexes
SELECT * FROM pg_indexes WHERE tablename = 'table_name';

-- MySQL: Check indexes
SHOW INDEX FROM table_name;

-- SQLite: Check indexes
PRAGMA index_list(table_name);
```

Add indexes to frequently filtered columns:
```
CREATE INDEX idx_orders_created_at ON orders(created_at);
CREATE INDEX idx_orders_user_id ON orders(user_id);
```

Use EXPLAIN to understand how database executes query:

```
-- PostgreSQL
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 123;

-- MySQL
EXPLAIN SELECT * FROM orders WHERE user_id = 123;
```

Look for sequential scans that could use indexes.

- Add indexes for columns in WHERE clause
- Use appropriate JOIN types
- Reduce number of columns selected
- Filter early before JOINs

- If local database is slow, issue is query/indexes
- If remote database is slow, may be network
- Try running same query from database server directly

### Common Scenarios

**"Table with 10M rows is too slow"**
- Add WHERE conditions
- Create appropriate indexes
- Use pagination

**"Export is slow"**
- Use LIMIT for testing
- Export in batches
- Consider SQL format (faster than CSV)

**"JOIN queries are slow"**
- Add indexes on join columns
- Check join conditions
- Consider materialized views

### Symptoms
- WhoDB process consuming excessive RAM
- System becomes unresponsive
- "Out of memory" errors

### Step-by-Step Solution

Is memory spike caused by:
- Large SELECT query returning many rows
- Exporting large dataset
- Generating mock data

Use pagination instead:
- Reduce page size in WhoDB
- Add LIMIT clause to queries
- Fetch results in batches

Export in smaller chunks:
- Use WHERE conditions to filter data
- Export by date ranges
- Export specific columns only

Reduce row count:
- Generate 1,000 rows at a time instead of 10,000
- Split generation across multiple sessions
- Increase DB_MAX_MEMORY if available

```
environment:
  - DB_MAX_MEMORY=2gb
```

Check WhoDB container/process memory:

**Docker:**
```
docker stats
```

**System:**
```
# macOS
top -o MEM

# Linux
top | grep whodb
```

If memory keeps growing, there may be a memory leak.

### Common Scenarios

**"Memory spikes when viewing large table"**
- Use pagination
- Apply filters
- Use LIMIT in query

**"Export operation runs out of memory"**
- Export in smaller batches
- Use CSV instead of JSON (usually smaller)
- Increase allocated memory

### Symptoms
- UI is sluggish
- Clicking buttons has delay
- Interface appears to freeze

### Step-by-Step Solution

Open browser developer tools (F12):
- Console tab for JavaScript errors
- Network tab to see slow requests
- Performance tab to profile

Look for:
- Failed network requests
- Long-running JavaScript
- Large resource downloads

```
# Hard refresh in browser
Ctrl+Shift+R (Windows/Linux)
Cmd+Shift+R (macOS)
```

Or clear cache and cookies in browser settings.

A slow database connection affects UI responsiveness:
- Run a simple query in Scratchpad
- Time how long it takes
- If >5 seconds, database is slow

See "Slow query performance" section above.

If UI is slow in one browser, may be browser-specific:
- Try Chrome, Firefox, Safari, Edge
- Update browser to latest version
- Disable problematic extensions

Ensure WhoDB has enough resources:

**Docker:**
```
docker stats whodb
CPU: Should be 

```
# Docker
docker restart whodb

# Binary
Kill and restart the process
```

This clears any memory leaks or stale state.

### Common Scenarios

**"UI freezes when clicking buttons"**
- Database query is running
- Network is slow
- Try reducing query result size

**"Page loads very slowly"**
- Large result set being rendered
- Slow network
- Browser performance issue

## UI and Usability Issues

### Symptoms
- Table appears empty even though data exists
- After editing, changes don't appear
- Page shows "No results" incorrectly

### Step-by-Step Solution

Look for filter badges in the toolbar:
- WHERE conditions might be filtering out all rows
- Search box might be filtering results

Clear filters to see all data:
- Click "Clear filters" button
- Reset WHERE conditions

Run query in Scratchpad:
```
SELECT COUNT(*) FROM table_name;
```

If count is 0, table is actually empty.

Columns might be hidden:
- Right-click column headers
- Select "Show All Columns"
- Verify columns are selected

Press F5 or click refresh button to reload data from database.

Data might be on another page:
- Check page number at bottom
- Verify page size setting
- Navigate between pages

Make sure still connected to database:
- Connection status should show "Connected"
- Try running a simple query
- Reconnect if needed

### Common Scenarios

**"Added data but doesn't appear"**
- Data was added to different table
- Filter is hiding new data
- Page needs refresh

**"Only seeing some columns"**
- Columns are hidden
- Scroll horizontally to see more
- Use "Show All Columns" option

### Symptoms
- "Edit Row" button doesn't work
- "Delete Row" fails
- "Add Row" doesn't insert data

### Step-by-Step Solution

Your database user may not have write permissions:

```
-- PostgreSQL: Check permissions
\dp table_name

-- MySQL: Check permissions
SHOW GRANTS FOR 'username'@'hostname';
```

Grant INSERT/UPDATE/DELETE permissions if needed.

Look at error message:
- "Permission denied" → Need database permissions
- "Constraint violation" → Primary key or foreign key issue
- "NULL in NOT NULL column" → Validation error

Each error has a specific solution.

Test if modifications work via SQL:
```
UPDATE table_name SET column = 'value' WHERE id = 123;
```

If this fails, it's a database permissions issue.
If it succeeds, it's a WhoDB UI issue.

When adding/updating rows:
- Verify all required fields are filled
- Check foreign key references exist
- Ensure values match expected types

If user lacks permissions:

**PostgreSQL:**
```
GRANT INSERT, UPDATE, DELETE ON table_name TO username;
```

**MySQL:**
```
GRANT INSERT, UPDATE, DELETE ON database.* TO 'username'@'hostname';
FLUSH PRIVILEGES;
```

Connect with admin account to verify it works. If admin can edit but your user can't, it's a permissions problem.

### Common Scenarios

**"Edit works but Delete fails"**
- User has UPDATE permission but not DELETE
- Foreign key prevents deletion
- Row has dependent records

**"All write operations fail"**
- User has SELECT-only permissions
- Need to grant INSERT/UPDATE/DELETE
- May need to use different user account

### Symptoms
- Graph view is empty or shows isolated tables
- Foreign key relationships not displayed
- Nodes aren't connected

### Step-by-Step Solution

Check if foreign key constraints exist:

**PostgreSQL:**
```
SELECT constraint_name, table_name, column_name
FROM information_schema.key_column_usage
WHERE table_schema = 'public';
```

**MySQL:**
```
SELECT CONSTRAINT_NAME, TABLE_NAME, COLUMN_NAME
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE REFERENCED_TABLE_NAME IS NOT NULL;
```

If no foreign keys exist, add them:

```
ALTER TABLE orders ADD FOREIGN KEY (user_id) REFERENCES users(id);
```

Graph visualization depends on foreign keys.

- Click "Refresh" or "Fit View" button
- Return to table view, then back to graph
- Full page refresh (F5)

If graph exists but layout is poor:
- Try different layout options
- Zoom in/out to see all relationships
- Pan around to find connected components

Some tables might be filtered:
- Verify tables you expect are visible
- Check if search/filter is active
- Select schemas to include

### Common Scenarios

**"Graph shows only one table"**
- Other tables not connected via foreign keys
- Foreign keys not defined in schema
- Tables are in different schemas

**"Graph is too cluttered"**
- Many interconnected tables
- Use zoom controls to navigate
- Filter to specific schema or tables

## Query and Scratchpad Issues

### Symptoms
- Error appears when executing query
- Error message is unclear
- Same query works in other tools

### Step-by-Step Solution

Common error patterns:
- **Syntax Error**: Query syntax is wrong
- **Permission Error**: User lacks access
- **Data Type Error**: Value doesn't match expected type
- **Constraint Error**: Violates database rule

Different databases have different SQL syntax:
- PostgreSQL: Uses `SERIAL`, `ARRAY`, JSON operators
- MySQL: Uses `AUTO_INCREMENT`, different functions
- SQLite: Limited function set
- MongoDB: Uses aggregation pipeline

Use database-specific syntax.

SQL is case-sensitive in some databases:
```
-- Check actual names
SELECT * FROM information_schema.tables;

-- Use correct case in query
SELECT * FROM "TableName";
```

User may not have permission for operation:
```
-- Test with simple SELECT first
SELECT 1;

-- If that works, issue is with specific query
```

Run same query in native database client:
```
# PostgreSQL
psql -h host -U user -d database -c "SELECT * FROM table;"

# MySQL
mysql -h host -u user -p database -e "SELECT * FROM table;"
```

If it fails there too, it's a database issue, not WhoDB.

Reduce complexity to find the issue:
```
-- Start simple
SELECT * FROM table LIMIT 1;

-- Add complexity gradually
SELECT col1, col2 FROM table WHERE col3 = 'value';
```

### Common Scenarios

**"Syntax error near [keyword]"**
- Check keyword is valid for your database
- Verify commas, quotes, parentheses
- Use IDE with syntax highlighting

**"Table or view does not exist"**
- Check table name spelling
- Verify correct database is selected
- Use schema-qualified names if needed

**"Access denied for this query"**
- User lacks SELECT/UPDATE/DELETE permissions
- Try with admin account
- Grant appropriate permissions

### Symptoms
- Execute button doesn't work
- Query doesn't run when pressing Ctrl+Enter
- Cell appears to be stuck

### Step-by-Step Solution

Cell won't execute if not connected:
- Verify "Connected" status in sidebar
- Check database connection
- Try reconnecting to database

WhoDB may be executing a previous query:
- Wait for results to appear
- Look for loading indicator
- Try stopping the query if there's a stop button

Click in the code editor to focus:
- Cursor should be in the cell
- Try pressing Ctrl+Enter to execute
- Use "Execute" button instead if needed

Very long-running queries may timeout:
- Add LIMIT clause to reduce results
- Simplify query
- Increase timeout setting if available

```
DB_QUERY_TIMEOUT=120
```

Execute basic query to verify execution:
```
SELECT 1;
```

If this works, issue is with specific query.
If this fails, issue is with connection/execution.

If one cell is stuck:
- Press Ctrl+N to create new cell
- Try query in new cell
- Delete the stuck cell

### Common Scenarios

**"Ctrl+Enter doesn't work"**
- Cell not focused (click in code area)
- Try using Execute button instead
- Check keyboard shortcut settings

**"Query appears to execute but no results**
- Query is still running (very long timeout)
- Try stopping execution if available
- Simplify query or add LIMIT

### Symptoms
- Only seeing part of the data
- Large numbers are rounded
- Long text is cut off

### Step-by-Step Solution

Results are paginated:
- Click page size dropdown
- Increase page size to show more rows
- Use pagination buttons to navigate

Click and drag column border:
- Widen columns to see truncated text
- Double-click column border for auto-fit
- Scroll horizontally to see all columns

Some data types have display limits:
- Long text fields show summary
- JSON shows preview
- Large numbers may be formatted

Click on cell to see full content in detail view.

In Scratchpad, check if data is actually truncated:
```
SELECT column_name, LENGTH(column_name) as length FROM table LIMIT 1;
```

If length is longer than displayed, it's just a display limit.

Export the data to see everything:
- CSV export shows all content
- Excel export shows all content
- Use these formats for complete view

### Common Scenarios

**"Long text is cut off"**
- Column is too narrow
- Data is just truncated in UI
- Expand column or export to see full text

**"Showing page 1 of 10, but need all data"**
- Results are paginated
- Increase page size to load more at once
- Export all data to file

## Helpful Resources

Detailed explanation of error messages and solutions

Optimize your WhoDB experience and database performance

Frequently asked questions about WhoDB

Search for known issues or report new ones

---


# resources/common-errors.mdx
## Common Errors and Solutions

Reference guide for common WhoDB error messages with explanations and solutions

# Common Errors and Solutions

This guide provides detailed explanations and solutions for error messages you may encounter when using WhoDB.

## Connection Errors

### Error Message
```
Connection refused
Unable to connect to the database
```

### What It Means
WhoDB tried to connect to the database server, but the connection was rejected. This usually means the database server isn't running or isn't listening on the specified port.

### Causes
- Database service not running
- Incorrect host or port
- Firewall blocking connection
- Database server configuration issue

### Solutions

**1. Verify database is running:**
```
# PostgreSQL
systemctl status postgresql

# MySQL
systemctl status mysql

# Check if port is listening
lsof -i :5432
```

**2. Check connection parameters:**
- Host: Use correct hostname or IP (for Docker: `host.docker.internal`)
- Port: Verify correct port (PostgreSQL: 5432, MySQL: 3306, MongoDB: 27017)
- Test from command line first

**3. Check firewall:**
```
# Allow port through firewall
sudo ufw allow 5432/tcp

# Test connectivity
telnet hostname 5432
```

**4. Restart database service:**
```
systemctl restart postgresql
```

See [Connection Issues Troubleshooting](/resources/troubleshooting#connection-issues) for detailed steps.

### Error Message
```
Connection timeout
Connection attempt timed out
```

### What It Means
WhoDB connected to the server but didn't receive a response within the timeout period. The network is slow or the database is unresponsive.

### Causes
- Network latency too high
- Database server overloaded
- Slow query taking too long to execute
- Firewall dropping packets

### Solutions

**1. Increase timeout setting:**
```
environment:
  - DB_CONNECTION_TIMEOUT=30
  - DB_QUERY_TIMEOUT=60
```

**2. Test network connectivity:**
```
ping hostname
traceroute hostname
```

**3. Check if database is responsive:**
```
# PostgreSQL
psql -h hostname -U user -d database

# MySQL
mysql -h hostname -u user -p
```

**4. Reduce query load:**
- Add WHERE conditions to filter data
- Use LIMIT to reduce results
- Check for running queries: `SHOW PROCESSLIST;`

If local connections work fine, the issue is network-related.

### Error Message
```
Connection reset by peer
Connection closed unexpectedly
```

### What It Means
The connection was established but then closed abruptly by the database server. Something about the connection violated the server's policy.

### Causes
- Wrong authentication credentials
- User doesn't have permission
- Server configuration rejected connection
- SSL/TLS certificate issue

### Solutions

**1. Verify credentials are correct:**
```
# Test with database client
psql -h hostname -U username -d database
# Enter password when prompted
```

**2. Check user permissions:**
```
-- PostgreSQL: Check if user can connect
SELECT * FROM pg_user WHERE usename = 'username';

-- MySQL: Check user exists
SELECT User FROM mysql.user WHERE User='username';
```

**3. Check SSL requirements:**
Try disabling SSL temporarily:
- Set SSL Mode to "disable"
- If connection works, SSL is the issue
- Configure proper SSL certificates

**4. Check server logs:**
```
# PostgreSQL logs
tail -f /var/log/postgresql/postgresql.log

# MySQL logs
tail -f /var/log/mysql/error.log
```

Look for rejection reasons in logs.

### Error Message
```
Authentication failed
Access denied for user 'username'@'hostname'
Invalid username or password
```

### What It Means
The database server recognized your connection but rejected your credentials. Either the username doesn't exist, the password is wrong, or the user isn't allowed to connect from this host.

### Causes
- Incorrect password
- Username doesn't exist
- User not allowed to connect from this host
- User account is locked or disabled

### Solutions

**1. Verify credentials outside WhoDB:**
```
# PostgreSQL
psql -h hostname -U username -d database

# MySQL
mysql -h hostname -u username -p

# MongoDB
mongosh --host hostname:27017 -u username -p
```

If you can connect with the client, credentials are correct but WhoDB might have a formatting issue.

**2. Check for special characters in password:**
Some special characters need escaping. Try:
- Removing special characters temporarily
- Testing with simpler password
- Using quotes around password in connection string

**3. Verify user exists:**
```
-- PostgreSQL
\du

-- MySQL
SELECT User, Host FROM mysql.user;

-- MongoDB
db.getUsers()
```

**4. Create user if doesn't exist:**
```
-- PostgreSQL
CREATE USER username WITH PASSWORD 'password';
GRANT CONNECT ON DATABASE mydb TO username;

-- MySQL
CREATE USER 'username'@'hostname' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON mydb.* TO 'username'@'hostname';
FLUSH PRIVILEGES;
```

**5. Unlock user account:**
```
-- PostgreSQL
ALTER USER username WITH ENCRYPTED PASSWORD 'newpassword';

-- MySQL
SET PASSWORD FOR 'username'@'hostname' = PASSWORD('newpassword');
```

### Error Message
```
SSL certificate problem: self signed certificate
SSL: certificate_verify_failed
Certificate verification failed
```

### What It Means
The database is using a self-signed SSL certificate that isn't in the trusted certificate store. This is common in development and testing environments.

### Causes
- Database using self-signed certificate
- SSL verification mode set to "verify-full"
- Certificate not imported to trust store
- Certificate expired

### Solutions

**1. Try with lower SSL verification mode:**
Change from "verify-full" to "require":
- Click SSL settings
- Set SSL Mode to "require"
- Reconnect

This accepts self-signed certificates.

**2. Accept the certificate on first connection:**
Some tools ask to accept certificate. Look for warning dialog.

**3. Add certificate to trusted store:**
Export certificate and trust it:
```
# Export certificate
openssl s_client -connect hostname:5432 -starttls postgres \
  -showcerts > cert.pem

# Add to trusted certificates
sudo cp cert.pem /usr/local/share/ca-certificates/
sudo update-ca-certificates
```

**4. Disable SSL verification (development only):**
```
SSL Mode: disable
```

Never do this in production.

### Error Message
```
SSL: CERTIFICATE_VERIFY_FAILED hostname mismatch
The hostname doesn't match the certificate
```

### What It Means
The SSL certificate is valid but issued for a different hostname than the one you're connecting to. For example, certificate is for `db.internal` but you're connecting to `192.168.1.100`.

### Causes
- Using IP address instead of hostname
- Hostname doesn't match certificate CN
- Certificate is for different domain
- Certificate uses wildcard incorrectly

### Solutions

**1. Connect using correct hostname:**
If certificate is for `db.example.com`, use that hostname instead of IP address.

**2. Use SSL Mode "verify-ca" instead of "verify-full":**
```
SSL Mode: verify-ca
```

This verifies the certificate is valid but doesn't check hostname matching.

**3. Add hostname to hosts file (local development only):**
```
# /etc/hosts
192.168.1.100  db.example.com
```

Then connect using `db.example.com`.

**4. Import certificate with correct hostname:**
Ensure certificate includes the hostname you're using:
```
openssl x509 -in certificate.pem -text -noout | grep "Subject:"
```

## Query Errors

### Error Message
```
Syntax error at or near "[keyword]"
Syntax error near [token]
```

### What It Means
Your SQL query has invalid syntax. The database parser encountered a token it didn't expect.

### Causes
- Misspelled keywords
- Missing or extra commas
- Unmatched quotes or parentheses
- Using syntax from wrong database type
- Missing FROM clause or other required elements

### Solutions

**1. Check for common syntax mistakes:**
```
-- WRONG: Missing comma
SELECT col1 col2 FROM table;

-- CORRECT
SELECT col1, col2 FROM table;

-- WRONG: Mismatched quotes
SELECT * FROM table WHERE name = 'value;

-- CORRECT
SELECT * FROM table WHERE name = 'value';
```

**2. Verify keywords spelling:**
```
-- WRONG
SELELCT * FROM table;

-- CORRECT
SELECT * FROM table;
```

**3. Check database-specific syntax:**
PostgreSQL, MySQL, SQLite each have different functions:
```
-- PostgreSQL
SELECT NOW();

-- MySQL
SELECT NOW();

-- SQLite
SELECT datetime('now');
```

**4. Use syntax highlighting and linter:**
- IDEs like VS Code show syntax errors
- Use database-specific extensions
- Test in database client first

**5. Break down complex queries:**
Test parts of the query separately:
```
-- Test SELECT
SELECT col1, col2 FROM table;

-- Add WHERE
SELECT col1, col2 FROM table WHERE condition;

-- Add JOIN
SELECT t1.col1, t2.col2 FROM table1 t1 JOIN table2 t2 ON t1.id = t2.id;
```

### Error Message
```
Table "public.tablename" does not exist
Table or view does not exist
Unknown table 'tablename'
```

### What It Means
The table you're trying to query doesn't exist in the database, or you're looking in the wrong schema/database.

### Causes
- Table name is misspelled
- Table is in different schema
- Using wrong database
- Table was dropped
- Case sensitivity issue

### Solutions

**1. Verify table name:**
```
-- PostgreSQL: List all tables
\dt

-- MySQL: List all tables
SHOW TABLES;

-- SQLite: List all tables
.tables
```

**2. Use correct table name in query:**
```
-- WRONG
SELECT * FROM orders;  -- If table is actually named 'order'

-- CORRECT
SELECT * FROM order;
```

**3. Specify schema/database if needed:**
```
-- PostgreSQL: Fully qualified name
SELECT * FROM public.users;

-- MySQL: Database-qualified name
SELECT * FROM mydb.users;
```

**4. Check if table exists in current database:**
Some databases use different naming:
```
-- PostgreSQL: Check current database
SELECT current_database();

-- MySQL: Check current database
SELECT DATABASE();
```

**5. Case sensitivity:**
Some databases are case-sensitive:
```
-- PostgreSQL requires quotes for case sensitivity
SELECT * FROM "TableName";

-- MySQL is usually case-insensitive
SELECT * FROM TableName;
```

### Error Message
```
Column "columnname" does not exist
Unknown column 'columnname' in field list
```

### What It Means
The column you're trying to access doesn't exist in the table, or there's a typo in the column name.

### Causes
- Column name is misspelled
- Column doesn't exist in this table
- Case sensitivity issue
- Column is in different table

### Solutions

**1. List columns in table:**
```
-- PostgreSQL
\d tablename

-- MySQL
DESCRIBE tablename;

-- SQLite
PRAGMA table_info(tablename);
```

**2. Verify spelling:**
```
-- WRONG: Typo in column name
SELECT user_name FROM users;  -- If column is actually 'username'

-- CORRECT
SELECT username FROM users;
```

**3. Handle case sensitivity:**
```
-- PostgreSQL: Use quotes if needed
SELECT "UserName" FROM users;

-- Safer approach: lowercase everything
SELECT LOWER(column_name) FROM users;
```

**4. Specify table for ambiguous columns:**
```
-- WRONG: Ambiguous in JOIN
SELECT id FROM users JOIN orders ON users.id = orders.user_id;

-- CORRECT: Specify which table
SELECT users.id FROM users JOIN orders ON users.id = orders.user_id;
```

### Error Message
```
Permission denied
Access denied for user
User does not have permission
```

### What It Means
Your database user doesn't have permission to perform the requested operation (SELECT, INSERT, UPDATE, DELETE, etc.).

### Causes
- User only has SELECT permission but trying to INSERT/UPDATE/DELETE
- User doesn't have permission on specific table
- User role doesn't include required permissions
- Administrative operations require admin user

### Solutions

**1. Check current permissions:**
```
-- PostgreSQL: Check table permissions
\dp tablename

-- MySQL: Check user permissions
SHOW GRANTS FOR 'username'@'hostname';
```

**2. Grant missing permissions:**
```
-- PostgreSQL: Grant all permissions on table
GRANT ALL PRIVILEGES ON tablename TO username;

-- PostgreSQL: Grant specific permissions
GRANT SELECT, INSERT, UPDATE ON tablename TO username;

-- MySQL: Grant permissions
GRANT SELECT, INSERT, UPDATE ON database.* TO 'username'@'hostname';
FLUSH PRIVILEGES;
```

**3. Try with admin account:**
Connect with admin/superuser account to verify operation works:
- If admin can do it but user can't, it's a permissions issue
- Grant appropriate permissions to user

**4. Check user role:**
```
-- PostgreSQL: Check user roles
\du username

-- PostgreSQL: Make user superuser
ALTER USER username SUPERUSER;
```

**5. Reconnect after permission change:**
After granting permissions, reconnect to database for changes to take effect.

### Error Message
```
Foreign key constraint failed
Constraint violation
Cannot add or update a child row
Duplicate entry in foreign key
```

### What It Means
You tried to insert or update a row that violates a foreign key constraint. Either the referenced record doesn't exist, or the value doesn't match any existing record in the referenced table.

### Causes
- Referenced record doesn't exist in parent table
- Trying to delete record that has child records
- Foreign key value is NULL but constraint requires it
- Type mismatch between foreign key and reference

### Solutions

**1. Verify referenced record exists:**
```
-- Verify user exists before inserting order
SELECT * FROM users WHERE id = 123;

-- Then insert order
INSERT INTO orders (user_id, amount) VALUES (123, 99.99);
```

**2. Check all existing data meets constraints:**
```
-- Find orders with non-existent user IDs
SELECT o.* FROM orders o
LEFT JOIN users u ON o.user_id = u.id
WHERE u.id IS NULL;
```

**3. Use correct data type:**
```
-- WRONG: String foreign key
INSERT INTO orders (user_id) VALUES ('123');

-- CORRECT: Integer foreign key
INSERT INTO orders (user_id) VALUES (123);
```

**4. Temporarily disable constraint for bulk operations:**
```
-- PostgreSQL
ALTER TABLE orders DISABLE TRIGGER ALL;
-- ... do operations ...
ALTER TABLE orders ENABLE TRIGGER ALL;

-- MySQL
SET FOREIGN_KEY_CHECKS=0;
-- ... do operations ...
SET FOREIGN_KEY_CHECKS=1;
```

**5. Delete child records first:**
```
-- If deleting user, first delete their orders
DELETE FROM orders WHERE user_id = 123;
DELETE FROM users WHERE id = 123;
```

### Error Message
```
Timeout waiting for query execution
Query execution timeout
Operation took too long
```

### What It Means
Your query ran longer than the allowed timeout period. The database took too long to return results and the connection was terminated.

### Causes
- Query is inefficient and takes too long
- Large result set requires long to transfer
- Database is busy with other queries
- No indexes on query columns

### Solutions

**1. Add LIMIT to reduce results:**
```
-- WRONG: May return millions of rows
SELECT * FROM large_table;

-- CORRECT: Limit results
SELECT * FROM large_table LIMIT 1000;
```

**2. Add WHERE condition to filter:**
```
-- WRONG: Scans entire table
SELECT * FROM orders;

-- CORRECT: Filter by date range
SELECT * FROM orders WHERE created_at > '2024-01-01';
```

**3. Create indexes on frequently queried columns:**
```
-- Find slow queries and add indexes
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_users_email ON users(email);
```

**4. Increase timeout setting:**
```
environment:
  - DB_QUERY_TIMEOUT=120
```

**5. Check if database is overloaded:**
```
-- See currently running queries
-- PostgreSQL
SELECT * FROM pg_stat_activity;

-- MySQL
SHOW PROCESSLIST;
```

Kill long-running operations if needed.

## Data Errors

### Error Message
```
NOT NULL constraint failed
Cannot insert NULL into NOT NULL column
Column does not allow NULL values
```

### What It Means
You tried to insert or update a row with a NULL value in a column that requires a value.

### Causes
- Trying to insert NULL in required field
- Field left empty when submitting form
- NULL value from missing join result
- Default value not applied

### Solutions

**1. Provide value for all required columns:**
```
-- WRONG: Missing required email
INSERT INTO users (name) VALUES ('John');

-- CORRECT: Provide all required fields
INSERT INTO users (name, email) VALUES ('John', 'john@example.com');
```

**2. In UI form: Fill all required fields:**
- Fields marked with asterisk (*) are required
- Don't leave them blank
- Check error message for which field

**3. Use default values:**
```
INSERT INTO users (name, email, created_at)
VALUES ('John', 'john@example.com', NOW());
```

**4. Handle NULL in JOINs:**
```
-- WRONG: NULL if no match in join
SELECT u.name, o.id FROM users u
LEFT JOIN orders o ON u.id = o.user_id;

-- CORRECT: Use COALESCE to provide default
SELECT u.name, COALESCE(o.id, 0) FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
```

**5. Check column definition:**
```
-- See if column allows NULL
\d users  -- PostgreSQL
DESCRIBE users; -- MySQL
PRAGMA table_info(users); -- SQLite
```

### Error Message
```
Duplicate key value violates unique constraint
Duplicate entry for key 'fieldname'
Unique constraint failed
```

### What It Means
You tried to insert or update a row with a value that already exists in a column that has a unique constraint (typically primary key or unique index).

### Causes
- Inserting duplicate value in primary key
- Email already exists in database
- Unique index violation
- Trying to insert same value twice

### Solutions

**1. Verify value doesn't already exist:**
```
-- Check if email already exists
SELECT * FROM users WHERE email = 'test@example.com';

-- If it exists, update instead of insert
UPDATE users SET name = 'Updated' WHERE email = 'test@example.com';
```

**2. Use INSERT ... ON DUPLICATE KEY UPDATE (MySQL):**
```
INSERT INTO users (email, name) VALUES ('test@example.com', 'John')
ON DUPLICATE KEY UPDATE name = 'John';
```

**3. Use upsert syntax (PostgreSQL):**
```
INSERT INTO users (email, name) VALUES ('test@example.com', 'John')
ON CONFLICT (email) DO UPDATE SET name = 'John';
```

**4. Check unique constraints:**
```
-- PostgreSQL: List constraints
\d users

-- MySQL: Check indexes
SHOW INDEX FROM users;
```

**5. For primary key conflicts:**
```
-- If primary key is auto-increment, don't specify it
INSERT INTO users (email, name) VALUES ('test@example.com', 'John');
-- ID will auto-generate

-- WRONG: Specifying duplicate ID
INSERT INTO users (id, email, name) VALUES (1, 'test@example.com', 'John');
```

### Error Message
```
Invalid input syntax for type integer
Cannot convert string to number
Type conversion error
```

### What It Means
You tried to insert or compare a value with the wrong data type. For example, inserting text into a numeric column.

### Causes
- Inserting string in numeric column
- Comparing incompatible types
- Invalid date format
- Type conversion error

### Solutions

**1. Verify correct data type:**
```
-- Check column types
\d tablename  -- PostgreSQL
DESCRIBE tablename; -- MySQL

-- Example error
INSERT INTO orders (id, amount) VALUES ('abc', '99.99');  -- WRONG
INSERT INTO orders (id, amount) VALUES (1, 99.99);  -- CORRECT
```

**2. Cast values to correct type:**
```
-- Cast to integer
SELECT CAST('123' AS INTEGER);
SELECT '123'::INTEGER;  -- PostgreSQL

-- Cast to decimal
SELECT CAST('99.99' AS DECIMAL(10,2));
```

**3. Fix date format:**
```
-- PostgreSQL: Use proper date format
INSERT INTO orders (created_at) VALUES ('2024-01-15');

-- MySQL: Multiple formats work
INSERT INTO orders (created_at) VALUES ('2024-01-15');
INSERT INTO orders (created_at) VALUES (STR_TO_DATE('2024-01-15', '%Y-%m-%d'));
```

**4. Handle type conversion in application:**
Ensure UI validates types before submitting:
- Numbers: Only numeric input
- Dates: Use date picker
- Emails: Validate format

### Error Message
```
Division by zero
Attempted to divide by zero
```

### What It Means
A query tried to divide by zero, which is mathematically undefined and not allowed.

### Causes
- Direct division by 0
- Denominator column contains 0
- Empty result set in division
- Logic error in calculation

### Solutions

**1. Add zero check:**
```
-- WRONG: May divide by zero
SELECT total / count FROM orders;

-- CORRECT: Check for zero
SELECT CASE WHEN count > 0 THEN total / count ELSE 0 END FROM orders;
```

**2. Filter out zero values:**
```
-- WRONG
SELECT total / count FROM orders WHERE count = 0;

-- CORRECT
SELECT total / count FROM orders WHERE count > 0;
```

**3. Use NULLIF to handle zeros:**
```
-- NULLIF returns NULL if expression equals value
SELECT total / NULLIF(count, 0) FROM orders;

-- Then handle NULLs
SELECT COALESCE(total / NULLIF(count, 0), 0) FROM orders;
```

**4. Check denominator values:**
```
-- Find which rows have zero denominator
SELECT * FROM orders WHERE count = 0;

-- Fix the data
UPDATE orders SET count = 1 WHERE count = 0;
```

## Performance and Resource Errors

### Error Message
```
Out of memory
Memory allocation failed
Insufficient memory
```

### What It Means
The operation required more RAM than available, and the system couldn't allocate enough memory to complete the operation.

### Causes
- Query returning very large result set
- Exporting huge amount of data
- Multiple large operations simultaneously
- Memory limit too low

### Solutions

**1. Reduce result set size:**
```
-- WRONG: All 10 million rows
SELECT * FROM huge_table;

-- CORRECT: Use pagination
SELECT * FROM huge_table LIMIT 1000;

-- CORRECT: Filter with WHERE
SELECT * FROM huge_table WHERE created_at > '2024-01-01' LIMIT 1000;
```

**2. Use smaller page size:**
- Change page size setting from 1000 to 100
- Load data in smaller batches
- Navigate between pages instead of loading all

**3. Increase memory allocation:**
```
environment:
  - DB_MAX_MEMORY=2gb
```

**4. Export in smaller batches:**
Instead of exporting 10M rows at once:
- Export by date ranges
- Export specific columns only
- Use multiple export operations

**5. Close other applications:**
Free up system memory by closing unnecessary programs.

### Error Message
```
Connection pool exhausted
No more connections available
Maximum pool size reached
```

### What It Means
All available database connections are in use and new queries can't get a connection.

### Causes
- Too many concurrent operations
- Connections not being released
- Connection pool size too small
- Queries hanging without completing

### Solutions

**1. Increase connection pool size:**
```
environment:
  - DB_MAX_CONNECTIONS=100
  - DB_MAX_IDLE_CONNECTIONS=20
```

**2. Close unused connections:**
Wait for current operations to complete or restart WhoDB.

**3. Check for hanging queries:**
```
-- PostgreSQL: See active connections
SELECT * FROM pg_stat_activity;

-- MySQL: See running queries
SHOW PROCESSLIST;
```

**4. Kill long-running queries:**
```
-- PostgreSQL
SELECT pg_terminate_backend(pid) FROM pg_stat_activity
WHERE state = 'active' AND query_start < NOW() - INTERVAL '10 minutes';

-- MySQL
KILL ;
```

**5. Reduce concurrent operations:**
- Don't run too many queries simultaneously
- Use queue instead of parallel operations
- Restart WhoDB to reset pool

## Getting More Help

If you encounter an error not listed here:

Step-by-step solutions for common issues

Optimize queries and database performance

Search for similar issues or report a bug

Frequently asked questions about WhoDB

---


# resources/performance-tuning.mdx
## Performance Tuning Guide

Optimize WhoDB performance and database queries for faster results and better resource utilization

# Performance Tuning Guide

Learn how to optimize WhoDB and your database for better performance. This guide covers query optimization, database configuration, and best practices for working with large datasets.

## Query Optimization

### Identifying Slow Queries

The first step in optimization is identifying which queries are slow:

**In WhoDB Scratchpad:**
1. Run your query
2. Note the execution time shown in results
3. Queries over 1 second should be investigated
4. Queries over 5 seconds definitely need optimization

**Using EXPLAIN to understand execution:**

```
-- PostgreSQL: Detailed execution plan
EXPLAIN ANALYZE
SELECT * FROM orders
WHERE customer_id = 123 AND created_at > '2024-01-01';

-- MySQL: Execution plan
EXPLAIN
SELECT * FROM orders
WHERE customer_id = 123 AND created_at > '2024-01-01';

-- SQLite: Query plan
EXPLAIN QUERY PLAN
SELECT * FROM orders
WHERE customer_id = 123 AND created_at > '2024-01-01';
```

**Key metrics to watch:**
- Sequential Scan vs Index Scan: Seq Scan on large tables is slow
- Loop times: Shows how many times operations repeat
- Rows: Expected vs actual rows returned
- Cost: Relative expense of operation

### Common Slow Query Patterns

**Pattern 1: No WHERE clause**
```
-- SLOW: Scans entire table
SELECT * FROM orders;

-- FAST: Filter results
SELECT * FROM orders WHERE status = 'pending' LIMIT 100;
```

**Pattern 2: Missing indexes**
```
-- SLOW: Column has no index
SELECT * FROM users WHERE phone = '555-1234';

-- FAST: After creating index
CREATE INDEX idx_users_phone ON users(phone);
SELECT * FROM users WHERE phone = '555-1234';
```

**Pattern 3: Complex JOINs**
```
-- SLOW: Multiple JOINs without indexes
SELECT * FROM orders o
JOIN customers c ON o.customer_id = c.id
JOIN products p ON o.product_id = p.id
WHERE o.created_at > '2024-01-01';

-- FAST: Index all join columns
CREATE INDEX idx_orders_customer_id ON orders(customer_id);
CREATE INDEX idx_orders_product_id ON orders(product_id);
CREATE INDEX idx_orders_created_at ON orders(created_at);

SELECT * FROM orders o
JOIN customers c ON o.customer_id = c.id
JOIN products p ON o.product_id = p.id
WHERE o.created_at > '2024-01-01';
```

**Pattern 4: Using functions in WHERE clause**
```
-- SLOW: Function call on every row (can't use index)
SELECT * FROM users WHERE LOWER(email) = 'test@example.com';

-- FAST: Query data as stored (can use index)
SELECT * FROM users WHERE email = 'test@example.com';

-- SLOW: Date calculation
SELECT * FROM orders WHERE DATE(created_at) = '2024-01-15';

-- FAST: Date range
SELECT * FROM orders WHERE created_at >= '2024-01-15' AND created_at < '2024-01-16';
```

### When to Create Indexes

Create indexes on columns frequently used in:
- WHERE clauses (filter conditions)
- JOIN conditions (ON clauses)
- ORDER BY clauses (sorting)
- GROUP BY clauses (grouping)

Do NOT index:
- Columns rarely queried
- Columns with mostly NULL values
- Boolean columns (only 2 values)
- Very small tables

### Creating Effective Indexes

**Single Column Index:**
```
-- Index on frequently searched column
CREATE INDEX idx_orders_customer_id ON orders(customer_id);

-- PostgreSQL: Include additional columns
CREATE INDEX idx_orders_lookup ON orders(customer_id) INCLUDE (status, amount);
```

**Composite Index (Multiple Columns):**
```
-- For queries with multiple filters
-- Good for: WHERE status = 'pending' AND created_at > '2024-01-01'
CREATE INDEX idx_orders_status_created ON orders(status, created_at);

-- Column order matters! Put equality conditions first
-- Good for: WHERE status = 'pending' AND amount > 100 AND customer_id = 5
CREATE INDEX idx_orders_multi ON orders(status, amount, customer_id);
```

**Partial Index (Filtered Rows):**
```
-- Only index active records (smaller index, faster)
-- PostgreSQL and SQLite
CREATE INDEX idx_orders_active ON orders(created_at)
WHERE status = 'pending';
```

**Full-Text Index (Text Search):**
```
-- PostgreSQL: For text search
CREATE INDEX idx_products_name_tsvector ON products
USING GIN(to_tsvector('english', name));

SELECT * FROM products WHERE to_tsvector('english', name) @@ plainto_tsquery('english', 'laptop');

-- MySQL: Full-text index
CREATE FULLTEXT INDEX idx_products_name_ft ON products(name);

SELECT * FROM products WHERE MATCH(name) AGAINST('laptop');
```

### Index Maintenance

```
-- PostgreSQL: Reindex to optimize
REINDEX INDEX idx_orders_customer_id;

-- MySQL: Analyze table statistics
ANALYZE TABLE orders;

-- SQLite: Analyze
ANALYZE;

-- Find unused indexes
-- PostgreSQL
SELECT schemaname, tablename, indexname
FROM pg_indexes
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
ORDER BY tablename, indexname;
```

### Index Performance Trade-offs

- Indexes speed up reads but slow down writes (INSERT/UPDATE/DELETE)
- Each index uses disk space
- Composite indexes help some queries but not others
- Too many indexes can confuse query optimizer

**Best practice: Index strategically**
- Index most important queries first
- Remove indexes that aren't used
- Monitor query performance after index changes

### Use LIMIT to Reduce Data Transfer

```
-- SLOW: Get all million rows
SELECT * FROM orders;

-- FAST: Get first 100 rows
SELECT * FROM orders LIMIT 100;

-- FAST: Get rows 100-200 (pagination)
SELECT * FROM orders LIMIT 100 OFFSET 100;
```

WhoDB implements pagination automatically, but explicit LIMIT helps the database optimize.

### Select Only Needed Columns

```
-- SLOW: Get all 50 columns
SELECT * FROM users;

-- FAST: Get only needed columns
SELECT id, name, email FROM users;

-- Much faster: Reduces network transfer and memory
SELECT id, name FROM users;  -- Only 2 columns
```

Large columns slow down queries:
```
-- SLOW: Includes large JSON column
SELECT *, metadata FROM products;

-- FAST: Exclude large columns
SELECT id, name, price FROM products;

-- Get large column separately when needed
SELECT metadata FROM products WHERE id = 123;
```

### Aggregate Remotely, Not Locally

```
-- SLOW: Get all data and count locally
SELECT * FROM orders;
-- Then count in application

-- FAST: Count in database
SELECT COUNT(*) FROM orders;

-- SLOW: Get all orders for averaging
SELECT * FROM orders WHERE user_id = 5;
-- Then average in application

-- FAST: Average in database
SELECT AVG(amount) FROM orders WHERE user_id = 5;

-- SLOW: Get all amounts for SUM
SELECT amount FROM orders WHERE status = 'completed';

-- FAST: Sum in database
SELECT SUM(amount) FROM orders WHERE status = 'completed';
```

### Filter Early with WHERE Clauses

```
-- SLOW: Filter after JOINs
SELECT * FROM orders o
JOIN customers c ON o.customer_id = c.id
JOIN products p ON o.product_id = p.id;
-- Then filter 10M rows in application

-- FAST: Filter before JOINs
SELECT o.*, c.name, p.name FROM orders o
JOIN customers c ON o.customer_id = c.id
JOIN products p ON o.product_id = p.id
WHERE o.created_at > '2024-01-01'
  AND o.status = 'completed';
-- Database filters before JOIN, fewer rows to process
```

### Optimize Subqueries

```
-- SLOW: Subquery runs for every row (N+1 problem)
SELECT o.*,
  (SELECT COUNT(*) FROM order_items WHERE order_id = o.id) as item_count
FROM orders o;

-- FAST: Use JOIN with GROUP BY
SELECT o.*, COUNT(oi.id) as item_count
FROM orders o
LEFT JOIN order_items oi ON o.id = oi.order_id
GROUP BY o.id;
```

### Use CTEs for Complex Queries

```
-- Organize complex logic with Common Table Expressions
WITH recent_orders AS (
  SELECT * FROM orders
  WHERE created_at > '2024-01-01'
),
high_value_orders AS (
  SELECT * FROM recent_orders
  WHERE amount > 1000
)
SELECT * FROM high_value_orders;
```

### Optimize UNION Operations

```
-- SLOW: Multiple queries (each scans table)
SELECT * FROM orders WHERE status = 'pending'
UNION
SELECT * FROM orders WHERE status = 'processing';

-- FAST: Single query with OR
SELECT * FROM orders
WHERE status = 'pending' OR status = 'processing';

-- FAST: Use IN for multiple values
SELECT * FROM orders
WHERE status IN ('pending', 'processing');
```

### DISTINCT and GROUP BY Performance

```
-- SLOW: DISTINCT on large result set
SELECT DISTINCT customer_id FROM orders;

-- FAST: GROUP BY (if database optimizes it)
SELECT customer_id FROM orders GROUP BY customer_id;

-- Check execution plan to see which is faster
EXPLAIN SELECT DISTINCT customer_id FROM orders;
EXPLAIN SELECT customer_id FROM orders GROUP BY customer_id;
```

## Database Configuration Tuning

### Configure Connection Pooling in WhoDB

```
version: "3.8"
services:
  whodb:
    image: clidey/whodb
    environment:
      # Connection pool settings
      DB_MAX_CONNECTIONS: 50
      DB_MAX_IDLE_CONNECTIONS: 10
      DB_CONNECTION_MAX_LIFETIME: 3600
      DB_CONNECTION_TIMEOUT: 10
      DB_QUERY_TIMEOUT: 60
```

**Parameter Explanation:**

- `DB_MAX_CONNECTIONS`: Maximum connections to database (typical: 20-100)
  - Higher = more concurrent queries
  - Don't exceed database max connections / number of apps
  - PostgreSQL default: 100, MySQL default: 100-200

- `DB_MAX_IDLE_CONNECTIONS`: Idle connections to keep open (typical: 5-20)
  - Speeds up next query if connection available
  - Too high = wastes database connections
  - Usually 20-30% of max connections

- `DB_CONNECTION_MAX_LIFETIME`: Max connection age in seconds (typical: 1800-7200)
  - Recycle old connections periodically
  - Prevents stale connection issues
  - 1-2 hours is reasonable

- `DB_CONNECTION_TIMEOUT`: How long to wait for connection (seconds)
  - If pool exhausted, wait this long for idle connection
  - 10-30 seconds is typical

- `DB_QUERY_TIMEOUT`: How long query can run (seconds)
  - Kill queries taking longer
  - Prevent runaway queries
  - 30-120 seconds is reasonable

### Database-Side Connection Limits

**PostgreSQL:**
```
-- Check current settings
SHOW max_connections;

-- Adjust in postgresql.conf
max_connections = 200
```

**MySQL:**
```
-- Check current max connections
SHOW VARIABLES LIKE 'max_connections';

-- Increase limit
SET GLOBAL max_connections = 200;

-- Add to my.cnf for persistent change
[mysqld]
max_connections = 200
```

### Connection Pooling Best Practices

- Monitor actual connection usage: `DB_MAX_CONNECTIONS >= (concurrent users * 2)`
- Don't set max connections higher than database allows
- Use connection pooling proxy (PgBouncer, ProxySQL) for high concurrency
- Regularly review and adjust based on actual usage

### Configure WhoDB Memory

```
environment:
  # Memory for query results and caching
  DB_MAX_MEMORY: 1gb
  # Schema cache duration (seconds)
  SCHEMA_CACHE_TTL: 300
  # Query result cache (not for production)
  QUERY_CACHE_ENABLED: "false"
```

### Database Buffer Pool Configuration

**PostgreSQL:**
```
-- Show current buffer pool setting
SHOW shared_buffers;

-- Set in postgresql.conf (need restart)
shared_buffers = '256MB'  -- Usually 25% of system RAM

-- Effective cache size (helps query optimizer)
effective_cache_size = '1GB'  -- Usually 50% of system RAM
```

**MySQL:**
```
-- Show current buffer pool
SHOW VARIABLES LIKE 'innodb_buffer_pool%';

-- Increase buffer pool in my.cnf
innodb_buffer_pool_size = 1G

-- Multiple buffer pool instances for high concurrency
innodb_buffer_pool_instances = 4
```

### Query Cache (MySQL only)

```
-- Check if query cache enabled
SHOW VARIABLES LIKE 'have_query_cache';

-- Query cache usually disabled in modern MySQL
-- Disable it for better performance
query_cache_type = 0
query_cache_size = 0
```

### Table Statistics

Database needs up-to-date statistics for query optimization:

**PostgreSQL:**
```
-- Update table statistics
ANALYZE users;

-- Update all table statistics
ANALYZE;

-- Check statistics are recent
SELECT schemaname, tablename, last_vacuum, last_analyze
FROM pg_stat_user_tables;
```

**MySQL:**
```
-- Update table statistics
ANALYZE TABLE users;

-- Auto-stats collection usually enabled
SHOW VARIABLES LIKE 'innodb_stats%';

-- Force stats update
ANALYZE TABLE users;
```

### Table Partitioning for Very Large Tables

For tables with 100M+ rows:

**PostgreSQL Range Partitioning:**
```
-- Create partitioned table
CREATE TABLE orders (
  id INT,
  created_at TIMESTAMP,
  amount DECIMAL
) PARTITION BY RANGE (YEAR(created_at));

-- Create partitions by year
CREATE TABLE orders_2023 PARTITION OF orders
  FOR VALUES FROM (2023) TO (2024);

CREATE TABLE orders_2024 PARTITION OF orders
  FOR VALUES FROM (2024) TO (2025);
```

Benefits:
- Faster queries on specific date ranges
- Faster deletion of old data (drop partition)
- Parallel query execution

### Maintenance Tasks

```
-- PostgreSQL: Vacuum removes dead rows
VACUUM FULL users;

-- PostgreSQL: Reindex to compact index
REINDEX TABLE users;

-- MySQL: Check and repair table
CHECK TABLE users;
REPAIR TABLE users;

-- SQLite: Vacuum to shrink database
VACUUM;
```

Schedule these during low-traffic periods.

## Large Dataset Handling

### Pagination Strategy

Instead of loading all data at once:

```
-- Inefficient: Get all million rows
SELECT * FROM orders;

-- Efficient: Paginate through results
SELECT * FROM orders LIMIT 1000 OFFSET 0;      -- Page 1
SELECT * FROM orders LIMIT 1000 OFFSET 1000;   -- Page 2
SELECT * FROM orders LIMIT 1000 OFFSET 2000;   -- Page 3
```

WhoDB automatically paginates. For manual pagination:
- Use LIMIT for page size (100-1000)
- Use OFFSET for page number
- Always sort consistently for pagination

### Batch Processing

```
-- Process in batches instead of all at once
-- Batch 1: Process first 10,000 rows
UPDATE orders SET status = 'processed'
WHERE id > 0 AND id <= 10000
AND status = 'pending';

-- Batch 2: Process next 10,000 rows
UPDATE orders SET status = 'processed'
WHERE id > 10000 AND id <= 20000
AND status = 'pending';
```

Benefits:
- Lower memory usage
- Doesn't lock table for entire operation
- Can resume if failed

### Data Archival

Move old data to separate tables:

```
-- Create archive table
CREATE TABLE orders_archive AS
SELECT * FROM orders
WHERE created_at < '2023-01-01'
AND status = 'completed';

-- Delete from main table
DELETE FROM orders
WHERE created_at < '2023-01-01'
AND status = 'completed';

-- Keep table small and fast
VACUUM orders;
```

### Indexing Strategy for Large Tables

```
-- Partial index: Only index active records
CREATE INDEX idx_orders_pending ON orders(created_at)
WHERE status = 'pending';

-- More selective than full table index
-- Smaller size, faster queries

-- Covering index: Includes columns needed
CREATE INDEX idx_orders_full ON orders(customer_id)
INCLUDE (amount, status, created_at);

-- Query doesn't need to read main table
```

### Export Strategies

**CSV Export (Recommended for Large Data):**
```
-- PostgreSQL: Direct CSV export
COPY (SELECT * FROM orders WHERE created_at > '2024-01-01')
TO '/tmp/orders.csv' WITH CSV HEADER;

-- MySQL: CSV export
SELECT * FROM orders
WHERE created_at > '2024-01-01'
INTO OUTFILE '/tmp/orders.csv'
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n';
```

**Selective Export:**
```
-- Export only needed columns
SELECT id, customer_id, amount, created_at
FROM orders
WHERE created_at > '2024-01-01';

-- Use date ranges to split export
SELECT * FROM orders WHERE created_at BETWEEN '2024-01-01' AND '2024-01-31';
SELECT * FROM orders WHERE created_at BETWEEN '2024-02-01' AND '2024-02-28';
```

### In WhoDB UI

1. Use filters to reduce result set before export
2. Export in smaller batches if memory is limited
3. CSV is usually faster than JSON for large data
4. Multiple exports with different filters is better than one huge export

### What WhoDB Caches

WhoDB caches schema information to improve performance:

```
environment:
  # Schema cache duration (seconds)
  SCHEMA_CACHE_TTL: 300  # 5 minutes default
```

Cached items:
- Table list
- Column definitions
- Index information
- Foreign key relationships

### Manual Cache Management

```
-- Force schema refresh in database
-- This invalidates WhoDB cache

-- PostgreSQL: Table structure changed
ALTER TABLE users ADD COLUMN new_column VARCHAR;
-- Cache invalidated, WhoDB will see new column

-- MySQL: Same
ALTER TABLE users ADD COLUMN new_column VARCHAR(255);
-- Cache invalidated
```

### Client-Side Caching

Browser caches:
- Already-viewed data
- Query history
- Connection profiles

Clear browser cache:
- Press Ctrl+Shift+Delete (Chrome/Firefox/Edge)
- Cmd+Shift+Delete (macOS)
- Or refresh page with F5

### Best Practices

- Don't cache live transaction data
- Cache is mostly for schema/metadata
- Results are not cached (always fresh)
- Refreshing table structure clears cache

## Monitoring Performance

### Using EXPLAIN to Profile Queries

**PostgreSQL Example:**
```
EXPLAIN (ANALYZE, BUFFERS)
SELECT o.*, c.name
FROM orders o
JOIN customers c ON o.customer_id = c.id
WHERE o.created_at > '2024-01-01'
LIMIT 100;
```

Look for:
- Seq Scan (slow) vs Index Scan (fast)
- Actual Rows vs Planned Rows (huge difference = optimization issue)
- Buffer Hits (high is good, misses = slower)

**MySQL Example:**
```
EXPLAIN
SELECT o.*, c.name
FROM orders o
JOIN customers c ON o.customer_id = c.id
WHERE o.created_at > '2024-01-01'
LIMIT 100;

-- Look at 'type' column:
-- system/const/eq_ref/ref/range/index/ALL (ALL is slowest)
-- Look at 'key' column - should show index used
```

### Identifying Slow Queries

**PostgreSQL Slow Query Log:**
```
-- Enable query logging
ALTER SYSTEM SET log_min_duration_statement = 1000;  -- Log queries > 1 second
SELECT pg_reload_conf();

-- View logs
tail -f /var/log/postgresql/postgresql.log | grep duration
```

**MySQL Slow Query Log:**
```
-- Enable slow query log
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;  -- 1 second threshold

-- View log
tail -f /var/log/mysql/slow.log
```

### Query Performance Dashboard

In WhoDB Scratchpad, queries show execution time. Track:
- First run time (includes compilation)
- Second run time (should be faster if using cache)
- Large variations indicate resource contention

### System Resource Monitoring

Monitor while running queries in WhoDB:

```
# macOS: Watch real-time stats
top -o MEM

# Linux: Similar view
top

# More detailed: iostat
iostat -x 1

# Network I/O
iftop

# Disk usage
du -sh /path/to/database
```

**Key metrics:**
- CPU: Should stay below 80% (headroom for spikes)
- Memory: Active memory should be 70-80% (80-100% is risky)
- Disk I/O: Watch %util (>50% sustained is concerning)
- Network: Check throughput matches expectations

### Database-Specific Monitoring

**PostgreSQL:**
```
-- Active queries
SELECT pid, usename, query, query_start
FROM pg_stat_activity
WHERE state != 'idle';

-- Table size
SELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as size
FROM pg_tables
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
```

**MySQL:**
```
-- Active queries
SHOW PROCESSLIST;

-- Kill long-running query
KILL ;

-- Table size
SELECT table_name, ROUND(((data_length + index_length) / 1024 / 1024), 2) AS size_mb
FROM information_schema.TABLES
WHERE table_schema = 'your_database'
ORDER BY (data_length + index_length) DESC;
```

## Performance Tuning Checklist

Use this checklist to systematically improve performance:

**Analysis Phase:**
- [ ] Identify slow queries using EXPLAIN
- [ ] Check which queries run most frequently
- [ ] Measure current baseline performance
- [ ] Monitor server resource usage

**Indexing:**
- [ ] Create indexes on WHERE clause columns
- [ ] Create indexes on JOIN columns
- [ ] Create indexes on ORDER BY columns
- [ ] Remove unused indexes
- [ ] Consider composite indexes for common query patterns

**Query Optimization:**
- [ ] Remove SELECT * (select only needed columns)
- [ ] Add WHERE clauses to filter early
- [ ] Use LIMIT to reduce results
- [ ] Avoid functions in WHERE clauses
- [ ] Use appropriate JOIN types

**Database Configuration:**
- [ ] Tune buffer pool size (25% system RAM)
- [ ] Set effective_cache_size (50% system RAM)
- [ ] Configure connection pooling
- [ ] Enable query statistics collection
- [ ] Set appropriate timeout values

**Data Management:**
- [ ] Archive old data to keep tables small
- [ ] Update table statistics regularly
- [ ] Partition very large tables
- [ ] Clean up unused tables and indexes
- [ ] Defragment tables (VACUUM, OPTIMIZE)

**Monitoring:**
- [ ] Set up slow query logging
- [ ] Monitor query times over time
- [ ] Track resource usage during peak load
- [ ] Create performance baselines
- [ ] Set up alerts for degradation

**Testing:**
- [ ] Test changes in non-production first
- [ ] Measure impact of each change
- [ ] Rollback if performance doesn't improve
- [ ] Document what worked and what didn't

## Additional Resources

Solve common performance issues and errors

Understand and fix error messages

Frequently asked questions about performance

Ask community for performance advice

---


# resources/comparisons/overview.mdx
## Database Tool Comparison Overview

Compare WhoDB with popular database management tools. See which solution best fits your needs with our comprehensive comparison guide.

# Database Tool Comparison Overview

Choosing the right database management tool can significantly impact your productivity, workflow efficiency, and team collaboration. This comprehensive comparison guide helps you understand how WhoDB stacks up against popular alternatives and make an informed decision.

## Quick Comparison Table

This comparison reflects the Community Edition of WhoDB. Enterprise Edition includes additional features not shown here.

| Feature | WhoDB | phpMyAdmin | pgAdmin | DBeaver | TablePlus |
|---------|-------|------------|---------|---------|-----------|
| **Price** | Free (Open Source) | Free | Free | Free / $199 | $89-$129 |
| **Memory Usage** | ~50MB | ~100MB | ~300MB | ~500MB+ | ~150MB |
| **Startup Time** | \

**Strengths:**
- Minimal memory footprint (~50MB)
- Instant startup (\

**Strengths:**
- Moderate resource usage
- Fast page loads
- Widely deployed and tested

**Limitations:**
- PHP runtime overhead
- Limited to MySQL/MariaDB
- Older architecture

**Best For:** Legacy MySQL deployments, shared hosting environments

**Strengths:**
- Comprehensive PostgreSQL features
- Active development
- Good for complex operations

**Limitations:**
- Heavy resource usage (300MB+)
- Slow startup time
- Desktop application overhead
- PostgreSQL-only

**Best For:** PostgreSQL DBAs needing advanced features

**Strengths:**
- Supports 50+ database types
- Advanced query tools
- Plugin ecosystem

**Limitations:**
- Very high memory usage (500MB+)
- Slow startup (10-15 seconds)
- Complex interface
- Steep learning curve

**Best For:** Power users managing diverse database ecosystems

**Strengths:**
- Modern, native interface
- Good performance
- Multi-database support

**Limitations:**
- Commercial license required
- Desktop-only
- MacOS/Windows/Linux versions vary
- No web interface

**Best For:** Individual developers who prefer native apps

### User Interface and Experience

**Modern, Clean Design**

Intuitive spreadsheet-like interface with minimal learning curve. Interactive graph visualization for schema exploration. Keyboard shortcuts for power users.

**UI Score: 9/10**

**Functional but Dated**

Legacy interface with numerous menus. Gets the job done but feels outdated. Limited visualization capabilities.

**UI Score: 5/10**

**Complex and Dense**

Feature-rich but overwhelming interface. Steep learning curve for new users. Tree-based navigation can be slow.

**UI Score: 6/10**

**Feature-Heavy**

Powerful but cluttered interface. Many hidden features require exploration. Customizable but complex.

**UI Score: 6/10**

**Modern and Polished**

Native interface with good design. Intuitive for common tasks. Limited customization.

**UI Score: 8/10**

**Choose Based On Priority**

Speed & Simplicity → WhoDB
Advanced PostgreSQL → pgAdmin
Maximum Databases → DBeaver
Native Experience → TablePlus
MySQL Legacy → phpMyAdmin

### Database Support

**Community Edition:**
- PostgreSQL 10+
- MySQL 5.7+ / MariaDB
- SQLite 3
- MongoDB 4.0+
- Redis 5.0+
- ElasticSearch

**Enterprise Edition adds:**
- Oracle
- SQL Server
- DynamoDB
- Cassandra
- Snowflake
- Athena

- MySQL 5.5+
- MariaDB 10.1+

That's it. No other database support.

- PostgreSQL 9.6+
- EDB Postgres Advanced Server

PostgreSQL-only tool with excellent PostgreSQL-specific features.

**50+ databases including:**
- All major SQL databases
- Many NoSQL databases
- Cloud databases
- Data warehouses
- Custom JDBC connections

Most comprehensive database support.

**15+ databases including:**
- PostgreSQL
- MySQL / MariaDB
- SQL Server
- SQLite
- MongoDB
- Redis
- Cassandra
- CockroachDB

Good coverage of popular databases.

### Key Features Comparison

**WhoDB**: Interactive graph view with relationship visualization, pan/zoom, node details

**phpMyAdmin**: Designer view (basic, limited)

**pgAdmin**: ERD tool (functional but outdated)

**DBeaver**: ERD diagrams (comprehensive but complex)

**TablePlus**: Basic structure viewer, no graph visualization

Winner: WhoDB (most intuitive and interactive)

**WhoDB**: Multi-cell scratchpad with syntax highlighting, query history, one-click reuse

**phpMyAdmin**: Basic SQL tab, limited history

**pgAdmin**: Query tool with explain plans, advanced PostgreSQL features

**DBeaver**: Powerful editor with auto-completion, multiple tabs, visual query builder

**TablePlus**: Good editor with auto-completion, saved queries

Winner: Tie between DBeaver (power) and WhoDB (simplicity)

**WhoDB**: Inline editing, add/edit/delete dialogs, bulk operations

**phpMyAdmin**: Row-by-row editing, basic interface

**pgAdmin**: Grid editing, limited inline editing

**DBeaver**: Spreadsheet-like grid, advanced editing features

**TablePlus**: Inline editing with good UX

Winner: Tie between WhoDB and TablePlus (both excellent)

**WhoDB**: CSV, Excel, JSON, SQL with filtering and column selection

**phpMyAdmin**: Multiple formats, good options

**pgAdmin**: CSV, binary, text formats

**DBeaver**: Extensive export options, templates, scheduling

**TablePlus**: CSV, JSON, SQL, good options

Winner: DBeaver (most comprehensive)

**WhoDB**: Built-in realistic data generation for all column types

**phpMyAdmin**: No built-in support

**pgAdmin**: No built-in support

**DBeaver**: Mock data plugin available

**TablePlus**: No built-in support

Winner: WhoDB (only tool with native support)

## Decision Matrix

Use this matrix to determine which tool best fits your needs:

### Choose WhoDB If You:

Need instant startup and minimal resource usage

Prefer intuitive interfaces over feature overload

Work with multiple database types regularly

Appreciate clean, modern design

Deploy in containerized environments

Need interactive schema exploration

### Choose phpMyAdmin If You:

Exclusively use MySQL/MariaDB and need basic management on shared hosting

### Choose pgAdmin If You:

Need advanced PostgreSQL-specific features and are comfortable with complex interfaces

### Choose DBeaver If You:

Manage many database types, need advanced features, and have resources to spare

### Choose TablePlus If You:

Prefer native desktop apps and don't mind paying for software

## Use Case Recommendations

**Recommended: WhoDB**

Startups need to move fast with limited resources. WhoDB provides:
- Zero configuration deployment
- Minimal infrastructure requirements
- Multi-database support as you scale
- Easy onboarding for new team members
- No licensing costs

**Alternative:** TablePlus for Mac-centric teams who prefer native apps

**Recommended: WhoDB Enterprise or DBeaver Enterprise**

Large organizations need:
- **WhoDB Enterprise**: Best for standardized, web-based access with modern UX
- **DBeaver Enterprise**: Best for power users needing maximum database coverage

Consider WhoDB Enterprise if you want:
- Central web-based access
- Easier IT management
- Lower training costs
- Better security controls

Consider DBeaver Enterprise if you need:
- Maximum database type support
- Advanced query capabilities
- Desktop-first workflow

**Recommended: pgAdmin or WhoDB**

For PostgreSQL-only environments:
- **pgAdmin**: Maximum PostgreSQL-specific features
- **WhoDB**: Better UX and performance for common tasks

Choose pgAdmin for advanced PostgreSQL administration features.
Choose WhoDB for daily development work with better speed and UX.

Many teams use both: pgAdmin for DBA tasks, WhoDB for development.

**Recommended: WhoDB (migrating from phpMyAdmin)**

If you're currently using phpMyAdmin:
- WhoDB provides everything phpMyAdmin does, plus much more
- Migration is painless (just connect to the same databases)
- Modern interface improves productivity
- Opens door to multi-database support

See our [phpMyAdmin migration guide](/resources/comparisons/vs-phpmyadmin) for details.

**Recommended: WhoDB or TablePlus**

Individual developers should choose based on preference:
- **WhoDB**: Free, web-based, lightweight, multi-database
- **TablePlus**: Native app, polished UX, one-time payment

Both are excellent choices. Try WhoDB first (it's free), then evaluate TablePlus if you prefer native apps.

**Recommended: WhoDB or DBeaver**

Data analysts need good export, filtering, and query capabilities:
- **WhoDB**: Better for quick analysis, easier to learn
- **DBeaver**: Better for complex analysis, visual query builder

WhoDB excels at:
- Quick ad-hoc queries
- Fast data export
- Easy filtering and search
- Lower learning curve

DBeaver excels at:
- Complex query building
- Extensive export options
- Multiple result sets
- Advanced data transformations

## Pricing Comparison

    **WhoDB Community Edition**
    - Completely free
    - Open source (can audit code)
    - Self-hosted
    - No user limits
    - Community support

    **phpMyAdmin**
    - Completely free
    - Open source
    - Self-hosted
    - Community support

    **pgAdmin**
    - Completely free
    - Open source
    - Community support

    **DBeaver**
    - Community Edition: Free
    - Enterprise Edition: $199/year per user
    - Team Edition: Custom pricing
    - Enterprise includes: NoSQL, cloud databases, team features, priority support

    **TablePlus**
    - Standard: $89 (lifetime)
    - Pro: $129 (lifetime)
    - Free trial available
    - Includes updates for 1 year
    - Native desktop experience

    **WhoDB Enterprise**
    - Custom pricing
    - Additional database support
    - Priority support
    - SSO integration
    - Audit logging

## Migration Difficulty

Switching database tools is easier than you might think. Here's how difficult it is to migrate:

**Difficulty: Very Easy (30 minutes)**

1. Install WhoDB
2. Connect to same MySQL databases
3. Start using immediately

[Complete migration guide](/resources/comparisons/vs-phpmyadmin)

**Difficulty: Easy (1 hour)**

1. Install WhoDB
2. Connect to PostgreSQL databases
3. Learn new interface (minimal learning curve)

[Complete migration guide](/resources/comparisons/vs-pgadmin)

**Difficulty: Moderate (2-4 hours)**

1. Evaluate database coverage (DBeaver supports more types)
2. Export saved queries
3. Recreate commonly-used queries in WhoDB
4. Adjust to simpler interface

Consider keeping DBeaver for specialized databases while using WhoDB for common ones.

**Difficulty: Easy (1 hour)**

1. Install WhoDB
2. Connect to databases (similar process)
3. Adjust to web-based interface

Main difference is web vs. native app experience.

## Common Questions

Yes, and many teams do. Common combinations:

- **WhoDB + pgAdmin**: WhoDB for daily work, pgAdmin for advanced PostgreSQL tasks
- **WhoDB + DBeaver**: WhoDB for common databases, DBeaver for specialized ones
- **WhoDB + CLI tools**: WhoDB for visual work, CLI for scripting

WhoDB doesn't lock you in - use it alongside other tools as needed.

**WhoDB** has the lowest learning curve. New users become productive in minutes.

**TablePlus** is also beginner-friendly with its clean, native interface.

**Avoid** pgAdmin and DBeaver for beginners - these tools have steep learning curves and can be overwhelming.

**WhoDB** is ideal for teams because:
- Web-based access (no installation required)
- Consistent experience across platforms
- Easy to deploy centrally
- No per-user licensing costs
- Quick onboarding

**DBeaver Enterprise** is good for large teams with diverse database needs and dedicated training.

**For most users, no.** Free versions of WhoDB, phpMyAdmin, pgAdmin, and DBeaver cover 90%+ of use cases.

Consider paid versions if you need:
- **DBeaver Enterprise**: NoSQL databases, cloud databases, team collaboration
- **TablePlus**: You prefer native apps and like their design
- **WhoDB Enterprise**: Additional databases, SSO, audit logging, priority support

Try free versions first. You'll know when you need paid features.

All tools can be secured properly with:
- SSL/TLS connections
- Strong authentication
- Network restrictions
- Regular updates

**Self-hosted tools (WhoDB, phpMyAdmin, pgAdmin)** give you complete control over security.

**Desktop tools (DBeaver, TablePlus)** store credentials locally on your machine.

For enterprise security needs, consider **WhoDB Enterprise** with SSO and audit logging.

## Final Recommendation

For most users and teams, **WhoDB** offers the best balance of speed, simplicity, and features.

Here's our honest recommendation by scenario:

**WhoDB**

Fast, modern, multi-database support, excellent UX, free and open source

**pgAdmin (advanced) or WhoDB (daily use)**

Maximum PostgreSQL features vs. better UX

**DBeaver Enterprise**

Maximum databases and advanced features

**TablePlus or WhoDB**

Native app vs. web-based

**WhoDB**

Easy deployment, no licensing costs, low learning curve

**WhoDB (upgrade from phpMyAdmin)**

Modern replacement for phpMyAdmin

## Try WhoDB Today

Ready to see how WhoDB compares in practice?

Get started in under 5 minutes

Try WhoDB without installing

## Detailed Comparisons

Want to dive deeper into specific tool comparisons?

Detailed comparison with phpMyAdmin

Detailed comparison with pgAdmin

Detailed comparison with DBeaver

Detailed comparison with TablePlus

Still not sure? Join our [community discussions](https://github.com/clidey/whodb/discussions) to ask questions and hear from other users who have made the switch.

---


# resources/comparisons/vs-phpmyadmin.mdx
## WhoDB vs phpMyAdmin: Complete Comparison

Compare WhoDB and phpMyAdmin for MySQL database management. See detailed feature comparison, speed tests, UI differences, and when to use each tool.

# WhoDB vs phpMyAdmin: Complete Comparison

If you're managing MySQL or MariaDB databases, you've probably encountered phpMyAdmin. For decades, phpMyAdmin has been the go-to web-based database management tool for MySQL. But the database management landscape has evolved significantly. This comprehensive comparison will help you decide between WhoDB and phpMyAdmin for your MySQL database management needs.

## Quick Summary

**The Modern Choice**

Fast, intuitive, supports multiple databases, beautiful UI, mock data generation

**Best for:** Teams wanting modern database management with minimal learning curve

**The Legacy Standard**

Mature, feature-rich for MySQL, widely deployed, older interface

**Best for:** MySQL-only environments on shared hosting with existing installations

## Performance and Speed Comparison

One of the most noticeable differences between WhoDB and phpMyAdmin is performance. Let's look at real-world metrics:

| Metric | WhoDB | phpMyAdmin |
|--------|-------|-----------|
| **Startup Time** | \

- Clean, minimalist design inspired by modern SaaS applications
- Dark mode support for comfortable late-night database work
- Spreadsheet-like data grid (familiar to Excel users)
- Right-click context menus for common actions
- Keyboard shortcuts for power users
- Sidebar navigation with schema hierarchy
- Real-time search across tables and columns

**Learning Curve:** Most users become productive in **minutes**, not hours.

### phpMyAdmin Interface

- Traditional menu-driven interface
- Numerous tabs and navigation options
- Multiple ways to accomplish the same task
- Left sidebar with database tree
- Modal dialogs for many operations
- Legacy design patterns
- Lots of text-based options and configurations

**Learning Curve:** Users typically need **hours to days** to become proficient, especially for advanced features.

## Feature Comparison Table

| Feature | WhoDB | phpMyAdmin |
|---------|:----:|:---------:|
| **Web-Based Access** | Yes | Yes |
| **Multiple Database Support** | Yes (6+) | MySQL/MariaDB only |
| **Table Management** | Yes | Yes |
| **Column Management** | Yes | Yes |
| **Indexes** | Yes | Yes |
| **Views** | Yes | Yes |
| **Triggers** | Yes | Yes |
| **Stored Procedures** | Yes | Yes |
| **User Management** | Yes | Yes |
| **Permissions** | Yes | Yes |
| **Backup/Restore** | Yes | Yes |
| **Database Replication** | Yes | Limited |

| Feature | WhoDB | phpMyAdmin |
|---------|:----:|:---------:|
| **Add Records** | Simple dialog | Form interface |
| **Edit Records** | Inline or dialog | Row-by-row form |
| **Delete Records** | Simple & safe | Multiple options |
| **Bulk Operations** | Select & act | Limited |
| **Sorting** | Click columns | Column controls |
| **Filtering** | Visual builder | SQL conditions |
| **Search** | Real-time across all columns | Per-column search |
| **Data Export** | CSV, Excel, JSON, SQL | Multiple formats |
| **Data Import** | Yes | Yes |
| **Mock Data** | Native generation | No support |

| Feature | WhoDB | phpMyAdmin |
|---------|:----:|:---------:|
| **SQL Editor** | Syntax highlighting | Basic editor |
| **Auto-completion** | Yes | Yes |
| **Query History** | Searchable & organized | Basic history |
| **Query Reuse** | One-click clone | Copy/paste |
| **Query Bookmarks** | Yes | Yes |
| **Multi-query Execution** | Multi-cell support | One at a time |
| **Result Export** | Direct export | Download required |
| **Execution Time** | Shown | Shown |
| **Query Formatting** | Auto-format | Manual |

| Feature | WhoDB | phpMyAdmin |
|---------|:----:|:---------:|
| **Schema Visualization** | Interactive graph | Designer view |
| **Relationship Visualization** | Yes, interactive | Basic ERD |
| **Foreign Key Visualization** | Yes | Yes |
| **Constraint Visualization** | Yes | Yes |
| **Export Diagrams** | Yes (PNG) | Yes |
| **Pan & Zoom** | Smooth | Limited |
| **Relationship Details** | Click to explore | Static view |

| Feature | WhoDB | phpMyAdmin |
|---------|:----:|:---------:|
| **Database Creation** | Yes | Yes |
| **Database Deletion** | Yes (with safeguards) | Yes |
| **User Management** | Yes | Comprehensive |
| **Privilege Management** | Yes | Very comprehensive |
| **Replication Setup** | Yes | Yes |
| **Database Status** | Yes | Yes |
| **Performance Stats** | Yes | Basic |
| **Server Variables** | Yes | Yes |
| **Log Viewer** | Yes | Yes |

## Mock Data Generation

This is one of WhoDB's standout features that phpMyAdmin completely lacks.

### WhoDB Mock Data

- Configure row count (1 to 100,000+)
- Column-type aware generation (dates, emails, numbers, text, etc.)
- Realistic data that mimics production patterns
- Append or replace mode
- Generate with single click
- Perfect for testing and development

**Example Use Case:**
```
Your developers need test data for their API integration tests.
With WhoDB: 3 clicks, 30 seconds
With phpMyAdmin: Manual data entry or complex SQL scripts, 30+ minutes
```

### phpMyAdmin Alternative

phpMyAdmin requires:
- Writing custom SQL INSERT statements
- Using external tools or scripts
- Manual data entry
- External data generation tools

**Advantage: WhoDB** - Dramatically faster and easier for development work

## Deployment and Hosting

**WhoDB:**
- Official Docker image
- Single command deployment
- Lightweight image (~100MB)
- Environment variable configuration
- Perfect for containerized environments

```
docker run -p 8080:8080 clidey/whodb:latest
```

**phpMyAdmin:**
- Mature Docker support
- Requires separate database connection
- Needs additional services
- More complex configuration

**Winner:** WhoDB (simpler, cleaner)

**WhoDB:**
- Simple deployment manifests
- Minimal resource requirements
- Scales easily
- Single container per pod

**phpMyAdmin:**
- More complex configuration
- Heavier resource requirements
- Multiple dependencies

**Winner:** WhoDB (better Kubernetes support)

**WhoDB:**
- Requires Go runtime
- Can be compiled to single binary
- Limited shared hosting support
- Better with dedicated servers

**phpMyAdmin:**
- PHP is ubiquitous on shared hosting
- Pre-installed on most hosts
- Easiest shared hosting option
- Virtually universal support

**Winner:** phpMyAdmin (for traditional shared hosting)

**WhoDB:**
- Excellent for self-hosted
- Works on any Linux server
- Small footprint
- Cloud-ready architecture

**phpMyAdmin:**
- Battle-tested on shared hosting
- Available pre-installed
- Works on Windows/Mac/Linux
- Traditional web hosting friendly

**Winner:** Both good, depends on infrastructure

## Multi-Database Support

### WhoDB - Multi-Database Advantage

PostgreSQL, MySQL, MariaDB, SQLite, MongoDB, Redis, ElasticSearch

All above + Oracle, SQL Server, DynamoDB, Cassandra, Snowflake, Athena

**Why This Matters:**
Modern applications often use multiple database types. WhoDB lets you manage them all from a single interface with consistent UX.

### phpMyAdmin - MySQL Only

phpMyAdmin exclusively supports:
- MySQL 5.5+
- MariaDB 10.1+

**Limitation:** If your infrastructure includes PostgreSQL, SQLite, or NoSQL databases, you'll need separate tools.

**Practical Impact:**
- phpMyAdmin: 5+ tools for different databases
- WhoDB: 1 tool for all databases

## Security Comparison

| Feature | WhoDB | phpMyAdmin |
|---------|:----:|:---------:|
| **SSL/TLS Support** | Yes | Yes |
| **SSH Tunneling** | Yes | Yes |
| **Certificate Verification** | Yes | Yes |
| **Custom Certificates** | Yes | Yes |

Both tools handle connection security well. WhoDB has a cleaner certificate configuration interface.

| Feature | WhoDB | phpMyAdmin |
|---------|:----:|:---------:|
| **Database Auth** | Yes | Yes |
| **HTTP Basic Auth** | Yes | Yes |
| **Session Management** | Secure | Secure |
| **LDAP Integration** | EE only | Yes |
| **OAuth** | EE only | No |

phpMyAdmin has more built-in authentication options. WhoDB Enterprise adds LDAP/OAuth.

**WhoDB:**
- Stores connection profiles in browser by default
- Optional server-side storage
- Credentials cleared on logout
- Environment variable support

**phpMyAdmin:**
- Stores credentials in PHP session
- Server-side storage option
- Traditional HTTP session handling

Both options are secure when properly configured.

| Feature | WhoDB | phpMyAdmin |
|---------|:----:|:---------:|
| **Query Logging** | Yes | Yes |
| **Change Tracking** | Yes | Limited |
| **User Action Logging** | Yes | Yes |
| **Audit Trail Export** | Yes | Limited |

WhoDB has better audit logging for compliance needs.

## Learning Curve and Onboarding

### WhoDB Onboarding

New users can navigate the interface without training. Layouts follow modern SaaS patterns most developers already know.

Labels are clear, actions are discoverable, contextual help appears when needed. Right-click menus follow web standards.

Connect to database → start working. No configuration needed for basic tasks.

Most new users become productive in **5-10 minutes**.

### phpMyAdmin Onboarding

Multiple ways to accomplish the same task require learning multiple paths. Navigation isn't always intuitive.

Many features aren't self-explanatory. New users often need to consult documentation.

Many settings and options require configuration for optimal use. Database-specific features require understanding MySQL specifics.

New users typically need **1-3 hours** to become proficient with common tasks.

**Impact for Teams:**
- WhoDB: New developer onboarding takes minutes
- phpMyAdmin: New developer onboarding takes hours

## Use Case Analysis

### When to Use WhoDB

- Fast-moving teams that value productivity
- Mix of database types in infrastructure
- Want visual schema exploration
- Prefer modern UI/UX

- Containerized environments
- Cloud-native infrastructure
- Need lightweight deployments
- Multiple database servers

- Need mock data generation
- Frequently need to inspect/modify database state
- Want quick prototyping
- Need ad-hoc queries

- Export and analyze data frequently
- Need good filtering and search
- Benefit from visualizations
- Work across multiple databases

### When to Use phpMyAdmin

- Pre-installed on hosting platform
- PHP available, Go not available
- Traditional hosting environments
- Cost-sensitive deployments

- Exclusively MySQL/MariaDB
- Don't need multi-database support
- Happy with existing installation
- Don't need visualization features

- Long-running phpMyAdmin installations
- Team already trained on phpMyAdmin
- Change not justified by benefits
- Works, doesn't break anything

- Need maximum MySQL-specific features
- Complex user permission management
- Server replication administration
- DBA-level operations

## Migration Guide: phpMyAdmin to WhoDB

Ready to switch? Here's how easy it is:

Download WhoDB or use Docker. Startup takes less than 5 minutes.

```
docker run -p 8080:8080 clidey/whodb:latest
```

No complex configuration needed.

Use the same connection details you used in phpMyAdmin:
- Host, port, username, password
- WhoDB recognizes them immediately
- No data migration needed - you're connecting to existing databases

Spend 10-15 minutes learning where things are:
- Data viewing and editing
- Query editor (Scratchpad)
- Schema visualization
- Export options

Common phpMyAdmin tasks map directly to WhoDB:
- Viewing tables → Same data grid
- Running queries → Scratchpad editor
- Exporting data → Export function
- Adding records → Add dialog

All familiar, just cleaner UI.

Show team members the new interface. Most need only a 15-minute walkthrough to become productive. Huge time savings compared to phpMyAdmin learning curve.

Once comfortable, switch completely. Run WhoDB alongside phpMyAdmin for a few days if you prefer gradual transition.

**Total Migration Time:** 30 minutes to 1 hour
**Training Time per Developer:** 15-30 minutes
**Productivity Improvement:** 20-30% faster database operations

## Feature Differences Deep Dive

**WhoDB:**
- Multiple formats (CSV, Excel, JSON, SQL)
- Column selection before export
- Filter data before export
- Custom delimiters
- Clean, simple interface
- Faster export process

**phpMyAdmin:**
- Multiple formats available
- More format options (XML, LaTeX, etc.)
- Complex export dialogs
- Slower export with large datasets
- More options (sometimes too many)

**Verdict:** WhoDB for simplicity, phpMyAdmin for advanced format options

**WhoDB:**
- Syntax highlighting
- Auto-completion
- Query history with search
- One-click query reuse
- Multi-cell notebooks
- Format on command

**phpMyAdmin:**
- Basic query editor
- Syntax highlighting
- Limited auto-completion
- Query bookmarks
- Explain plans
- Query profiler

**Verdict:** WhoDB for modern developer experience, phpMyAdmin for advanced MySQL analysis

**WhoDB:**
- Inline cell editing
- Add record dialog (clean form)
- Edit record dialog
- Bulk select and delete
- Relationship-aware editing
- Validation feedback

**phpMyAdmin:**
- Row-by-row editing
- Form-based editing
- Multi-row manipulation
- Various edit modes
- Advanced options everywhere

**Verdict:** WhoDB for simple operations, phpMyAdmin for complex bulk modifications

**WhoDB:**
- Visual schema explorer
- Interactive relationship graph
- Table structure view
- Column management
- Index viewing
- Constraint details

**phpMyAdmin:**
- Designer view (basic ERD)
- Table structure view
- Detailed table properties
- Advanced MySQL options
- Replication setup

**Verdict:** WhoDB for understanding structure, phpMyAdmin for detailed management

## Pricing Comparison

| Aspect | WhoDB | phpMyAdmin |
|--------|-------|-----------|
| **Base Cost** | Free (Community Edition) | Free (Open Source) |
| **Source Code** | Open Source | Open Source |
| **Per-User Cost** | None | None |
| **Hosting** | Self-hosted | Self-hosted |
| **Support** | Community | Community |
| **Enterprise Edition** | Available (custom pricing) | N/A |
| **Enterprise Support** | Available | N/A |

**Bottom Line:** Both free and open source. Cost difference is near zero unless you need WhoDB Enterprise features.

## Real-World Scenarios

### Scenario 1: Startup with Growing Database

**Situation:** Growing startup currently uses shared hosting with phpMyAdmin. Adding PostgreSQL for analytics.

**Problem with phpMyAdmin:**
- Can't manage PostgreSQL from same interface
- Need separate tool
- Team context switching
- Multiple UI paradigms

**WhoDB Solution:**
- Manage both MySQL and PostgreSQL
- Single, modern interface
- No context switching
- Scale easily to production

**Outcome:** Faster development, easier team workflows

### Scenario 2: Development Team Refactor

**Situation:** Team moving from shared hosting to Docker/Kubernetes on AWS.

**Problem with phpMyAdmin:**
- Requires PHP runtime
- Heavier resource footprint
- More complex Kubernetes manifests
- Not optimized for cloud

**WhoDB Solution:**
- Single binary deployment
- 50MB resource footprint
- Simple Kubernetes manifests
- Cloud-native architecture

**Outcome:** Simpler infrastructure, faster deployments, lower cloud costs

### Scenario 3: Data Analysis Team

**Situation:** Analysts need to query database and export data for reporting.

**Problem with phpMyAdmin:**
- No mock data for testing queries
- Complex filtering interface
- Slower data export
- Limited visualization

**WhoDB Solution:**
- Generate test data to verify queries
- Visual filtering interface
- Fast, clean exports
- Schema visualization helps understand relationships

**Outcome:** 40% faster analysis workflow, fewer mistakes

## Final Verdict

- Value modern user experience
- Work with multiple database types
- Use containers/Kubernetes
- Want to maximize developer productivity
- Need mock data generation
- Deploy to cloud environments
- Want visual schema exploration

- Exclusively use MySQL/MariaDB
- Deploy to traditional shared hosting
- Already have phpMyAdmin running
- Need maximum MySQL-specific features
- Don't want to change working systems
- Require PHP-based deployment

## Try WhoDB Today

Ready to experience the speed and simplicity of modern database management?

Get started in under 5 minutes. No credit card needed.

Try WhoDB without installing anything

## Common Questions

Yes! WhoDB connects to any MySQL 5.7+ or MariaDB database. Just use the same connection details you used in phpMyAdmin. No data migration needed - you're just switching tools.

WhoDB requires Go support, which most shared hosting doesn't provide. Shared hosting is phpMyAdmin's strength. For cloud environments, VPS, or Docker, WhoDB is ideal.

WhoDB has everything phpMyAdmin has for data management, plus mock data generation, better visualization, and modern UX. phpMyAdmin has a few advanced MySQL-specific features WhoDB doesn't, but 95% of users never need them.

Migration is straightforward: install WhoDB (5 min), connect to existing databases (2 min), learn interface (10-15 min). Total: 30 minutes. Team onboarding adds 15-30 minutes per developer.

Yes. Many teams run both during transition. WhoDB for daily work, phpMyAdmin for specific advanced MySQL tasks if needed. They work perfectly alongside each other.

Both tools are secure when properly configured with SSL/TLS. WhoDB Enterprise adds LDAP, OAuth, SSO, and comprehensive audit logging for enterprise compliance needs.

**Pro Tip:** Start with WhoDB Community Edition (free) today. If you need advanced features, WhoDB Enterprise is available for enterprise deployments with additional database support, SSO, and audit logging.

---

**Ready to upgrade your database management?** [Install WhoDB now](/installation) and join thousands of developers who've made the switch.

---


# resources/comparisons/vs-pgadmin.mdx
## WhoDB vs pgAdmin: Complete Comparison

Compare WhoDB and pgAdmin for PostgreSQL database management. Feature comparison, performance analysis, UI differences, and when to use each tool.

# WhoDB vs pgAdmin: Complete Comparison

PostgreSQL is one of the most powerful open-source databases available, and both WhoDB and pgAdmin help manage it. If you're working with PostgreSQL, you've likely encountered pgAdmin—it's been the standard PostgreSQL management tool for years. But modern database management needs have evolved. This comprehensive comparison will help you choose between WhoDB and pgAdmin for managing your PostgreSQL databases.

## Quick Summary

**The Modern, Fast Alternative**

Lightning-fast startup, intuitive UI, multi-database support, web-based

**Best for:** Daily PostgreSQL work, development teams, modern infrastructure

**The Comprehensive Standard**

PostgreSQL-specific features, advanced administration, mature ecosystem

**Best for:** PostgreSQL DBAs, complex administration tasks, comprehensive feature needs

## Performance and Speed Comparison

Performance is a critical differentiator between these tools. Let's look at real-world metrics:

| Metric | WhoDB | pgAdmin |
|--------|-------|---------|
| **Startup Time** | \

**Deployment Model:**
- Single Go binary
- Lightweight (~15MB)
- Runs on Linux, Mac, Windows
- Docker optimized (50MB image)
- Minimal dependencies
- Browser-based access

**Infrastructure Impact:**
- Single process per instance
- \

**Deployment Model:**
- Python/PostgreSQL backend
- Web server (Apache/Nginx required)
- Multiple services/processes
- Heavier footprint
- More complex setup
- Desktop or web deployment

**Infrastructure Impact:**
- Multiple processes needed
- ~300MB+ memory per instance
- Heavier resource requirements
- Traditional server deployment
- Requires web server configuration
- More complex scaling

## User Interface Comparison

### WhoDB Interface

- Clean, contemporary design
- Dark mode support
- Spreadsheet-like data grid
- Right-click context menus
- Keyboard-driven workflow
- Sidebar with database hierarchy
- Real-time search functionality
- Visual schema graph

**User Experience:**
- **Navigation:** Intuitive, follows modern SaaS patterns
- **Data Editing:** Click to edit, no complex dialogs
- **Query Execution:** Write, execute, see results instantly
- **Learning Curve:** Most users productive in **5-10 minutes**
- **Complexity:** Simple tasks stay simple

### pgAdmin Interface

- Comprehensive feature set
- Tree-based navigation
- Multiple panel layout
- Context menus with many options
- Form-based dialogs
- Traditional database tool design
- Advanced PostgreSQL-specific features
- Many configuration options

**User Experience:**
- **Navigation:** Tree navigation requires learning
- **Data Editing:** Form-based editing
- **Query Execution:** Query tool with multiple options
- **Learning Curve:** New users need **4-8 hours** to be proficient
- **Complexity:** Many features, many options

## Feature Comparison Table

| Feature | WhoDB | pgAdmin |
|---------|:----:|:-------:|
| **Explain Plans** | Yes | Yes, Advanced |
| **VACUUM Analysis** | Yes | Yes |
| **Query Profiling** | Yes | Yes, Detailed |
| **Extension Management** | Yes | Yes |
| **Trigger Management** | Yes | Yes |
| **Rule Management** | Limited | Yes |
| **Partition Management** | Yes | Yes |
| **Replication Setup** | Yes | Advanced |
| **Logical Replication** | Yes | Advanced |
| **Materialized Views** | Yes | Yes |
| **JSONB Operations** | Yes | Yes |
| **Custom Types** | Yes | Yes |
| **Domains** | Yes | Yes |
| **Full-Text Search** | Yes | Yes |

| Feature | WhoDB | pgAdmin |
|---------|:----:|:-------:|
| **Add Records** | Simple dialog | Form interface |
| **Edit Records** | Inline/dialog | Form interface |
| **Delete Records** | Safe with confirmation | Bulk delete available |
| **Bulk Operations** | Select & act | Multiple options |
| **Column Filtering** | Visual builder | SQL WHERE builder |
| **Data Search** | Real-time | Per-column |
| **Data Export** | CSV, Excel, JSON, SQL | Multiple formats |
| **Data Import** | Yes | Yes |
| **Mock Data** | Native generation | No |
| **Column Sorting** | Click to sort | Column controls |

| Feature | WhoDB | pgAdmin |
|---------|:----:|:-------:|
| **User Management** | Yes | Comprehensive |
| **Role Management** | Yes | Advanced |
| **Permission Management** | Yes | Very comprehensive |
| **Database Creation** | Yes | Yes |
| **Database Cloning** | Limited | Yes |
| **Tablespace Management** | Yes | Yes |
| **Backup/Restore** | Yes | Advanced UI |
| **Replication Management** | Yes | Advanced |
| **Monitoring** | Yes | Detailed |
| **Connection Pooling** | Limited | Limited |
| **Server Configuration** | Yes | Advanced |
| **Log Viewer** | Yes | Yes |

| Feature | WhoDB | pgAdmin |
|---------|:----:|:-------:|
| **Syntax Highlighting** | Yes | Yes |
| **Auto-completion** | Yes | Yes |
| **Query History** | Searchable | Limited |
| **Query Bookmarks** | Yes | Yes |
| **Multi-Query Execution** | Multi-cell | Batch execution |
| **Explain Plans** | Yes | Advanced |
| **Query Profiling** | Yes | Detailed |
| **Result Formatting** | JSON, CSV, export | Multiple formats |
| **Execution Statistics** | Yes | Yes |

| Feature | WhoDB | pgAdmin |
|---------|:----:|:-------:|
| **Schema Graph** | Interactive | No |
| **ERD Diagrams** | Yes | No |
| **Relationship Visualization** | Yes | No |
| **Table Dependencies** | Visual | No |
| **Index Visualization** | Yes | No |
| **Constraint Visualization** | Yes | No |
| **Export Diagrams** | PNG | No |

## Multi-Database Support: A Key Difference

### WhoDB - Multi-Database Advantage

WhoDB supports **multiple database types** with a single, consistent interface:
- PostgreSQL 10+
- MySQL 5.7+ / MariaDB
- SQLite 3
- MongoDB 4.0+
- Redis 5.0+
- ElasticSearch

**Why This Matters:**
Modern applications often use specialized databases for different needs (PostgreSQL for relational data, Redis for caching, MongoDB for documents). WhoDB manages them all with one tool and one UI paradigm.

### pgAdmin - PostgreSQL Only

pgAdmin exclusively supports:
- PostgreSQL 9.6+
- EDB Postgres Advanced Server

**Limitation:** If your infrastructure includes other databases, you need separate tools.

**Real-World Impact:**
- PostgreSQL + Redis cache → Need pgAdmin + Redis CLI/UI
- PostgreSQL + MongoDB → Need pgAdmin + MongoDB Compass
- Mixed stack → 5+ different tools

## Use Case Comparison

**WhoDB Advantages:**
- Faster startup (critical for frequent connections)
- Simpler interface for basic CRUD
- Mock data generation for testing
- Lightweight, resources available for development
- Intuitive even for junior developers

**pgAdmin Advantages:**
- More detailed query information
- Advanced profiling tools

**Winner:** WhoDB (20-30% faster workflow)

**WhoDB Advantages:**
- Cleaner interface for basic admin
- Better performance
- Multi-database support

**pgAdmin Advantages:**
- More comprehensive admin features
- Advanced replication tools
- Role permission management depth
- Complex backup strategies
- Logical replication configuration

**Winner:** pgAdmin (for complex DBA tasks)

**WhoDB Advantages:**
- Faster data access
- Mock data for query testing
- Cleaner export interface
- Better performance with large datasets
- Visual filtering

**pgAdmin Advantages:**
- More export format options
- Advanced result filtering

**Winner:** WhoDB (faster, cleaner workflow)

**WhoDB Advantages:**
- Visual schema graph
- Interactive relationship exploration
- Instant understanding
- Export diagrams

**pgAdmin Advantages:**
- Detailed schema information
- Advanced properties

**Winner:** WhoDB (visual approach better for understanding)

**WhoDB:**
- New team member productive in 10-15 minutes
- Intuitive interface
- Minimal training
- Self-explanatory features

**pgAdmin:**
- New team member needs 4-8 hours training
- Complex interface
- Many learning curve hurdles
- Feature discovery required

**Winner:** WhoDB (5x faster onboarding)

**WhoDB:**
- Perfect for development (fast, simple)
- Good for production read-only access
- Mock data for testing environments

**pgAdmin:**
- Appropriate for both
- Advanced features available when needed
- More comprehensive monitoring

**Winner:** Tie (depends on use case)

## Performance Deep Dive

### Startup Performance

When you need to check something quickly in a database, startup time matters:

**pgAdmin Startup Sequence:**
1. Application initialization (1-2 seconds)
2. Python environment startup (1-2 seconds)
3. Component loading (2-3 seconds)
4. First page render (1-2 seconds)
**Total: 5-8+ seconds**

**WhoDB Startup Sequence:**
1. Go binary initialization (\

Ask yourself:
- Do I need PostgreSQL-specific admin features?
- Am I using other databases alongside PostgreSQL?
- What am I doing most frequently (daily work vs. occasional admin)?

If mostly daily development/queries: WhoDB is great
If mostly PostgreSQL admin: Consider keeping pgAdmin
Many teams use both for different workflows.

```
# Docker
docker run -p 8080:8080 clidey/whodb:latest

# Or binary
wget https://github.com/clidey/whodb/releases/download/v1.0/whodb
chmod +x whodb
./whodb
```
Takes less than 5 minutes.

Use the same connection credentials you use in pgAdmin:
- Host, port, username, password
- WhoDB recognizes them immediately
- No data migration needed

Spend 15-30 minutes exploring:
- Sidebar navigation (similar to pgAdmin tree)
- Data grid (like pgAdmin but faster)
- Scratchpad editor (similar to pgAdmin query tool)
- Schema visualization (new feature)

Map your common tasks:
- **Browse tables** → Click table in sidebar → View data
- **Run queries** → Scratchpad tab → Write SQL → Execute
- **Edit data** → Click cell → Edit inline or dialog
- **Export data** → Right-click table → Export

All familiar, cleaner interface.

Options:
- **Full switch:** Move completely to WhoDB
- **Hybrid:** WhoDB for daily work, pgAdmin for admin tasks
- **Gradual:** Run both during transition period

Most teams prefer hybrid approach.

**Total Migration Time:** 1-2 hours
**Team Training:** 15-30 minutes per person
**Productivity Gain:** 20-40% faster daily workflows

## Security and Compliance

Both tools support enterprise security features:

| Feature | WhoDB | pgAdmin |
|---------|:----:|:-------:|
| **SSL/TLS** | Yes | Yes |
| **SSH Tunneling** | Yes | Limited |
| **Custom Certificates** | Yes | Yes |
| **Certificate Pinning** | Yes | Limited |

| Feature | WhoDB | pgAdmin |
|---------|:----:|:-------:|
| **Database Auth** | Yes | Yes |
| **LDAP** | EE only | Yes |
| **OAuth** | EE only | No |
| **Kerberos** | No | Yes |
| **SAML** | EE only | No |

pgAdmin has more traditional enterprise auth. WhoDB Enterprise adds modern auth.

| Feature | WhoDB | pgAdmin |
|---------|:----:|:-------:|
| **Query Logging** | Yes | Yes |
| **User Actions** | Yes | Yes |
| **Change Tracking** | Yes | Limited |
| **Audit Export** | Yes | Limited |
| **Compliance Reports** | EE | Limited |

WhoDB Enterprise has better audit logging for compliance.

## Feature Comparison: Detailed Analysis

**WhoDB:**
- Syntax highlighting
- Auto-completion
- Execution time display
- Basic explain plans
- Query history
- Multi-cell notebooks

**pgAdmin:**
- All of above, plus:
- Advanced explain plans with visualization
- Detailed query profiling
- Cost analysis
- Index suggestions
- Performance recommendations

**Verdict:** pgAdmin for advanced query analysis, WhoDB for fast execution

**WhoDB:**
- Role creation
- Permission management
- Grant/revoke operations
- User listing

**pgAdmin:**
- All of above, plus:
- Complex privilege hierarchies
- Inheritance chains
- Template roles
- Fine-grained permissions
- Advanced role properties

**Verdict:** pgAdmin for complex permission structures, WhoDB for standard setups

**WhoDB:**
- Simple backup/restore interface
- Database export
- Table-level backup
- SQL export

**pgAdmin:**
- All of above, plus:
- Full backup options
- Scheduled backups UI
- Advanced pg_dump parameters
- Parallel backup support
- Selective restore options

**Verdict:** pgAdmin for complex backup strategies, WhoDB for standard backups

**WhoDB:**
- Basic replication monitoring
- Connection configuration
- Status viewing

**pgAdmin:**
- Streaming replication setup
- Logical replication configuration
- Failover management
- Detailed replication monitoring

**Verdict:** pgAdmin for replication setup, WhoDB for monitoring

## Real-World Scenarios

### Scenario 1: Development Team Productivity

**Situation:** Development team using pgAdmin spends 2-3 hours daily on database tasks.

**Problem with pgAdmin:**
- 5+ second startup for each connection
- Complex interface for simple queries
- No mock data for testing
- Slower data viewing/editing
- Steep onboarding for new developers

**WhoDB Solution:**
- \

- **Daily work:** WhoDB (faster, simpler)
- **Admin tasks:** pgAdmin (comprehensive features)
- **Best of both:** Speed for common work, features when needed
- **Typical split:** 80% WhoDB, 20% pgAdmin

- Small teams that don't need advanced admin features
- Development-focused shops
- Want standardized tooling across databases
- Prefer simpler, faster interface

- PostgreSQL specialists who need all features
- Complex administration requirements
- Enterprise deployments with heavy DBA needs
- Willing to accept slower performance

## Pricing Comparison

| Aspect | WhoDB | pgAdmin |
|--------|-------|---------|
| **Base Cost** | Free | Free |
| **Source Code** | Open Source | Open Source |
| **Licensing** | Permissive | PostgreSQL License |
| **Hosting** | Self-hosted | Self-hosted |
| **Support** | Community | Community |
| **Enterprise Edition** | Available | N/A |
| **Enterprise Support** | Yes | No |
| **Per-User Cost** | None | None |

**Cost Analysis:**
- Community users: Both completely free
- Enterprise users: WhoDB Enterprise available with support
- Infrastructure: WhoDB cheaper due to lighter resource usage

## Final Verdict

- Value speed and simplicity
- Work with multiple databases
- Deploy to containers/Kubernetes
- Frequent context switching between databases
- Want faster onboarding for teams
- Need mock data generation
- Prefer modern UI
- Resource-constrained environments

- PostgreSQL specialist/DBA
- Need advanced admin features
- Complex replication setup
- Advanced query profiling
- Detailed monitoring needs
- Comfortable with complexity
- Already invested in pgAdmin
- Don't need other database types

## Best Practices for Both

**Development Teams:**
- Use WhoDB as primary tool
- 20-30% faster workflow
- Better new developer onboarding
- Keep pgAdmin for complex admin tasks

**DBA Teams:**
- Use pgAdmin for administration
- Use WhoDB for daily monitoring/browsing
- Best of both worlds approach

**Mixed Teams:**
- Developers use WhoDB
- DBAs use pgAdmin
- Architects use both as needed

**Docker/Kubernetes:**
- WhoDB is optimal choice
- Lighter resource footprint
- Scales better
- Simpler deployment manifests

**On-Premises:**
- Either tool works
- pgAdmin might already be deployed
- WhoDB more efficient

**Cloud (AWS, GCP, Azure):**
- WhoDB preferred
- Cost-efficient
- Simpler management

## Try WhoDB Today

Ready to experience faster PostgreSQL management?

Get started in under 5 minutes

Try it without installing

## Common Questions

Yes! WhoDB works with PostgreSQL 10+ (same versions pgAdmin supports). Use your existing connection details—no data migration needed.

Absolutely! Many teams do exactly this. Use WhoDB for daily work and pgAdmin for complex administration tasks. They work perfectly alongside each other.

WhoDB has 95% of pgAdmin's features for common use cases. pgAdmin has additional PostgreSQL-specific admin features DBAs need. For most developers and analysts, WhoDB has everything needed.

WhoDB supports all major PostgreSQL features: arrays, JSONB, full-text search, partitioning, extensions, etc. pgAdmin provides more configuration options for these features, but both handle them well.

WhoDB: 10-15 minutes for basic productivity
pgAdmin: 4-8 hours for basic proficiency

If you're already a pgAdmin expert, you'll find WhoDB's interface refreshingly simple. If new to database tools, WhoDB is dramatically easier.

Yes, WhoDB supports configuring and monitoring replication. pgAdmin provides more detailed replication management. For most deployments, WhoDB's replication support is sufficient.

**Pro Tip:** Start with WhoDB Community Edition (free) for daily PostgreSQL work. Keep pgAdmin installed for advanced administration tasks. This hybrid approach gives you the speed and simplicity you need with the comprehensive features available when required.

---

**Ready to speed up your PostgreSQL workflow?** [Install WhoDB now](/installation) and discover how modern database management should work.

---


# resources/comparisons/vs-dbeaver.mdx
## WhoDB vs DBeaver: Complete Comparison

Compare WhoDB and DBeaver database tools. Features, performance, database support, pricing, and when to use each tool for data management.

# WhoDB vs DBeaver: Complete Comparison

DBeaver is one of the most powerful database management tools available, supporting 50+ database types. If you manage diverse database environments, you've likely considered DBeaver. This comprehensive comparison will help you decide between WhoDB and DBeaver for your database management needs.

## Quick Summary

**The Lightweight Modern Alternative**

Fast, intuitive, web-based, multi-database support, perfect for teams

**Best for:** Fast workflow, teams, modern infrastructure, development work

**The Comprehensive Powerhouse**

50+ databases, advanced features, powerful query tools, desktop application

**Best for:** Power users, diverse database environments, complex operations

## Performance Comparison

The first thing you notice when using these tools is performance. DBeaver prioritizes features over speed, while WhoDB prioritizes speed:

| Metric | WhoDB | DBeaver |
|--------|-------|---------|
| **Startup Time** | \
If you need support for 20+ different database types, DBeaver is necessary. WhoDB covers the most common databases but can't compete on breadth.

## User Interface and Experience

### WhoDB Interface

- Clean, minimalist interface inspired by modern SaaS
- Responsive design that works on any device
- Dark mode support
- Keyboard shortcuts for efficiency
- Right-click context menus
- Intuitive data grid
- Interactive schema visualization
- Accessible on any browser

**Advantages:**
- Works anywhere (home, office, mobile)
- Consistent experience across platforms
- No installation required
- Team collaboration friendly
- Easy updates

**Disadvantages:**
- Limited offline capabilities
- Requires browser
- No native OS integration

### DBeaver Interface

- Native desktop experience
- Hundreds of features visible/discoverable
- Tabbed interface for multiple connections
- Extensive configuration options
- Plugin system for extensions
- Deep integration with OS
- Professional development tool feel

**Advantages:**
- Works offline after download
- More responsive (no browser overhead)
- Familiar for developers
- More screen real estate options
- Deep feature access

**Disadvantages:**
- Must install (takes time and space)
- Different experience per OS
- Desktop-only (can't use on iPad, tablet)
- Slower startup
- Heavier resource usage

## Feature Comparison

| Feature | WhoDB | DBeaver |
|---------|:----:|:-------:|
| **Syntax Highlighting** | Yes | Yes |
| **Auto-Completion** | Yes | Yes |
| **Query History** | Searchable | Extensive |
| **Query Bookmarks** | Yes | Yes |
| **Multi-Query Execution** | Multi-cell | Batch |
| **Explain Plans** | Database-specific | Advanced |
| **Result Export** | 4 formats | 10+ formats |
| **Execution Statistics** | Yes | Yes |
| **Visual Query Builder** | No | Yes |
| **Query Formatting** | Auto-format | Advanced |
| **Snippets/Templates** | Limited | Extensive |

| Feature | WhoDB | DBeaver |
|---------|:----:|:-------:|
| **Add Records** | Clean dialog | Form or inline |
| **Edit Records** | Inline/dialog | Multiple modes |
| **Delete Records** | Safe | Multiple options |
| **Bulk Operations** | Select & act | Advanced options |
| **Filtering** | Visual builder | Multiple options |
| **Column Filtering** | Real-time | Advanced |
| **Sorting** | Click columns | Advanced |
| **Search** | Real-time | Comprehensive |
| **Copy/Paste** | Yes | Yes |
| **Mock Data** | Built-in | Plugin available |

| Feature | WhoDB | DBeaver |
|---------|:----:|:-------:|
| **Schema Diagram** | Interactive graph | ERD diagrams |
| **Relationship View** | Yes | Yes |
| **Dependencies** | Yes | Yes |
| **Export Diagrams** | Yes | Yes |
| **Customization** | Basic | Advanced |
| **Multiple Views** | Yes | Yes |

| Feature | WhoDB | DBeaver |
|---------|:----:|:-------:|
| **User Management** | Yes | Yes |
| **Role Management** | Yes | Yes |
| **Privilege Management** | Yes | Yes |
| **Database Creation** | Yes | Yes |
| **Backup/Restore** | Yes | Advanced |
| **Replication Setup** | Yes | Limited |
| **Monitoring** | Yes | Yes |
| **Performance Tools** | Yes | Advanced |
| **Server Configuration** | Yes | Limited |

| Feature | WhoDB | DBeaver |
|---------|:----:|:-------:|
| **Desktop (Windows)** | No | Yes |
| **Desktop (Mac)** | No | Yes |
| **Desktop (Linux)** | No | Yes |
| **Web-Based** | Yes | No |
| **Docker** | Yes | No |
| **Kubernetes** | Yes | No |
| **Docker Compose** | Yes | No |
| **Cloud Native** | Yes | No |

## Use Case Analysis

**Scenario:** Managing PostgreSQL, MySQL, Oracle, SQL Server, MongoDB, Redis

**WhoDB:**
- Handles PostgreSQL, MySQL, SQLite, MongoDB, Redis well
- Would need WhoDB Enterprise for Oracle, SQL Server
- Simple consistent interface across databases
- Fast switching between databases

**DBeaver:**
- Handles all 6+ databases natively
- Many options and configurations
- Comprehensive feature set
- More complex interface

**Verdict:** DBeaver wins for 10+ different database types. For top 5 most common databases, WhoDB is simpler.

**Scenario:** Developers running queries, inspecting data, writing tests

**WhoDB:**
- \

**Scenario:** Complex queries, query optimization, performance analysis

**WhoDB:**
- Syntax highlighting and auto-completion
- Execution statistics
- Query history
- Simple, fast execution

**DBeaver:**
- Advanced explain plans
- Query profiling
- Visual query builder
- Performance analysis tools
- Optimization suggestions

**Verdict:** DBeaver wins for complex query work

**Scenario:** Analyzing data, exporting to multiple formats

**WhoDB:**
- CSV, Excel, JSON, SQL export
- Simple export interface
- Mock data for testing queries
- Fast data access
- Visual filtering

**DBeaver:**
- 10+ export formats
- Advanced export options
- Result transformations
- Scheduled exports

**Verdict:** DBeaver for many export formats, WhoDB for common use cases

**Scenario:** Working from different locations, using various devices

**WhoDB:**
- Browser-based, works anywhere
- Laptop, desktop, even tablet browser
- Consistent experience
- SSH tunneling support
- VPN friendly

**DBeaver:**
- Desktop application only
- Windows, Mac, Linux versions differ
- Can't use on iPad, Android
- Requires installation on each device
- SSH tunneling available

**Verdict:** WhoDB wins decisively for remote/mobile

**Scenario:** Team of developers sharing database access

**WhoDB:**
- Central web server
- Same experience for all users
- Easy access control
- Browser-based (no installation)
- Easier team onboarding

**DBeaver:**
- Each user installs separately
- Individual configuration
- Desktop resource usage per user
- No native team features
- More installation/support overhead

**Verdict:** WhoDB wins for team efficiency

**Scenario:** Working with niche databases (Neo4j, Cassandra, Trino, etc.)

**WhoDB:**
- Limited support (maybe 1-2 of these)
- Would need different tools

**DBeaver:**
- Excellent support for niche databases
- Plugin ecosystem
- Custom JDBC support
- One tool for everything

**Verdict:** DBeaver absolutely wins

## Deployment Architecture

### WhoDB Architecture

**Lightweight, Cloud-Native:**
- Single Go binary (~15MB)
- Runs on Linux, Mac, Windows
- Docker image: 50MB
- Browser-based access
- Stateless server design
- Scales horizontally
- Perfect for Kubernetes

**Resource Profile:**
- \

Modern SaaS design patterns. If you use Figma, Notion, or modern web apps, you'll find WhoDB intuitive.

Everything is discoverable. Sidebar navigation, data grid, query editor, schema graph all have clear purposes.

Write query → hit Enter → see results. No waiting, no configuration.

Most developers can run their first query in 5 minutes, navigate data in 10 minutes, and be fully productive by 15 minutes.

### DBeaver Onboarding

**Time to Productivity:** 4-8 hours

Many options, many ways to do things. Multiple right-click menus, multiple tabs, extensive configuration.

Features aren't always obvious. You need to explore or read documentation to find what you need.

Many settings to adjust for optimal workflow. Performance tuning, UI customization, preferences.

You become more productive over days and weeks as you learn features. Desktop app paradigm requires adjustment for some users.

## Pricing Comparison

| Aspect | WhoDB | DBeaver |
|--------|-------|---------|
| **Community Edition** | Free, Open Source | Free (feature-limited) |
| **Enterprise Edition** | Custom pricing | $199/year per user |
| **Team Edition** | Custom | Custom pricing |
| **Features Difference** | Significant | Significant |
| **Per-User Cost** | 0 (CE) / varies (EE) | $199/year standard |
| **Support** | Community (CE) / Included (EE) | Community or paid support |
| **License** | Permissive | Proprietary |

**Cost Analysis:**
- Single user: WhoDB free vs. DBeaver $199/year
- Small team (5 people): WhoDB free vs. DBeaver $995/year
- Large team (50 people): WhoDB free vs. DBeaver $9,950/year
- Enterprise scale: WhoDB custom vs. DBeaver custom

## Real-World Scenarios

### Scenario 1: Startup with Limited Budget

**Situation:** Early-stage startup with limited resources, PostgreSQL + MongoDB + Redis

**WhoDB Approach:**
- Free Community Edition covers all needs
- Fast development workflow
- Minimal infrastructure
- Zero licensing costs
- Easy deployment

**DBeaver Approach:**
- Free Community Edition available
- Desktop apps on developer machines
- Each developer: $0 (free version) to $199 (pro)
- Still supports all databases

**Result:** WhoDB wins on cost and simplicity

### Scenario 2: Enterprise with 20+ Databases

**Situation:** Large enterprise using PostgreSQL, Oracle, SQL Server, Cassandra, Neo4j, Elasticsearch, etc.

**WhoDB Approach:**
- Can't natively support all
- Would need multiple tools
- More complexity

**DBeaver Approach:**
- One tool for all databases
- Comprehensive feature set
- All databases in one place
- Enterprise support available

**Result:** DBeaver wins decisively for this use case

### Scenario 3: Remote-First Distributed Team

**Situation:** Team of 10 developers across 4 time zones accessing databases

**WhoDB Approach:**
- Central server, browser access
- Same experience for all users
- Easy to scale
- No per-machine configuration
- Mobile-friendly
- Instant onboarding

**DBeaver Approach:**
- Each developer installs desktop app
- 10 separate installations to manage
- Different OS versions create support burden
- More difficult to maintain consistency
- Harder onboarding

**Result:** WhoDB wins for team efficiency

### Scenario 4: Power User Analytics

**Situation:** Data analyst doing complex queries, exports, and data manipulation

**WhoDB Approach:**
- Simple interface, fast queries
- Good export options (CSV, Excel, JSON)
- Adequate for most analysis
- Mock data for testing

**DBeaver Approach:**
- Advanced query tools
- 10+ export formats
- Complex data transformations
- Result set manipulation
- Advanced features for analysis

**Result:** DBeaver wins for advanced analytics

## Hybrid Usage Patterns

Many organizations use both tools:

**WhoDB for:**
- Daily development work
- Quick queries and data inspection
- Team collaboration
- Mobile/remote access
- Testing and mock data

**DBeaver for:**
- Specialized databases
- Complex query optimization
- Advanced data analysis
- One-off specialized tasks
- Power user features

**Result:** Best of both worlds—speed for common work, power when needed

## Migration from DBeaver to WhoDB

If you're considering switching:

- What percentage of time do you need advanced features?
- Do you use niche databases?
- How often do you switch between databases?
- Would your team benefit from web-based access?

If >80% common databases and workflows, WhoDB is great. If many niche databases, stick with DBeaver.

Install WhoDB while keeping DBeaver. Use WhoDB for common tasks, DBeaver for specialized work.

Train team on WhoDB. Measure productivity gains. Typically 20-30% faster for daily work.

- **Full switch:** If you don't need DBeaver's databases
- **Hybrid:** Keep both (many teams do this)
- **Niche tools:** Specialize each tool

Most teams choose hybrid approach.

## Security and Compliance

Both support SSL/TLS, SSH tunneling, and certificate management.
- WhoDB: Cleaner certificate interface
- DBeaver: More configuration options  
**Winner:** Tie (both adequate)

- WhoDB: Database auth, HTTP basic auth, plus OAuth/LDAP in Enterprise Edition
- DBeaver: Database auth, extensive options, more built-in integrations  
**Winner:** DBeaver (more options), but most users only need basic auth

- WhoDB: Browser-based (default), optional server storage
- DBeaver: Desktop-based local storage  
**Winner:** Tie (different approaches, both secure when configured properly)

- WhoDB: Built-in query/change logging, better in Enterprise Edition
- DBeaver: Limited logging  
**Winner:** WhoDB (better audit support)

## Recommended Pairings

### If You Use:

**WhoDB Alone**

All databases supported, fast, simple interface. DBeaver unnecessary.

**WhoDB + DBeaver**

WhoDB for PostgreSQL/MySQL, DBeaver for Oracle. Or DBeaver alone if you prefer.

**DBeaver Alone**

DBeaver necessary for breadth. WhoDB can supplement for common databases.

**WhoDB**

Simple, fast, perfect for PostgreSQL. Optional pgAdmin for advanced admin.

## Final Verdict

- Work mostly with common databases (PostgreSQL, MySQL, SQLite, MongoDB, Redis)
- Value speed and simplicity
- Use Docker/Kubernetes
- Work in distributed/remote teams
- Need web-based access
- Prefer modern UI
- Limited budget
- Want fast developer onboarding
- Don't need niche database support

- Manage 10+ different database types
- Use specialized/niche databases
- Need advanced query tools
- Do complex data analysis
- Want maximum feature richness
- Prefer desktop application
- Budget allows for licenses
- Need visual query builder
- Power user who needs everything

## Try WhoDB Today

Ready to experience faster, simpler database management?

Get started in under 5 minutes

Try without installing

## Common Questions

If you work exclusively with common databases (PostgreSQL, MySQL, SQLite, MongoDB, Redis), absolutely. If you need databases DBeaver supports that WhoDB doesn't, keep DBeaver. Many teams use both effectively.

No. DBeaver has more advanced query tools, more export formats, visual query builder, and more database support. WhoDB focuses on the 95% of features most users actually need, making them faster and simpler.

For a developer checking 10 tables: DBeaver takes 3-5 minutes (including startup time), WhoDB takes 30-45 seconds. Over a day's work, WhoDB saves 1-2 hours from startup and switching alone.

Yes, absolutely. Many teams do exactly this. Use WhoDB for speed with common databases, DBeaver for niche databases or specialized work. Complementary tools.

Both are production-ready and reliable. DBeaver is more mature (longer history). WhoDB is actively developed and used by thousands. Pick based on features needed, not reliability concerns.

DBeaver works offline after installation (desktop app). WhoDB requires server and browser connection. If you frequently work offline, DBeaver is necessary. For most remote work with internet, WhoDB is fine.

**Pro Tip:** Start with WhoDB Community Edition (free) for your primary databases. If you discover you need DBeaver's specific features, add it to your toolkit. Many successful teams use both strategically.

---

**Ready to speed up your database work?** [Install WhoDB now](/installation) and discover how much faster database management can be.

---


# resources/comparisons/vs-tableplus.mdx
## WhoDB vs TablePlus: Complete Comparison

Compare WhoDB and TablePlus database management tools. Feature differences, pricing, performance, UI comparison, and which is best for your workflow.

# WhoDB vs TablePlus: Complete Comparison

TablePlus is a modern, elegant database management tool that has gained popularity among developers who appreciate clean design and native performance. If you're choosing between WhoDB and TablePlus, this comprehensive comparison will help you make the right decision.

## Quick Summary

**The Web-Based Solution**

Fast, free, web-based, multi-database support, team-friendly

**Best for:** Teams, Docker deployments, cross-platform access, open source projects

**The Native Desktop Experience**

Elegant, polished, fast native app, excellent macOS integration, one-time payment

**Best for:** Individual developers, Mac users, native app enthusiasts, professionals willing to pay

## Head-to-Head Comparison

| Aspect | WhoDB | TablePlus |
|--------|-------|-----------|
| **Price** | Free (Open Source) | $89-$129 (lifetime) |
| **Startup Time** | \

- Access from anywhere with browser
- Works on Mac, Windows, Linux, iPad
- Central server deployment
- Team access from single URL
- No installation on client machines
- Scales horizontally
- Updates server-side (instant for all users)

**Advantages:**
- Access from anywhere (office, home, mobile)
- Same experience across all devices
- Easy team scaling
- One server to maintain
- Mobile/iPad support
- Instant updates for all users
- Work-from-anywhere friendly

**Disadvantages:**
- Requires internet connection
- Server needs to stay running
- Can't access completely offline

### TablePlus: Native Desktop

- Download and install on each device
- Works offline after installation
- Native OS integration
- Traditional desktop app paradigm
- Individual configuration per user
- No server required
- Download updates as released

**Advantages:**
- Works completely offline
- Native OS performance
- Desktop-first experience
- No server infrastructure needed
- Individual user control
- Direct database access
- OS integration (Dock, system menu, etc.)

**Disadvantages:**
- Must install on each device
- Different versions per OS (Mac/Windows/Linux differ)
- Team setup complexity
- Offline-first, not team-first
- Manual update management

## Price and Value Analysis

### WhoDB Pricing

| Edition | Cost | Per User | Best For |
|---------|------|----------|----------|
| Community | Free | $0 | Individual projects, small teams, open source |
| Enterprise | Custom | Varies | Large teams, enterprise features, support |

**Cost for Different Team Sizes:**
- Solo developer: $0/month
- Small team (5): $0/month
- Team (10): $0/month (Community) or custom (Enterprise)
- Enterprise (50+): Custom pricing

### TablePlus Pricing

| Edition | Cost | Per User | Best For |
|---------|------|----------|----------|
| Standard | $89 | Per user | Individual developers |
| Pro | $129 | Per user | Professionals, advanced features |

**Cost for Different Scenarios:**
- Solo developer: $89-$129 one-time
- Small team (5): $445-$645 total (one-time)
- Team (10): $890-$1,290 total (one-time)
- Team (50): $4,450-$6,450 total (one-time)

**Cost Analysis:**
- 1 developer: TablePlus costs $89
- 5 developers: WhoDB saves $445-$645
- 10 developers: WhoDB saves $890-$1,290
- Break-even: ~1 developer using WhoDB for 1 year equals TablePlus one-time cost

## Performance Comparison

Both tools are fast, but in different ways:

### Startup Performance

| Action | WhoDB | TablePlus |
|--------|-------|-----------|
| Application startup | \

| Feature | WhoDB | TablePlus |
|---------|:----:|:--------:|
| **Browse Tables** | Yes | Yes |
| **Edit Data** | Yes | Yes |
| **Add Records** | Yes | Yes |
| **Delete Records** | Yes | Yes |
| **Inline Editing** | Yes | Yes |
| **Bulk Operations** | Yes | Limited |
| **Sorting** | Click column | Click column |
| **Filtering** | Visual builder | Column filtering |
| **Search** | Real-time | Limited |
| **Favorites** | Yes | Yes |
| **Connection Profiles** | Yes | Yes |
| **Keyboard Shortcuts** | Extensive | Extensive |

| Feature | WhoDB | TablePlus |
|---------|:----:|:--------:|
| **SQL Editor** | Yes | Yes |
| **Syntax Highlighting** | Yes | Yes |
| **Auto-completion** | Yes | Yes |
| **Query History** | Searchable | Recent queries |
| **Saved Queries** | Yes | Yes |
| **Multi-Query** | Multi-cell | Sequential |
| **Export Results** | Yes | Yes |
| **Execution Time** | Yes | Yes |

| Feature | WhoDB | TablePlus |
|---------|:----:|:--------:|
| **CSV** | Yes | Yes |
| **JSON** | Yes | Yes |
| **SQL** | Yes | Yes |
| **Excel** | Yes | Yes |
| **XML** | No | Yes |
| **PDF** | No | Yes |
| **Column Selection** | Yes | Yes |
| **Custom Delimiters** | Yes | Yes |
| **Filter Before Export** | Yes | Yes |

| Feature | WhoDB | TablePlus |
|---------|:----:|:--------:|
| **User Management** | Yes | Yes |
| **Privilege Management** | Yes | Yes |
| **Database Creation** | Yes | Yes |
| **Index Management** | Yes | Yes |
| **Constraint Management** | Yes | Yes |
| **View Management** | Yes | Yes |
| **Backup/Restore** | Limited | Limited |

| Feature | WhoDB | TablePlus |
|---------|:----:|:--------:|
| **Schema Graph** | Interactive | Basic |
| **Relationships** | Yes | Yes |
| **ERD Diagrams** | Yes | No |
| **Pan/Zoom** | Yes | Limited |
| **Export Diagram** | Yes | No |

## Unique Features

### WhoDB Unique Features

Built-in realistic test data generation for all column types. Perfect for testing. TablePlus doesn't have this.

Visual schema exploration with clickable nodes and relationships. Helps understand complex databases instantly. TablePlus has basic visualization only.

Organize queries into cells like Jupyter notebooks. Great for complex workflows. TablePlus doesn't have this.

Central web server for team access. Share database connections easily. TablePlus requires individual setup.

Access from any browser anywhere. Perfect for distributed teams. TablePlus is desktop-only.

### TablePlus Unique Features

Beautifully designed native application. Integrates with OS perfectly. Feels like a professional tool. WhoDB is browser-based.

Works completely offline after installation. Access databases without internet. WhoDB requires internet connection.

PDF, XML export options beyond WhoDB's capabilities. More export flexibility.

Dock integration, system menu, notification support. Deep OS integration. WhoDB is browser-based so no system integration.

Open multiple database connections side by side. Multi-window support. WhoDB uses browser tabs.

## User Interface Comparison

### WhoDB Interface

**Strengths:**
- Very clean, minimalist design
- Consistent across all platforms
- Dark mode support
- Spreadsheet-like data grid
- Intuitive for new users
- Schema visualization included
- Keyboard-driven workflow

**Trade-offs:**
- Browser interface (not native)
- Limited OS integration
- Requires learning browser navigation

### TablePlus Interface

**Strengths:**
- Beautiful, polished native design
- Excellent macOS integration
- Professional appearance
- Native application feel
- OS-specific optimizations
- Context menus match OS conventions

**Trade-offs:**
- Different interface per OS
- Not as consistent across platforms
- Less visible schema visualization

## Learning Curve and Onboarding

### WhoDB Onboarding

**Time to Productivity:** 10-15 minutes

- Modern SaaS design patterns (familiar if you use any web app)
- Intuitive sidebar navigation
- Self-explanatory data grid
- Click to explore features

### TablePlus Onboarding

**Time to Productivity:** 10-15 minutes

- Native app experience (familiar if you use desktop apps)
- Context menus and traditional app navigation
- Quick feature discovery
- Professional tool feel

**Verdict:** Both equally easy to learn, different paradigms
- Choose TablePlus if you prefer desktop app conventions
- Choose WhoDB if you prefer web app conventions

## Use Case Comparison

**WhoDB:**
- Free option (no purchase necessary)
- Works on any device including iPad
- Always up to date
- Could share databases with teammates

**TablePlus:**
- $89-$129 one-time purchase
- Beautiful macOS native experience
- Works offline
- Professional tool feel

**Recommendation:** TablePlus if you value native experience and don't mind paying. WhoDB if you prefer free and multi-device access.

**WhoDB:**
- Huge cost advantage ($0 vs. $445+)
- Central deployment, easy onboarding
- All team members have consistent experience
- Easy scaling to larger team
- Supports remote work

**TablePlus:**
- Each developer needs license ($89-$129 each)
- Individual setup on each machine
- Team features limited
- Different experience per OS

**Recommendation:** WhoDB wins decisively for teams

**WhoDB:**
- Access from anywhere via browser
- Works on any device
- No installation needed anywhere
- Always up to date
- Mobile/tablet friendly

**TablePlus:**
- Can install on each device
- Works offline after install
- Requires installation at each location
- Heavier to set up when traveling

**Recommendation:** WhoDB for remote workers

**WhoDB:**
- Works great locally
- Can run on same machine as database
- Docker support for databases
- Free

**TablePlus:**
- Excellent local experience
- Native performance
- Beautiful interface
- Professional feel

**Recommendation:** Tie - both excellent, choose based on preference

**WhoDB:**
- Good for basic admin
- Visual schema helps
- Mock data for testing scenarios
- Team access for shared admin

**TablePlus:**
- Excellent for standard admin
- Multiple windows for complex operations
- Native performance
- Good feature set

**Recommendation:** TablePlus slightly better for pure admin work

**WhoDB:**
- Mock data to test queries
- Good export options
- Visual filtering
- Fast and responsive

**TablePlus:**
- More export formats (PDF, XML)
- Good filtering
- Quick export process
- Professional results

**Recommendation:** Tie - both excellent, TablePlus slightly more export options

## Security and Compliance

Both tools support secure database access:

| Feature | WhoDB | TablePlus |
|---------|:----:|:--------:|
| **SSL/TLS** | Yes | Yes |
| **SSH Tunnel** | Yes | Yes |
| **Certificate Support** | Yes | Yes |
| **Custom Certs** | Yes | Yes |

| Feature | WhoDB | TablePlus |
|---------|:----:|:--------:|
| **Database Auth** | Yes | Yes |
| **LDAP** | EE only | No |
| **OAuth** | EE only | No |
| **Local Auth** | Yes | Yes |

**WhoDB:**
- Browser-stored profiles
- Optional server storage
- Environment variables
- Cleared on logout

**TablePlus:**
- Local desktop storage
- Encrypted credentials
- System keychain integration

**Verdict:** Both secure. TablePlus has system keychain integration. WhoDB Enterprise has LDAP/OAuth.

## Real-World Scenarios

### Scenario 1: Freelance Developer

**Situation:** Solo developer, works from multiple locations

**WhoDB:**
- Free ($0)
- Works anywhere (browser access)
- Can work from coworking space, home, client office
- Mobile access for quick checks
- Always updated
- **Cost: $0**

**TablePlus:**
- One-time purchase ($89-$129)
- Must install on each device
- Works offline once installed
- Beautiful macOS experience
- Manual updates
- **Cost: $89-$129**

**Result:** Both work well. WhoDB saves money and has location flexibility. TablePlus has native beauty.

### Scenario 2: Small Startup (5-10 developers)

**Situation:** Growing team, shared database access

**WhoDB:**
- Free for all developers
- Central deployment
- Easy to onboard new developers
- Consistent experience
- Scales as team grows
- **Cost: $0**

**TablePlus:**
- $89-$129 per developer
- Each person manages own installation
- Different experience per developer's OS
- Harder to standardize
- **Cost: $445-$1,290+**

**Result:** WhoDB wins dramatically for teams

### Scenario 3: Enterprise Team (50+ developers)

**Situation:** Large team with centralized IT

**WhoDB:**
- Free
- Single deployment to manage
- Easy IT management
- Scaling to 50+ users is straightforward
- Consistent across organization
- **Cost: $0 (CE) or custom (EE)**

**TablePlus:**
- $89-$129 per developer
- 50 separate installations
- Significant licensing cost
- Deployment complexity
- **Cost: $4,450-$6,450+**

**Result:** WhoDB vastly superior for large teams

### Scenario 4: Mac Developer Premium Experience

**Situation:** Individual Mac developer who values native apps

**WhoDB:**
- Web-based (not native Mac app)
- Good performance
- Multi-platform access
- Free
- **Cost: $0**

**TablePlus:**
- Native Mac app (beautiful integration)
- Excellent performance
- Dock integration, system menu
- Professional feel
- **Cost: $89-$129**

**Result:** TablePlus if native experience is priority, WhoDB if multi-platform or budget matters

## Migration Considerations

### From TablePlus to WhoDB

If you're considering switching:

Solo? TablePlus is fine. Team? WhoDB makes sense.

Install WhoDB and try it for a week alongside TablePlus. See if it meets your needs.

- 5 people on TablePlus: $445+ spent
- Same 5 on WhoDB: $0
- Savings grow with team

- Create connections in WhoDB
- Import saved queries
- Train team on interface (easy, ~15 minutes each)
- Run both during transition
- Cut over when comfortable

Many developers keep TablePlus for:
- Native app preference
- Offline work
- Specific workflows

Hybrid approach: WhoDB for team, TablePlus for individuals who prefer it.

### From WhoDB to TablePlus

If you need TablePlus specific features (offline, native app, PDF export):

- Export saved queries
- Set up connections in TablePlus
- Take advantage of rich features
- Keep WhoDB if team will use it

## Final Verdict

- Work in a team (2+ people)
- Use multiple devices
- Need remote access
- Deploy to Docker/Kubernetes
- Have limited budget
- Prefer web-based tools
- Want team standardization
- Need cross-platform consistency
- Value free and open source
- Work with diverse databases

- Solo developer or small solo team
- Prefer native desktop apps
- Mac is primary platform
- Work offline frequently
- Value professional app feel
- Want advanced export formats
- Don't need team features
- Budget allows for purchase
- Prefer one-time payment
- Like deep OS integration

## Hybrid Approach

Many developers use both tools strategically:

**Use WhoDB for:**
- Team database access
- Remote/distributed work
- Development databases
- Daily team workflows

**Use TablePlus for:**
- Local development
- Native app experience
- Offline access
- Personal workflows
- Advanced exports

**Result:** Maximum productivity and flexibility

## Try WhoDB Today

Ready to see if WhoDB meets your needs?

Get started in under 5 minutes

Try without installing

## Common Questions

If you're a solo developer who prefers web-based apps and multi-device access, yes. If you love native apps and work offline frequently, you might miss TablePlus's experience. Try WhoDB free first.

Both are fast for query execution. TablePlus slightly faster startup (native app). WhoDB faster after startup (optimized). For daily work, differences minimal. Most users don't notice the difference.

Yes, absolutely. Many developers do this. Use WhoDB for team/shared work and TablePlus for personal workflows. They work perfectly alongside each other.

TablePlus is the solution for offline-first work. WhoDB requires internet. If you work offline frequently, TablePlus is necessary. If occasionally offline, WhoDB is fine.

TablePlus has beautiful native Mac integration. WhoDB works great on Mac too (browser-based). Choose TablePlus if native experience is important, WhoDB if multi-device access is priority.

WhoDB is built for teams with central deployment. TablePlus is individual-focused. For 5+ person teams, WhoDB is dramatically better. For solo work, TablePlus is adequate.

For solo developers who value native apps and don't mind paying, yes. For teams, no (WhoDB is free and better for teams). For budget-conscious developers, WhoDB's free option is compelling.

**Pro Tip:** Try WhoDB free for a month. If you love it, great—you save money and get team features. If you prefer TablePlus's native experience, buy it. Both are excellent tools; choose based on your workflow and values.

---

**Ready to experience modern database management?** [Install WhoDB now](/installation) and discover if it's the right fit for you.

---


# resources/integrations/docker.mdx
## Docker Integration

Deploy and integrate WhoDB with Docker in production environments

# Docker Integration

WhoDB is designed with Docker-first deployment in mind, offering containerized deployment that simplifies installation, scaling, and integration with existing infrastructure. This guide covers advanced Docker configurations, networking, volume management, and production deployment strategies.

Docker provides the fastest and most reliable way to deploy WhoDB across any environment

## Quick Start

Get WhoDB running with a single command:

```
docker run -d \
  --name whodb \
  -p 8080:8080 \
  clidey/whodb:latest
```

Access WhoDB at `http://localhost:8080`

## Docker Compose Configuration

Docker Compose provides declarative configuration for complex deployments. Here's a comprehensive setup:

### Basic Docker Compose Setup

```
version: '3.8'

services:
  whodb:
    image: clidey/whodb:latest
    container_name: whodb
    ports:
      - "8080:8080"
    environment:
      - WHODB_PORT=8080
      - WHODB_LOG_LEVEL=info
    restart: unless-stopped
    volumes:
      - whodb-data:/data
    networks:
      - whodb-network

volumes:
  whodb-data:
    driver: local

networks:
  whodb-network:
    driver: bridge
```

Save as `docker-compose.yml` and run:

```
docker-compose up -d
```

### Production Docker Compose Setup

For production environments, add health checks, resource limits, and monitoring:

```
version: '3.8'

services:
  whodb:
    image: clidey/whodb:latest
    container_name: whodb-prod
    ports:
      - "8080:8080"
    environment:
      - WHODB_PORT=8080
      - WHODB_HOST=0.0.0.0
      - WHODB_LOG_LEVEL=warn
      # AI Integration
      - WHODB_ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
      - WHODB_OPENAI_API_KEY=${OPENAI_API_KEY}
      - WHODB_OLLAMA_HOST=${OLLAMA_HOST:-localhost}
      - WHODB_OLLAMA_PORT=${OLLAMA_PORT:-11434}
    restart: always
    volumes:
      - whodb-data:/data
      - ./logs:/var/log/whodb
    networks:
      - app-network
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2G
        reservations:
          cpus: '0.5'
          memory: 512M
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

volumes:
  whodb-data:
    driver: local

networks:
  app-network:
    external: true
```

Use environment variables from `.env` files to keep sensitive credentials out of your compose file

## Docker Networking

### Connecting to Databases on Host

When your database runs on the host machine and WhoDB runs in Docker, use special hostnames:

``` Linux
services:
  whodb:
    image: clidey/whodb:latest
    network_mode: host
    # Database on host accessible via localhost
```
``` macOS/Windows
services:
  whodb:
    image: clidey/whodb:latest
    # Use host.docker.internal to reach host services
    environment:
      - DATABASE_HOST=host.docker.internal
```

### Shared Docker Network

Connect WhoDB to databases in separate containers using a shared network:

```
version: '3.8'

services:
  postgres:
    image: postgres:15
    container_name: postgres-db
    environment:
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=myapp
    volumes:
      - postgres-data:/var/lib/postgresql/data
    networks:
      - database-network

  whodb:
    image: clidey/whodb:latest
    container_name: whodb
    ports:
      - "8080:8080"
    depends_on:
      - postgres
    networks:
      - database-network

volumes:
  postgres-data:

networks:
  database-network:
    driver: bridge
```

Connect to PostgreSQL using:
- **Host**: `postgres` (the service name)
- **Port**: `5432`
- **Database**: `myapp`
- **Username**: `postgres`
- **Password**: `password`

### Multiple Database Connections

Connect WhoDB to multiple database types simultaneously:

```
version: '3.8'

services:
  postgres:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: postgres_pass
      POSTGRES_DB: postgres_db
    networks:
      - db-network

  mysql:
    image: mysql:8
    environment:
      MYSQL_ROOT_PASSWORD: mysql_pass
      MYSQL_DATABASE: mysql_db
    networks:
      - db-network

  mongodb:
    image: mongo:6
    environment:
      MONGO_INITDB_ROOT_USERNAME: mongo
      MONGO_INITDB_ROOT_PASSWORD: mongo_pass
    networks:
      - db-network

  redis:
    image: redis:7
    command: redis-server --requirepass redis_pass
    networks:
      - db-network

  whodb:
    image: clidey/whodb:latest
    ports:
      - "8080:8080"
    depends_on:
      - postgres
      - mysql
      - mongodb
      - redis
    networks:
      - db-network

networks:
  db-network:
    driver: bridge
```

All databases are accessible via their service names from WhoDB.

## Volume Management

### Persistent Data Storage

WhoDB stores configuration and session data. Use volumes to persist this data:

```
services:
  whodb:
    image: clidey/whodb:latest
    volumes:
      # Application data
      - whodb-data:/data
      # SQLite databases (optional)
      - ./databases:/databases
      # Configuration files
      - ./config:/config
      # Log files
      - ./logs:/var/log/whodb

volumes:
  whodb-data:
    driver: local
```

### SQLite Database Support

When working with SQLite databases, mount them as volumes:

```
services:
  whodb:
    image: clidey/whodb:latest
    ports:
      - "8080:8080"
    volumes:
      # Mount SQLite database file
      - ./sample.db:/databases/sample.db:ro
      # Mount multiple databases
      - ./databases:/databases:ro
```

Mount SQLite files as read-only (`:ro`) in production to prevent accidental modifications

## Environment Variables

Configure WhoDB behavior through environment variables:

| Variable | Description | Default | Example |
|----------|-------------|---------|---------|
| `WHODB_PORT` | HTTP server port | `8080` | `3000` |
| `WHODB_HOST` | Bind address | `0.0.0.0` | `127.0.0.1` |
| `WHODB_LOG_LEVEL` | Logging verbosity | `info` | `debug`, `warn`, `error` |
| `WHODB_OLLAMA_HOST` | Ollama AI host | `localhost` | `ollama-server` |
| `WHODB_OLLAMA_PORT` | Ollama AI port | `11434` | `11434` |
| `WHODB_ANTHROPIC_API_KEY` | Anthropic API key | - | `sk-ant-...` |
| `WHODB_ANTHROPIC_ENDPOINT` | Anthropic endpoint | `https://api.anthropic.com/v1` | Custom endpoint |
| `WHODB_OPENAI_API_KEY` | OpenAI API key | - | `sk-...` |
| `WHODB_OPENAI_ENDPOINT` | OpenAI endpoint | `https://api.openai.com/v1` | Custom endpoint |

### Environment File Example

Create a `.env` file for sensitive configuration:

```
# .env
WHODB_PORT=8080
WHODB_LOG_LEVEL=info

# AI Integration
ANTHROPIC_API_KEY=sk-ant-api03-xxx
OPENAI_API_KEY=sk-xxx

# Ollama Configuration
OLLAMA_HOST=ollama-server
OLLAMA_PORT=11434
```

Reference in `docker-compose.yml`:

```
services:
  whodb:
    image: clidey/whodb:latest
    env_file:
      - .env
```

Never commit `.env` files with sensitive credentials to version control. Add `.env` to `.gitignore`

## Reverse Proxy Integration

### Nginx Reverse Proxy

Deploy WhoDB behind Nginx for SSL termination and load balancing:

```
version: '3.8'

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./ssl:/etc/nginx/ssl:ro
    depends_on:
      - whodb
    networks:
      - proxy-network

  whodb:
    image: clidey/whodb:latest
    expose:
      - "8080"
    networks:
      - proxy-network

networks:
  proxy-network:
    driver: bridge
```

Nginx configuration (`nginx.conf`):

```
upstream whodb {
    server whodb:8080;
}

server {
    listen 80;
    server_name whodb.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name whodb.example.com;

    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

    location / {
        proxy_pass http://whodb;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
```

### Traefik Integration

Use Traefik for automatic SSL with Let's Encrypt:

```
version: '3.8'

services:
  traefik:
    image: traefik:v2.10
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.le.acme.email=admin@example.com"
      - "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
      - "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./letsencrypt:/letsencrypt
    networks:
      - proxy-network

  whodb:
    image: clidey/whodb:latest
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whodb.rule=Host(`whodb.example.com`)"
      - "traefik.http.routers.whodb.entrypoints=websecure"
      - "traefik.http.routers.whodb.tls.certresolver=le"
      - "traefik.http.services.whodb.loadbalancer.server.port=8080"
    networks:
      - proxy-network

networks:
  proxy-network:
    driver: bridge
```

## Resource Management

### CPU and Memory Limits

Prevent resource exhaustion with explicit limits:

```
services:
  whodb:
    image: clidey/whodb:latest
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 1G
        reservations:
          cpus: '0.25'
          memory: 256M
```

### Docker Swarm Deployment

Deploy WhoDB across a Docker Swarm cluster:

```
version: '3.8'

services:
  whodb:
    image: clidey/whodb:latest
    ports:
      - "8080:8080"
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
        order: start-first
      restart_policy:
        condition: on-failure
        max_attempts: 3
      placement:
        constraints:
          - node.role == worker
    networks:
      - swarm-network

networks:
  swarm-network:
    driver: overlay
```

Deploy with:

```
docker stack deploy -c docker-compose.yml whodb-stack
```

## Security Best Practices

### Run as Non-Root User

Enhance security by running WhoDB as a non-root user:

```
FROM clidey/whodb:latest

USER 1000:1000
```

### Read-Only Root Filesystem

Mount the root filesystem as read-only:

```
services:
  whodb:
    image: clidey/whodb:latest
    read_only: true
    tmpfs:
      - /tmp
      - /var/run
```

### Security Options

Apply security profiles:

```
services:
  whodb:
    image: clidey/whodb:latest
    security_opt:
      - no-new-privileges:true
      - seccomp:unconfined
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
```

## Production Deployment Example

Complete production-ready configuration:

```
version: '3.8'

services:
  whodb:
    image: clidey/whodb:latest
    container_name: whodb-production
    restart: always
    ports:
      - "127.0.0.1:8080:8080"
    environment:
      - WHODB_PORT=8080
      - WHODB_LOG_LEVEL=warn
    env_file:
      - .env.production
    volumes:
      - whodb-data:/data
      - ./logs:/var/log/whodb:rw
    networks:
      - internal-network
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2G
        reservations:
          cpus: '0.5'
          memory: 512M
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8080/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 40s
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "5"
    security_opt:
      - no-new-privileges:true
    read_only: true
    tmpfs:
      - /tmp
      - /var/run

volumes:
  whodb-data:
    driver: local

networks:
  internal-network:
    driver: bridge
    internal: false
```

## Troubleshooting

Check container logs for errors:

```
docker logs whodb
```

Common causes:
- Port already in use
- Invalid environment variables
- Missing required volumes

Verify network connectivity:

```
# Test from WhoDB container
docker exec -it whodb ping postgres

# Check network
docker network inspect whodb-network
```

Ensure database host uses service name, not `localhost`.

Fix volume permissions:

```
# Set correct ownership
sudo chown -R 1000:1000 ./whodb-data

# Or run with host user
docker run --user $(id -u):$(id -g) ...
```

Set memory limits in compose file:

```
deploy:
  resources:
    limits:
      memory: 1G
```

Monitor with:
```
docker stats whodb
```

## Summary

WhoDB's Docker integration provides:

- Simple deployment with single command
- Flexible networking for database connectivity
- Production-ready configurations with health checks
- Resource management and scaling capabilities
- Reverse proxy integration for SSL/TLS
- Volume management for persistent data

Docker offers the most reliable deployment method for WhoDB across development, staging, and production environments.

You're ready to deploy WhoDB in any Docker environment

---


# resources/integrations/ci-cd.mdx
## CI/CD Integration

Integrate WhoDB into your continuous integration and deployment pipelines

# CI/CD Integration

WhoDB can be integrated into your CI/CD pipelines to automate database testing, schema validation, data verification, and deployment workflows. This guide covers integration with popular CI/CD platforms and common automation patterns.

Automate database operations in your pipeline to catch issues early and ensure data consistency

## Use Cases for CI/CD Integration

WhoDB in CI/CD pipelines enables:

- **Database Schema Validation**: Verify schema changes before deployment
- **Data Migration Testing**: Test migrations against realistic datasets
- **Integration Testing**: Validate application database interactions
- **Automated Backups**: Schedule and verify database backups
- **Health Checks**: Monitor database connectivity and performance
- **Compliance Checks**: Verify data integrity and security policies

## GitHub Actions Integration

GitHub Actions provides native CI/CD for GitHub repositories. Here's how to integrate WhoDB:

### Basic Schema Validation Workflow

```
name: Database Schema Validation

on:
  pull_request:
    paths:
      - 'migrations/**'
      - 'schema/**'
  push:
    branches:
      - main

jobs:
  validate-schema:
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: testdb
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432

      whodb:
        image: clidey/whodb:latest
        ports:
          - 8080:8080

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Wait for services
        run: |
          timeout 60 bash -c 'until curl -f http://localhost:8080/health; do sleep 2; done'

      - name: Run migrations
        run: |
          psql -h localhost -U postgres -d testdb < migrations/001_initial_schema.sql
        env:
          PGPASSWORD: postgres

      - name: Verify schema
        run: |
          # Use psql to verify tables exist
          psql -h localhost -U postgres -d testdb -c "\dt"
        env:
          PGPASSWORD: postgres

      - name: Run schema tests
        run: |
          npm run test:schema
```

### Integration Testing with Multiple Databases

```
name: Integration Tests

on:
  pull_request:
  push:
    branches: [main, develop]

jobs:
  test-postgres:
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_PASSWORD: testpass
          POSTGRES_DB: testdb
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

      whodb:
        image: clidey/whodb:latest
        ports:
          - 8080:8080

    steps:
      - uses: actions/checkout@v3

      - name: Setup test data
        run: |
          psql -h localhost -U postgres -d testdb -f test/fixtures/sample_data.sql
        env:
          PGPASSWORD: testpass

      - name: Run application tests
        run: |
          npm install
          npm run test:integration
        env:
          DATABASE_URL: postgresql://postgres:testpass@localhost:5432/testdb

  test-mysql:
    runs-on: ubuntu-latest

    services:
      mysql:
        image: mysql:8
        env:
          MYSQL_ROOT_PASSWORD: testpass
          MYSQL_DATABASE: testdb
        ports:
          - 3306:3306
        options: >-
          --health-cmd="mysqladmin ping"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=5

      whodb:
        image: clidey/whodb:latest
        ports:
          - 8080:8080

    steps:
      - uses: actions/checkout@v3

      - name: Setup test data
        run: |
          mysql -h 127.0.0.1 -u root -ptestpass testdb < test/fixtures/sample_data.sql

      - name: Run application tests
        run: |
          npm install
          npm run test:integration
        env:
          DATABASE_URL: mysql://root:testpass@127.0.0.1:3306/testdb
```

### Database Migration Verification

```
name: Verify Database Migrations

on:
  pull_request:
    paths:
      - 'migrations/**'

jobs:
  verify-migrations:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        postgres-version: [13, 14, 15]

    services:
      postgres:
        image: postgres:${{ matrix.postgres-version }}
        env:
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: migrations_test
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v3

      - name: Install migration tool
        run: |
          npm install -g db-migrate db-migrate-pg

      - name: Run migrations up
        run: |
          db-migrate up --config database.json --env test
        env:
          DATABASE_URL: postgresql://postgres:postgres@localhost:5432/migrations_test

      - name: Verify migration state
        run: |
          psql postgresql://postgres:postgres@localhost:5432/migrations_test -c "\dt"

      - name: Test rollback
        run: |
          db-migrate down --config database.json --env test

      - name: Re-run migrations
        run: |
          db-migrate up --config database.json --env test
```

## GitLab CI Integration

GitLab CI provides integrated pipeline automation. Here's how to use WhoDB:

### Basic Pipeline Configuration

```
# .gitlab-ci.yml
stages:
  - test
  - deploy

variables:
  POSTGRES_DB: testdb
  POSTGRES_USER: postgres
  POSTGRES_PASSWORD: testpass

test:schema:
  stage: test
  image: clidey/whodb:latest
  services:
    - postgres:15
  script:
    - apt-get update && apt-get install -y postgresql-client
    - psql -h postgres -U $POSTGRES_USER -d $POSTGRES_DB -f schema.sql
    - psql -h postgres -U $POSTGRES_USER -d $POSTGRES_DB -c "\dt"
  only:
    changes:
      - schema/**
      - migrations/**

test:integration:
  stage: test
  image: node:18
  services:
    - name: postgres:15
      alias: postgres
    - name: clidey/whodb:latest
      alias: whodb
  variables:
    DATABASE_URL: postgresql://postgres:testpass@postgres:5432/testdb
  script:
    - npm install
    - npm run test:integration
  artifacts:
    reports:
      junit: test-results.xml

deploy:staging:
  stage: deploy
  image: clidey/whodb:latest
  services:
    - postgres:15
  script:
    - psql -h $STAGING_DB_HOST -U $STAGING_DB_USER -d $STAGING_DB_NAME -f migrations/latest.sql
  only:
    - develop
  environment:
    name: staging
```

### Database Backup Pipeline

```
# .gitlab-ci.yml
stages:
  - backup
  - verify

backup:production:
  stage: backup
  image: postgres:15
  script:
    - pg_dump -h $PROD_DB_HOST -U $PROD_DB_USER -d $PROD_DB_NAME > backup.sql
    - gzip backup.sql
    - aws s3 cp backup.sql.gz s3://backups/db-backup-$(date +%Y%m%d-%H%M%S).sql.gz
  artifacts:
    paths:
      - backup.sql.gz
    expire_in: 1 week
  only:
    - schedules

verify:backup:
  stage: verify
  image: postgres:15
  services:
    - name: postgres:15
      alias: verify-db
  dependencies:
    - backup:production
  script:
    - gunzip backup.sql.gz
    - psql -h verify-db -U postgres -d postgres < backup.sql
    - psql -h verify-db -U postgres -d postgres -c "SELECT COUNT(*) FROM users;"
```

## Jenkins Integration

Jenkins provides flexible pipeline automation through Jenkinsfiles:

### Declarative Pipeline

```
pipeline {
    agent any

    environment {
        DATABASE_URL = credentials('database-url')
        WHODB_IMAGE = 'clidey/whodb:latest'
    }

    stages {
        stage('Setup') {
            steps {
                script {
                    docker.image('postgres:15').withRun('-e POSTGRES_PASSWORD=testpass -e POSTGRES_DB=testdb') { db ->
                        docker.image(WHODB_IMAGE).withRun("-p 8080:8080 --link ${db.id}:postgres") { whodb ->
                            sh 'sleep 10'

                            stage('Run Migrations') {
                                sh '''
                                    docker exec ${db.id} psql -U postgres -d testdb -c "CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT);"
                                '''
                            }

                            stage('Integration Tests') {
                                sh '''
                                    npm install
                                    npm run test:integration
                                '''
                            }

                            stage('Schema Validation') {
                                sh '''
                                    docker exec ${db.id} psql -U postgres -d testdb -c "\\dt"
                                '''
                            }
                        }
                    }
                }
            }
        }
    }

    post {
        always {
            junit 'test-results/**/*.xml'
            cleanWs()
        }
    }
}
```

### Scripted Pipeline with Parallel Testing

```
node {
    stage('Checkout') {
        checkout scm
    }

    stage('Parallel Database Tests') {
        parallel(
            postgres: {
                docker.image('postgres:15').withRun('-e POSTGRES_PASSWORD=test') { db ->
                    sh "psql -h ${db.id} -U postgres -c 'SELECT version();'"
                }
            },
            mysql: {
                docker.image('mysql:8').withRun('-e MYSQL_ROOT_PASSWORD=test') { db ->
                    sh "sleep 30"
                    sh "mysql -h ${db.id} -u root -ptest -e 'SELECT version();'"
                }
            }
        )
    }

    stage('Deploy') {
        if (env.BRANCH_NAME == 'main') {
            sh 'kubectl apply -f k8s/whodb-deployment.yaml'
        }
    }
}
```

## CircleCI Integration

CircleCI provides cloud-based CI/CD automation:

```
# .circleci/config.yml
version: 2.1

executors:
  docker-executor:
    docker:
      - image: cimg/node:18.0
      - image: cimg/postgres:15.0
        environment:
          POSTGRES_USER: testuser
          POSTGRES_PASSWORD: testpass
          POSTGRES_DB: testdb
      - image: clidey/whodb:latest

jobs:
  test-database:
    executor: docker-executor
    steps:
      - checkout

      - run:
          name: Wait for PostgreSQL
          command: |
            dockerize -wait tcp://localhost:5432 -timeout 1m

      - run:
          name: Run migrations
          command: |
            psql -h localhost -U testuser -d testdb -f migrations/schema.sql
          environment:
            PGPASSWORD: testpass

      - run:
          name: Run integration tests
          command: |
            npm install
            npm run test:integration
          environment:
            DATABASE_URL: postgresql://testuser:testpass@localhost:5432/testdb

      - store_test_results:
          path: test-results

  deploy-staging:
    docker:
      - image: cimg/base:stable
    steps:
      - checkout
      - run:
          name: Deploy to staging
          command: |
            ssh staging-server "docker-compose pull && docker-compose up -d"

workflows:
  test-and-deploy:
    jobs:
      - test-database
      - deploy-staging:
          requires:
            - test-database
          filters:
            branches:
              only: main
```

## Database Health Check Scripts

Create reusable health check scripts for your pipelines:

### PostgreSQL Health Check

```
#!/bin/bash
# scripts/check-postgres-health.sh

set -e

HOST=${1:-localhost}
PORT=${2:-5432}
USER=${3:-postgres}
DB=${4:-postgres}

echo "Checking PostgreSQL health at $HOST:$PORT"

# Check connection
if ! psql -h "$HOST" -p "$PORT" -U "$USER" -d "$DB" -c "SELECT 1" > /dev/null 2>&1; then
    echo "ERROR: Cannot connect to PostgreSQL"
    exit 1
fi

# Check table count
TABLE_COUNT=$(psql -h "$HOST" -p "$PORT" -U "$USER" -d "$DB" -t -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public'")

echo "Found $TABLE_COUNT tables"

if [ "$TABLE_COUNT" -eq 0 ]; then
    echo "WARNING: No tables found in database"
    exit 1
fi

# Check for long-running queries
LONG_QUERIES=$(psql -h "$HOST" -p "$PORT" -U "$USER" -d "$DB" -t -c "SELECT COUNT(*) FROM pg_stat_activity WHERE state = 'active' AND query_start < NOW() - INTERVAL '5 minutes'")

if [ "$LONG_QUERIES" -gt 0 ]; then
    echo "WARNING: $LONG_QUERIES long-running queries detected"
fi

echo "PostgreSQL health check passed"
```

### Migration Verification Script

```
#!/bin/bash
# scripts/verify-migrations.sh

set -e

DATABASE_URL=$1
MIGRATIONS_DIR=${2:-./migrations}

echo "Verifying migrations in $MIGRATIONS_DIR"

# Count pending migrations
PENDING=$(ls -1 "$MIGRATIONS_DIR"/*.sql 2>/dev/null | wc -l)
echo "Found $PENDING migration files"

# Run migrations
for migration in "$MIGRATIONS_DIR"/*.sql; do
    echo "Applying migration: $(basename $migration)"
    psql "$DATABASE_URL" -f "$migration" || {
        echo "ERROR: Migration failed: $migration"
        exit 1
    }
done

# Verify schema
echo "Verifying schema..."
psql "$DATABASE_URL" -c "\dt" || exit 1

echo "All migrations applied successfully"
```

## Automated Testing Patterns

### Schema Comparison Test

```
// tests/schema-comparison.test.js
const { Client } = require('pg');

describe('Schema Validation', () => {
  let client;

  beforeAll(async () => {
    client = new Client({
      connectionString: process.env.DATABASE_URL
    });
    await client.connect();
  });

  afterAll(async () => {
    await client.end();
  });

  test('users table should have correct columns', async () => {
    const result = await client.query(`
      SELECT column_name, data_type
      FROM information_schema.columns
      WHERE table_name = 'users'
      ORDER BY column_name
    `);

    const columns = result.rows.map(r => ({
      name: r.column_name,
      type: r.data_type
    }));

    expect(columns).toMatchSnapshot();
  });

  test('all foreign keys should be valid', async () => {
    const result = await client.query(`
      SELECT
        tc.table_name,
        kcu.column_name,
        ccu.table_name AS foreign_table_name
      FROM information_schema.table_constraints tc
      JOIN information_schema.key_column_usage kcu
        ON tc.constraint_name = kcu.constraint_name
      JOIN information_schema.constraint_column_usage ccu
        ON ccu.constraint_name = tc.constraint_name
      WHERE tc.constraint_type = 'FOREIGN KEY'
    `);

    expect(result.rows.length).toBeGreaterThan(0);
  });
});
```

### Data Integrity Test

```
// tests/data-integrity.test.js
describe('Data Integrity', () => {
  test('no orphaned records exist', async () => {
    const result = await client.query(`
      SELECT COUNT(*) as orphaned
      FROM orders o
      LEFT JOIN users u ON o.user_id = u.id
      WHERE u.id IS NULL
    `);

    expect(parseInt(result.rows[0].orphaned)).toBe(0);
  });

  test('all emails are valid format', async () => {
    const result = await client.query(`
      SELECT COUNT(*) as invalid
      FROM users
      WHERE email !~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$'
    `);

    expect(parseInt(result.rows[0].invalid)).toBe(0);
  });
});
```

## Best Practices

Run databases as service containers rather than installing locally for consistent environments across all test runs.

Cache database images and npm packages to speed up pipeline execution:

```
- uses: actions/cache@v3
  with:
    path: ~/.docker
    key: docker-${{ hashFiles('Dockerfile') }}
```

Run database tests in parallel when testing multiple database types or versions to reduce total pipeline time.

Use separate database instances for each pipeline run to prevent test interference and ensure reproducibility.

Always clean up database containers and volumes after tests complete to prevent resource exhaustion:

```
post:
  always:
    - docker-compose down -v
```

## Deployment Workflows

### Blue-Green Deployment

```
deploy:
  stage: deploy
  script:
    # Deploy to green environment
    - docker-compose -f docker-compose.green.yml up -d
    - ./scripts/health-check.sh green

    # Run smoke tests
    - npm run test:smoke -- --env=green

    # Switch traffic to green
    - ./scripts/switch-traffic.sh green

    # Verify production
    - sleep 30
    - ./scripts/health-check.sh production

    # Tear down blue environment
    - docker-compose -f docker-compose.blue.yml down
```

## Summary

CI/CD integration with WhoDB enables:

- Automated database schema validation
- Integration testing with multiple database types
- Migration verification and rollback testing
- Continuous deployment with health checks
- Automated backup and verification
- Parallel testing for faster feedback

Integrating WhoDB into your CI/CD pipeline ensures database changes are tested thoroughly before reaching production.

You're ready to automate database operations in your CI/CD pipeline

---


# resources/integrations/monitoring.mdx
## Monitoring Integration

Monitor WhoDB with Prometheus, Grafana, and logging solutions

# Monitoring Integration

Effective monitoring ensures WhoDB and your databases remain healthy, performant, and secure. This guide covers integration with popular monitoring tools, logging solutions, alerting systems, and best practices for production observability.

Comprehensive monitoring helps you detect issues before they impact users and provides insights for optimization

## Monitoring Architecture

A complete monitoring setup includes:

- **Metrics Collection**: Gather performance and health metrics
- **Logging**: Centralized log aggregation and analysis
- **Alerting**: Notifications for critical events
- **Dashboards**: Visual representation of system health
- **Tracing**: Request flow analysis across services

## Prometheus Integration

Prometheus is the industry-standard metrics collection and monitoring system. WhoDB can be monitored using node exporters and custom metrics.

### Prometheus Configuration

Create a `prometheus.yml` configuration:

```
# prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s
  external_labels:
    cluster: 'production'
    environment: 'prod'

scrape_configs:
  - job_name: 'whodb'
    static_configs:
      - targets: ['whodb:8080']
        labels:
          service: 'whodb'
          instance: 'whodb-01'

  - job_name: 'postgres'
    static_configs:
      - targets: ['postgres-exporter:9187']
        labels:
          service: 'postgres'
          database: 'production'

  - job_name: 'mysql'
    static_configs:
      - targets: ['mysql-exporter:9104']
        labels:
          service: 'mysql'
          database: 'production'

  - job_name: 'redis'
    static_configs:
      - targets: ['redis-exporter:9121']
        labels:
          service: 'redis'

  - job_name: 'mongodb'
    static_configs:
      - targets: ['mongodb-exporter:9216']
        labels:
          service: 'mongodb'

alerting:
  alertmanagers:
    - static_configs:
        - targets: ['alertmanager:9093']

rule_files:
  - '/etc/prometheus/rules/*.yml'
```

### Docker Compose with Prometheus

```
version: '3.8'

services:
  whodb:
    image: clidey/whodb:latest
    ports:
      - "8080:8080"
    networks:
      - monitoring

  prometheus:
    image: prom/prometheus:latest
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--storage.tsdb.retention.time=30d'
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - ./prometheus-rules:/etc/prometheus/rules:ro
      - prometheus-data:/prometheus
    ports:
      - "9090:9090"
    networks:
      - monitoring

  postgres-exporter:
    image: prometheuscommunity/postgres-exporter:latest
    environment:
      DATA_SOURCE_NAME: "postgresql://postgres:password@postgres:5432/postgres?sslmode=disable"
    ports:
      - "9187:9187"
    networks:
      - monitoring

  mysql-exporter:
    image: prom/mysqld-exporter:latest
    environment:
      DATA_SOURCE_NAME: "root:password@(mysql:3306)/"
    ports:
      - "9104:9104"
    networks:
      - monitoring

  redis-exporter:
    image: oliver006/redis_exporter:latest
    environment:
      REDIS_ADDR: "redis:6379"
      REDIS_PASSWORD: "password"
    ports:
      - "9121:9121"
    networks:
      - monitoring

volumes:
  prometheus-data:

networks:
  monitoring:
    driver: bridge
```

### Prometheus Alert Rules

Create alert rules in `prometheus-rules/alerts.yml`:

```
groups:
  - name: whodb_alerts
    interval: 30s
    rules:
      - alert: WhoDBDown
        expr: up{job="whodb"} == 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "WhoDB instance is down"
          description: "WhoDB instance {{ $labels.instance }} has been down for more than 2 minutes."

      - alert: HighMemoryUsage
        expr: (container_memory_usage_bytes{name="whodb"} / container_spec_memory_limit_bytes{name="whodb"}) > 0.85
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High memory usage detected"
          description: "WhoDB is using {{ $value | humanizePercentage }} of available memory."

      - alert: DatabaseConnectionFailure
        expr: increase(database_connection_errors_total[5m]) > 10
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Database connection failures detected"
          description: "{{ $value }} connection failures in the last 5 minutes."

  - name: database_alerts
    interval: 30s
    rules:
      - alert: PostgreSQLDown
        expr: pg_up == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "PostgreSQL instance is down"
          description: "PostgreSQL on {{ $labels.instance }} is not responding."

      - alert: SlowQueries
        expr: rate(pg_stat_statements_mean_exec_time[5m]) > 1000
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "Slow queries detected"
          description: "Average query execution time is {{ $value }}ms."

      - alert: HighDatabaseConnections
        expr: (pg_stat_database_numbackends / pg_settings_max_connections) > 0.8
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High database connection usage"
          description: "Database is using {{ $value | humanizePercentage }} of available connections."
```

## Grafana Dashboards

Grafana provides visual dashboards for Prometheus metrics.

### Grafana Setup

```
# docker-compose.yml
services:
  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
      - GF_USERS_ALLOW_SIGN_UP=false
      - GF_SERVER_ROOT_URL=http://localhost:3000
    volumes:
      - grafana-data:/var/lib/grafana
      - ./grafana/dashboards:/etc/grafana/provisioning/dashboards
      - ./grafana/datasources:/etc/grafana/provisioning/datasources
    networks:
      - monitoring
    depends_on:
      - prometheus

volumes:
  grafana-data:
```

### Grafana Datasource Configuration

Create `grafana/datasources/prometheus.yml`:

```
apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
    editable: false
    jsonData:
      timeInterval: "15s"
```

### WhoDB Dashboard JSON

Create a dashboard in `grafana/dashboards/whodb.json`:

```
{
  "dashboard": {
    "title": "WhoDB Monitoring",
    "panels": [
      {
        "title": "WhoDB Uptime",
        "targets": [
          {
            "expr": "up{job=\"whodb\"}",
            "legendFormat": "{{ instance }}"
          }
        ],
        "type": "stat"
      },
      {
        "title": "Request Rate",
        "targets": [
          {
            "expr": "rate(http_requests_total{job=\"whodb\"}[5m])",
            "legendFormat": "{{ instance }}"
          }
        ],
        "type": "graph"
      },
      {
        "title": "Response Time",
        "targets": [
          {
            "expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))",
            "legendFormat": "p95"
          }
        ],
        "type": "graph"
      },
      {
        "title": "Database Connection Pool",
        "targets": [
          {
            "expr": "database_connection_pool_active",
            "legendFormat": "Active"
          },
          {
            "expr": "database_connection_pool_idle",
            "legendFormat": "Idle"
          }
        ],
        "type": "graph"
      }
    ]
  }
}
```

## Logging Integration

Centralized logging helps track errors, debug issues, and audit activity.

### ELK Stack Integration

Deploy the ELK (Elasticsearch, Logstash, Kibana) stack for log aggregation:

```
version: '3.8'

services:
  whodb:
    image: clidey/whodb:latest
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
        labels: "service=whodb"
    networks:
      - logging

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.10.0
    environment:
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - xpack.security.enabled=false
    volumes:
      - elasticsearch-data:/usr/share/elasticsearch/data
    ports:
      - "9200:9200"
    networks:
      - logging

  logstash:
    image: docker.elastic.co/logstash/logstash:8.10.0
    volumes:
      - ./logstash/pipeline:/usr/share/logstash/pipeline
    ports:
      - "5000:5000"
    environment:
      - "LS_JAVA_OPTS=-Xmx256m -Xms256m"
    depends_on:
      - elasticsearch
    networks:
      - logging

  kibana:
    image: docker.elastic.co/kibana/kibana:8.10.0
    ports:
      - "5601:5601"
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    depends_on:
      - elasticsearch
    networks:
      - logging

volumes:
  elasticsearch-data:

networks:
  logging:
    driver: bridge
```

### Logstash Pipeline Configuration

Create `logstash/pipeline/logstash.conf`:

```
input {
  file {
    path => "/var/log/whodb/*.log"
    start_position => "beginning"
    codec => json
  }
}

filter {
  if [service] == "whodb" {
    grok {
      match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{GREEDYDATA:message}" }
    }

    date {
      match => [ "timestamp", "ISO8601" ]
      target => "@timestamp"
    }

    mutate {
      remove_field => ["timestamp"]
    }
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "whodb-logs-%{+YYYY.MM.dd}"
  }

  stdout {
    codec => rubydebug
  }
}
```

### Structured Logging

Configure WhoDB for structured JSON logging:

```
services:
  whodb:
    image: clidey/whodb:latest
    environment:
      - WHODB_LOG_FORMAT=json
      - WHODB_LOG_LEVEL=info
    volumes:
      - ./logs:/var/log/whodb
```

## Loki and Promtail Integration

Loki provides lightweight log aggregation optimized for Kubernetes and cloud-native environments:

```
version: '3.8'

services:
  whodb:
    image: clidey/whodb:latest
    logging:
      driver: "json-file"
      options:
        tag: "whodb"
    networks:
      - monitoring

  loki:
    image: grafana/loki:latest
    ports:
      - "3100:3100"
    command: -config.file=/etc/loki/local-config.yaml
    networks:
      - monitoring

  promtail:
    image: grafana/promtail:latest
    volumes:
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
      - /var/run/docker.sock:/var/run/docker.sock
      - ./promtail-config.yml:/etc/promtail/config.yml
    command: -config.file=/etc/promtail/config.yml
    networks:
      - monitoring
    depends_on:
      - loki

networks:
  monitoring:
    driver: bridge
```

Promtail configuration (`promtail-config.yml`):

```
server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
  - job_name: docker
    docker_sd_configs:
      - host: unix:///var/run/docker.sock
        refresh_interval: 5s
    relabel_configs:
      - source_labels: ['__meta_docker_container_name']
        regex: '/(.*)'
        target_label: 'container'
      - source_labels: ['__meta_docker_container_log_stream']
        target_label: 'stream'
```

## Health Checks

Implement comprehensive health checks for monitoring systems:

### HTTP Health Check Endpoint

Create a health check script:

```
#!/bin/bash
# scripts/health-check.sh

WHODB_URL=${1:-http://localhost:8080}
TIMEOUT=${2:-5}

# Check WhoDB availability
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time "$TIMEOUT" "$WHODB_URL/health")

if [ "$HTTP_CODE" -eq 200 ]; then
    echo "WhoDB is healthy"
    exit 0
else
    echo "WhoDB health check failed with HTTP code: $HTTP_CODE"
    exit 1
fi
```

### Kubernetes Liveness and Readiness Probes

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: whodb
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: whodb
        image: clidey/whodb:latest
        ports:
        - containerPort: 8080
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
          timeoutSeconds: 3
          failureThreshold: 2
```

## Alerting with AlertManager

Configure AlertManager for intelligent alert routing:

```
# alertmanager.yml
global:
  resolve_timeout: 5m
  slack_api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'

route:
  group_by: ['alertname', 'cluster', 'service']
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 12h
  receiver: 'default'
  routes:
    - match:
        severity: critical
      receiver: 'pagerduty-critical'
      continue: true

    - match:
        severity: warning
      receiver: 'slack-warnings'

    - match:
        alertname: WhoDBDown
      receiver: 'slack-critical'

receivers:
  - name: 'default'
    slack_configs:
      - channel: '#monitoring'
        title: 'Alert: {{ .GroupLabels.alertname }}'
        text: '{{ range .Alerts }}{{ .Annotations.description }}{{ end }}'

  - name: 'slack-warnings'
    slack_configs:
      - channel: '#alerts-warnings'
        title: 'Warning: {{ .GroupLabels.alertname }}'
        text: '{{ .Annotations.summary }}'

  - name: 'slack-critical'
    slack_configs:
      - channel: '#alerts-critical'
        title: 'CRITICAL: {{ .GroupLabels.alertname }}'
        text: '@here {{ .Annotations.description }}'

  - name: 'pagerduty-critical'
    pagerduty_configs:
      - service_key: 'YOUR_PAGERDUTY_SERVICE_KEY'
        description: '{{ .GroupLabels.alertname }}: {{ .Annotations.summary }}'
```

Deploy AlertManager:

```
services:
  alertmanager:
    image: prom/alertmanager:latest
    ports:
      - "9093:9093"
    volumes:
      - ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
      - alertmanager-data:/alertmanager
    command:
      - '--config.file=/etc/alertmanager/alertmanager.yml'
      - '--storage.path=/alertmanager'
    networks:
      - monitoring

volumes:
  alertmanager-data:
```

## Application Performance Monitoring

### Jaeger for Distributed Tracing

```
services:
  jaeger:
    image: jaegertracing/all-in-one:latest
    ports:
      - "5775:5775/udp"
      - "6831:6831/udp"
      - "6832:6832/udp"
      - "5778:5778"
      - "16686:16686"
      - "14268:14268"
      - "14250:14250"
      - "9411:9411"
    environment:
      - COLLECTOR_ZIPKIN_HOST_PORT=:9411
    networks:
      - monitoring
```

## Monitoring Best Practices

Establish Service Level Objectives (SLOs) for:
- Availability: 99.9% uptime
- Latency: p95 < 200ms
- Error rate: < 0.1%

Focus alerts on user-impacting issues rather than internal metrics. Alert when users are affected, not when a single container restarts.

Configure proper thresholds and grouping to prevent notification spam. Use escalation policies for critical alerts.

Document response procedures for each alert type to enable quick resolution.

Schedule weekly dashboard reviews to identify trends and optimize performance proactively.

Define retention based on compliance requirements and storage capacity:
- Production logs: 30-90 days
- Development logs: 7-14 days
- Audit logs: 1-7 years (compliance dependent)

## Complete Monitoring Stack

Production-ready monitoring configuration:

```
version: '3.8'

services:
  whodb:
    image: clidey/whodb:latest
    ports:
      - "8080:8080"
    networks:
      - app-network
      - monitoring

  prometheus:
    image: prom/prometheus:latest
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus-data:/prometheus
    ports:
      - "9090:9090"
    networks:
      - monitoring

  grafana:
    image: grafana/grafana:latest
    volumes:
      - grafana-data:/var/lib/grafana
      - ./grafana:/etc/grafana/provisioning
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    networks:
      - monitoring

  loki:
    image: grafana/loki:latest
    ports:
      - "3100:3100"
    networks:
      - monitoring

  promtail:
    image: grafana/promtail:latest
    volumes:
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
      - ./promtail-config.yml:/etc/promtail/config.yml
    networks:
      - monitoring

  alertmanager:
    image: prom/alertmanager:latest
    volumes:
      - ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
    ports:
      - "9093:9093"
    networks:
      - monitoring

volumes:
  prometheus-data:
  grafana-data:

networks:
  app-network:
  monitoring:
```

## Summary

Comprehensive monitoring for WhoDB includes:

- Metrics collection with Prometheus
- Visual dashboards with Grafana
- Centralized logging with ELK or Loki
- Intelligent alerting with AlertManager
- Distributed tracing with Jaeger
- Health checks and SLO monitoring

Proper monitoring ensures reliability, enables proactive issue resolution, and provides insights for continuous improvement.

You're ready to implement production-grade monitoring for WhoDB

---


# resources/integrations/backup-tools.mdx
## Backup Tool Integration

Integrate WhoDB with backup solutions for automated database backups and disaster recovery

# Backup Tool Integration

Automated database backups are critical for disaster recovery, compliance, and data protection. This guide covers integration with popular backup tools, scheduling strategies, restore procedures, and best practices for maintaining reliable backups across all database types.

Automated backups ensure your data is protected against loss and enable quick recovery from unexpected incidents

## Backup Strategy Overview

A comprehensive backup strategy includes:

- **Backup Types**: Full, incremental, and differential backups
- **Scheduling**: Regular automated backup jobs on defined intervals
- **Retention Policies**: How long backups are retained
- **Testing**: Regular restore tests to verify backup integrity
- **Monitoring**: Alerts for backup failures
- **Documentation**: Runbooks for disaster recovery procedures

## Full vs. Incremental Backups

### Full Backups

Complete snapshot of the entire database. Larger file size but simplest recovery.

**When to use**:
- Initial baseline backups
- Critical production databases
- Before major schema changes
- For long-term archival

**Frequency**: Weekly or monthly

```
# PostgreSQL full backup
pg_dump -U postgres -d production_db > backup-full-$(date +%Y%m%d).sql

# MySQL full backup
mysqldump -u root -p --all-databases > backup-full-$(date +%Y%m%d).sql

# SQLite full backup
cp production.db backup-full-$(date +%Y%m%d).db
```

### Incremental Backups

Only backup changes since last backup. Smaller file size but requires full backup for recovery.

**When to use**:
- High-frequency backup schedules
- Large databases with frequent updates
- Cost optimization for storage

**Frequency**: Daily or multiple times per day

**Note**: Implementation depends on database type. PostgreSQL WAL archiving provides incremental backup capability.

## Database-Specific Backup Tools

### PostgreSQL Backups

PostgreSQL offers multiple backup methods suitable for different scenarios.

#### Using pg_dump

Simple SQL-based backup approach:

```
#!/bin/bash
# scripts/backup-postgres.sh

BACKUP_DIR="/backups/postgres"
DB_HOST="localhost"
DB_USER="postgres"
DB_NAME="production"
RETENTION_DAYS=30

# Create backup directory
mkdir -p "$BACKUP_DIR"

# Perform backup
BACKUP_FILE="$BACKUP_DIR/backup-$(date +%Y%m%d-%H%M%S).sql"
pg_dump -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" > "$BACKUP_FILE"

# Compress backup
gzip "$BACKUP_FILE"

# Remove old backups
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete

echo "Backup completed: ${BACKUP_FILE}.gz"
```

Run via cron:

```
# Daily backup at 2 AM
0 2 * * * /scripts/backup-postgres.sh

# Hourly incremental backups
0 * * * * /scripts/backup-postgres-incremental.sh
```

#### WAL Archiving

Enable continuous archiving for point-in-time recovery:

```
-- In postgresql.conf
wal_level = replica
max_wal_senders = 3
max_wal_size = 1GB
wal_keep_size = 1GB

# Archive WAL files
archive_mode = on
archive_command = 'test ! -f /backups/wal/%f && cp %p /backups/wal/%f'
archive_timeout = 300
```

#### pg_basebackup for Physical Backups

Create physical backups suitable for streaming replication:

```
#!/bin/bash
# scripts/backup-postgres-physical.sh

BACKUP_DIR="/backups/postgres-physical"
DB_HOST="localhost"
DB_PORT=5432
DB_USER="postgres"

mkdir -p "$BACKUP_DIR"

# Create base backup
pg_basebackup \
  -h "$DB_HOST" \
  -p "$DB_PORT" \
  -U "$DB_USER" \
  -D "$BACKUP_DIR/base-$(date +%Y%m%d-%H%M%S)" \
  -Ft \
  -z \
  -P

echo "Physical backup completed"
```

### MySQL/MariaDB Backups

Multiple backup strategies for MySQL databases.

#### Using mysqldump

Logical backup approach:

```
#!/bin/bash
# scripts/backup-mysql.sh

BACKUP_DIR="/backups/mysql"
DB_HOST="localhost"
DB_USER="root"
DB_PASSWORD="password"
RETENTION_DAYS=30

mkdir -p "$BACKUP_DIR"

# Full backup
BACKUP_FILE="$BACKUP_DIR/backup-$(date +%Y%m%d-%H%M%S).sql"
mysqldump \
  -h "$DB_HOST" \
  -u "$DB_USER" \
  -p"$DB_PASSWORD" \
  --all-databases \
  --single-transaction \
  --lock-tables=false > "$BACKUP_FILE"

# Compress
gzip "$BACKUP_FILE"

# Cleanup old backups
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete

echo "MySQL backup completed: ${BACKUP_FILE}.gz"
```

#### Percona XtraBackup

Advanced incremental backup solution:

```
#!/bin/bash
# scripts/backup-mysql-xtrabackup.sh

BACKUP_DIR="/backups/mysql-xtrabackup"
TARGET_DIR="$BACKUP_DIR/$(date +%Y%m%d-%H%M%S)"
FULL_BACKUP_DIR="$BACKUP_DIR/full-$(date +%A)"

mkdir -p "$TARGET_DIR"

# Weekly full backup
if [ "$(date +%A)" == "Sunday" ]; then
    xtrabackup --backup \
      --target-dir="$FULL_BACKUP_DIR" \
      --user=root \
      --password=password
    echo "Full backup: $FULL_BACKUP_DIR"
else
    # Daily incremental backup
    xtrabackup --backup \
      --target-dir="$TARGET_DIR" \
      --incremental-basedir="$FULL_BACKUP_DIR" \
      --user=root \
      --password=password
    echo "Incremental backup: $TARGET_DIR"
fi
```

#### Binary Log Backup

Enable point-in-time recovery with binary logs:

```
#!/bin/bash
# scripts/backup-mysql-binlog.sh

BINLOG_DIR="/backups/mysql-binlogs"
LOG_INDEX_FILE="/var/lib/mysql/mysql-bin.index"

mkdir -p "$BINLOG_DIR"

# Copy current binary logs
cp /var/lib/mysql/mysql-bin.* "$BINLOG_DIR/"

# Purge old binary logs (keep 7 days)
mysql -u root -p"$PASSWORD" -e "PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 7 DAY);"

echo "Binary logs backed up to $BINLOG_DIR"
```

### SQLite Backups

Simple file-based backup for SQLite databases:

```
#!/bin/bash
# scripts/backup-sqlite.sh

BACKUP_DIR="/backups/sqlite"
SOURCE_DB="$1"
RETENTION_DAYS=30

mkdir -p "$BACKUP_DIR"

# Copy database file
BACKUP_FILE="$BACKUP_DIR/$(basename $SOURCE_DB)-$(date +%Y%m%d-%H%M%S).db"
cp "$SOURCE_DB" "$BACKUP_FILE"

# Compress
gzip "$BACKUP_FILE"

# Cleanup old backups
find "$BACKUP_DIR" -name "*.db.gz" -mtime +$RETENTION_DAYS -delete

echo "SQLite backup: ${BACKUP_FILE}.gz"
```

### MongoDB Backups

MongoDB-specific backup strategies:

#### Using mongodump

Logical backup approach:

```
#!/bin/bash
# scripts/backup-mongodb.sh

BACKUP_DIR="/backups/mongodb"
MONGO_HOST="localhost"
MONGO_PORT=27017
RETENTION_DAYS=30

mkdir -p "$BACKUP_DIR"

# Perform backup
BACKUP_FILE="$BACKUP_DIR/backup-$(date +%Y%m%d-%H%M%S)"
mongodump \
  --host "$MONGO_HOST:$MONGO_PORT" \
  --out "$BACKUP_FILE"

# Compress
tar -czf "${BACKUP_FILE}.tar.gz" -C "$BACKUP_DIR" "$(basename $BACKUP_FILE)"
rm -rf "$BACKUP_FILE"

# Cleanup
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete

echo "MongoDB backup: ${BACKUP_FILE}.tar.gz"
```

#### Replica Set Backups

For production MongoDB replica sets:

```
#!/bin/bash
# scripts/backup-mongodb-replica.sh

# Connect to secondary member
mongodump \
  --host secondary-member:27017 \
  --oplog \
  --out /backups/mongodb-replica

# Archive with timestamp
tar -czf "/backups/mongodb-replica-$(date +%Y%m%d-%H%M%S).tar.gz" \
  -C /backups mongodb-replica

echo "Replica set backup completed"
```

## Automated Backup Scheduling

### Docker-Based Scheduling

Create a backup container with cron:

```
# Dockerfile.backup
FROM postgres:15

RUN apt-get update && apt-get install -y \
    curl \
    awscli \
    && rm -rf /var/lib/apt/lists/*

COPY scripts/backup-postgres.sh /scripts/backup.sh
RUN chmod +x /scripts/backup.sh

# Run backup container with cron
CMD ["crond", "-f", "-l", "2"]
```

Docker Compose integration:

```
version: '3.8'

services:
  backup-postgres:
    build:
      context: .
      dockerfile: Dockerfile.backup
    environment:
      PGHOST: postgres
      PGUSER: postgres
      PGPASSWORD: postgres_password
    volumes:
      - ./scripts/backup-postgres.sh:/scripts/backup.sh:ro
      - backup-storage:/backups
      - /var/spool/cron/crontabs:/var/spool/cron/crontabs
    depends_on:
      - postgres
    networks:
      - db-network

  postgres:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: postgres_password
    networks:
      - db-network

volumes:
  backup-storage:

networks:
  db-network:
```

### Kubernetes CronJob

Schedule backups in Kubernetes:

```
apiVersion: batch/v1
kind: CronJob
metadata:
  name: postgres-backup
spec:
  schedule: "0 2 * * *"  # 2 AM daily
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 3
  jobTemplate:
    spec:
      template:
        spec:
          serviceAccountName: postgres-backup
          containers:
          - name: postgres-backup
            image: postgres:15
            command:
            - /bin/bash
            - -c
            - |
              pg_dump -h postgres-service -U postgres -d production > /backups/backup-$(date +%Y%m%d-%H%M%S).sql
              gzip /backups/backup-*.sql
              aws s3 cp /backups/ s3://backup-bucket/postgres/ --recursive
            env:
            - name: PGPASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: password
            - name: AWS_ACCESS_KEY_ID
              valueFrom:
                secretKeyRef:
                  name: aws-credentials
                  key: access-key
            - name: AWS_SECRET_ACCESS_KEY
              valueFrom:
                secretKeyRef:
                  name: aws-credentials
                  key: secret-key
            volumeMounts:
            - name: backup-storage
              mountPath: /backups
          volumes:
          - name: backup-storage
            emptyDir: {}
          restartPolicy: OnFailure
```

## Cloud Backup Storage

### AWS S3 Integration

Store backups in AWS S3 for durability:

```
#!/bin/bash
# scripts/backup-to-s3.sh

BACKUP_DIR="/backups/temp"
S3_BUCKET="s3://company-backups"
DB_HOST="localhost"
DB_USER="postgres"
DB_NAME="production"
RETENTION_DAYS=30

# Create backup
mkdir -p "$BACKUP_DIR"
BACKUP_FILE="$BACKUP_DIR/backup-$(date +%Y%m%d-%H%M%S).sql"
pg_dump -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" > "$BACKUP_FILE"

# Compress
gzip "$BACKUP_FILE"

# Upload to S3
aws s3 cp "${BACKUP_FILE}.gz" "$S3_BUCKET/postgres/" \
    --storage-class GLACIER_IR \
    --metadata "date=$(date +%Y-%m-%d),database=$DB_NAME"

# List recent backups
echo "Recent backups in S3:"
aws s3 ls "$S3_BUCKET/postgres/" --recursive | tail -10

# Cleanup local backup
rm -f "${BACKUP_FILE}.gz"

echo "Backup uploaded to S3: ${BACKUP_FILE}.gz"
```

### Google Cloud Storage Integration

```
#!/bin/bash
# scripts/backup-to-gcs.sh

BACKUP_DIR="/backups/temp"
GCS_BUCKET="gs://company-database-backups"
DB_HOST="localhost"
DB_USER="postgres"
DB_NAME="production"

mkdir -p "$BACKUP_DIR"

# Create backup
BACKUP_FILE="$BACKUP_DIR/backup-$(date +%Y%m%d-%H%M%S).sql"
pg_dump -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" > "$BACKUP_FILE"
gzip "$BACKUP_FILE"

# Upload to GCS
gsutil cp "${BACKUP_FILE}.gz" "$GCS_BUCKET/postgres/$(date +%Y/%m/%d)/"

# Set lifecycle policy for cost optimization
gsutil lifecycle set - "$GCS_BUCKET" << EOF
{
  "lifecycle": {
    "rule": [
      {
        "action": {"type": "Delete"},
        "condition": {"age": 90}
      },
      {
        "action": {"type": "SetStorageClass", "storageClass": "COLDLINE"},
        "condition": {"age": 30}
      }
    ]
  }
}
EOF

rm -f "${BACKUP_FILE}.gz"
echo "Backup uploaded to GCS"
```

## Backup Verification and Testing

### Automated Restore Testing

Regular restore tests ensure backup integrity:

```
#!/bin/bash
# scripts/verify-backup.sh

BACKUP_FILE="$1"
TEST_DB="backup_test_$(date +%s)"

echo "Testing restore from: $BACKUP_FILE"

# Create test database
psql -U postgres -c "CREATE DATABASE $TEST_DB"

# Attempt restore
if gunzip -c "$BACKUP_FILE" | psql -U postgres -d "$TEST_DB" > /dev/null 2>&1; then
    # Verify tables exist
    TABLE_COUNT=$(psql -U postgres -d "$TEST_DB" -t -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public'")

    if [ "$TABLE_COUNT" -gt 0 ]; then
        echo "SUCCESS: Restore verified. Found $TABLE_COUNT tables"

        # Run integrity checks
        psql -U postgres -d "$TEST_DB" -c "SELECT COUNT(*) FROM pg_stat_user_tables;" > /dev/null

        # Cleanup
        psql -U postgres -c "DROP DATABASE $TEST_DB"
        exit 0
    fi
fi

echo "FAILED: Backup restore failed"
psql -U postgres -c "DROP DATABASE $TEST_DB" 2>/dev/null
exit 1
```

### Backup Checksums

Verify backup file integrity:

```
#!/bin/bash
# scripts/backup-checksum.sh

BACKUP_FILE="$1"
CHECKSUM_FILE="${BACKUP_FILE}.sha256"

# Generate checksum
sha256sum "$BACKUP_FILE" > "$CHECKSUM_FILE"

# Verify on restore
sha256sum -c "$CHECKSUM_FILE"

if [ $? -eq 0 ]; then
    echo "Checksum verification PASSED"
else
    echo "Checksum verification FAILED - backup may be corrupted"
    exit 1
fi
```

## Backup Retention Policies

### Tiered Retention Strategy

```
#!/bin/bash
# scripts/manage-backup-retention.sh

BACKUP_DIR="/backups"
ARCHIVE_DIR="/archive"

# Daily backups: keep 7 days
find "$BACKUP_DIR/daily" -name "*.sql.gz" -mtime +7 -delete

# Weekly backups: keep 8 weeks (56 days)
find "$BACKUP_DIR/weekly" -name "*.sql.gz" -mtime +56 -delete

# Monthly backups: keep 12 months (365 days)
find "$BACKUP_DIR/monthly" -name "*.sql.gz" -mtime +365 -delete

# Archive older backups to cold storage
find "$BACKUP_DIR/monthly" -name "*.sql.gz" -mtime +30 | while read file; do
    aws s3 cp "$file" "s3://backup-archive/$(date +%Y/%m)/"
done

echo "Backup retention policy applied"
```

## Restore Procedures

### PostgreSQL Restore

```
#!/bin/bash
# scripts/restore-postgres.sh

BACKUP_FILE="$1"
TARGET_DB="$2"
TARGET_HOST="${3:-localhost}"
TARGET_USER="${4:-postgres}"

if [ -z "$BACKUP_FILE" ] || [ -z "$TARGET_DB" ]; then
    echo "Usage: $0   [host] [user]"
    exit 1
fi

echo "Restoring PostgreSQL backup: $BACKUP_FILE"
echo "Target database: $TARGET_DB"

# Create target database
psql -h "$TARGET_HOST" -U "$TARGET_USER" -c "CREATE DATABASE $TARGET_DB" 2>/dev/null || true

# Restore from backup
if [[ "$BACKUP_FILE" == *.gz ]]; then
    gunzip -c "$BACKUP_FILE" | psql -h "$TARGET_HOST" -U "$TARGET_USER" -d "$TARGET_DB"
else
    psql -h "$TARGET_HOST" -U "$TARGET_USER" -d "$TARGET_DB" < "$BACKUP_FILE"
fi

if [ $? -eq 0 ]; then
    echo "Restore completed successfully"

    # Verify
    TABLES=$(psql -h "$TARGET_HOST" -U "$TARGET_USER" -d "$TARGET_DB" -t -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public'")
    echo "Restored database has $TABLES tables"
else
    echo "Restore failed"
    exit 1
fi
```

### MySQL Restore

```
#!/bin/bash
# scripts/restore-mysql.sh

BACKUP_FILE="$1"
TARGET_USER="$2"
TARGET_HOST="${3:-localhost}"

if [ -z "$BACKUP_FILE" ] || [ -z "$TARGET_USER" ]; then
    echo "Usage: $0   [host]"
    exit 1
fi

echo "Restoring MySQL backup: $BACKUP_FILE"

if [[ "$BACKUP_FILE" == *.gz ]]; then
    gunzip -c "$BACKUP_FILE" | mysql -h "$TARGET_HOST" -u "$TARGET_USER" -p
else
    mysql -h "$TARGET_HOST" -u "$TARGET_USER" -p < "$BACKUP_FILE"
fi

if [ $? -eq 0 ]; then
    echo "MySQL restore completed"
else
    echo "Restore failed"
    exit 1
fi
```

## Backup Monitoring and Alerts

### Backup Failure Alerts

```
#!/bin/bash
# scripts/backup-with-alert.sh

BACKUP_DIR="/backups"
DB_HOST="localhost"
DB_USER="postgres"
DB_NAME="production"
ALERT_EMAIL="ops@company.com"

BACKUP_FILE="$BACKUP_DIR/backup-$(date +%Y%m%d-%H%M%S).sql"

# Perform backup
if pg_dump -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" > "$BACKUP_FILE"; then
    gzip "$BACKUP_FILE"
    FILE_SIZE=$(du -h "${BACKUP_FILE}.gz" | cut -f1)

    echo "Backup successful: ${BACKUP_FILE}.gz ($FILE_SIZE)" | \
        mail -s "Database Backup Success" "$ALERT_EMAIL"
else
    echo "BACKUP FAILED at $(date)" | \
        mail -s "URGENT: Database Backup Failed" "$ALERT_EMAIL"
    exit 1
fi
```

### Backup Status Dashboard Script

```
#!/bin/bash
# scripts/backup-status.sh

BACKUP_DIR="/backups"
ALERT_EMAIL="ops@company.com"

echo "=== Backup Status Report ==="
echo "Generated: $(date)"
echo ""

# Check backup age
LATEST_BACKUP=$(ls -t "$BACKUP_DIR"/*.sql.gz 2>/dev/null | head -1)

if [ -z "$LATEST_BACKUP" ]; then
    echo "ERROR: No backups found!"
    echo "Error: No backups in $BACKUP_DIR" | mail -s "CRITICAL: No database backups found" "$ALERT_EMAIL"
    exit 1
fi

BACKUP_TIME=$(stat -c %y "$LATEST_BACKUP" | cut -d' ' -f1)
BACKUP_SIZE=$(du -h "$LATEST_BACKUP" | cut -f1)
BACKUP_AGE_HOURS=$(( ($(date +%s) - $(stat -c %Y "$LATEST_BACKUP")) / 3600 ))

echo "Latest backup: $LATEST_BACKUP"
echo "Time: $BACKUP_TIME"
echo "Size: $BACKUP_SIZE"
echo "Age: ${BACKUP_AGE_HOURS} hours"
echo ""

# Alert if backup is too old
if [ "$BACKUP_AGE_HOURS" -gt 25 ]; then
    echo "WARNING: Backup is older than 24 hours!"
    echo "Last backup is ${BACKUP_AGE_HOURS} hours old" | \
        mail -s "WARNING: Stale database backup" "$ALERT_EMAIL"
fi

# Check disk space
DISK_USAGE=$(df "$BACKUP_DIR" | awk 'NR==2 {print $5}' | sed 's/%//')
echo "Disk usage: $DISK_USAGE%"

if [ "$DISK_USAGE" -gt 80 ]; then
    echo "ALERT: Backup storage above 80%!"
    echo "Disk usage is $DISK_USAGE%" | \
        mail -s "ALERT: Backup storage nearly full" "$ALERT_EMAIL"
fi
```

## Complete Backup Docker Compose Setup

Production-ready backup infrastructure:

```
version: '3.8'

services:
  postgres:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: secure_password
    volumes:
      - postgres-data:/var/lib/postgresql/data
    networks:
      - db-network

  backup-scheduler:
    image: backup-scheduler:latest
    environment:
      DB_HOST: postgres
      DB_USER: postgres
      DB_PASSWORD: secure_password
      BACKUP_DIR: /backups
      AWS_BUCKET: s3://company-backups
      BACKUP_SCHEDULE: "0 2 * * *"
    volumes:
      - ./scripts:/scripts:ro
      - backup-storage:/backups
      - /var/run/docker.sock:/var/run/docker.sock
    depends_on:
      - postgres
    networks:
      - db-network

  backup-verify:
    image: postgres:15
    environment:
      PGPASSWORD: secure_password
    volumes:
      - ./scripts/verify-backup.sh:/verify.sh:ro
      - backup-storage:/backups:ro
    depends_on:
      - postgres
    networks:
      - db-network
    # Run verification daily
    entrypoint: /bin/bash -c "while true; do sleep 86400; /verify.sh /backups/*.sql.gz; done"

  backup-monitor:
    image: backup-monitor:latest
    volumes:
      - ./scripts/backup-status.sh:/monitor.sh:ro
      - backup-storage:/backups:ro
    environment:
      ALERT_EMAIL: ops@company.com
    # Check backup status every hour
    entrypoint: /bin/bash -c "while true; do sleep 3600; /monitor.sh; done"
    networks:
      - db-network

volumes:
  postgres-data:
  backup-storage:

networks:
  db-network:
```

## Best Practices

Maintain at least:
- 3 copies of critical data
- 2 different storage media
- 1 copy offsite

This ensures protection against hardware failure and disaster.

Schedule monthly restore tests from backups to verify integrity and document recovery procedures before you need them.

Automatically delete old backups according to your retention policy to control storage costs and comply with regulations.

Track backup duration and size trends to detect issues early. Alert on failed backups or unusually long backup times.

Maintain detailed runbooks for restoring from backups, including:
- Required credentials
- Network requirements
- Expected recovery time
- Verification steps

Use encryption for backups in transit and at rest, especially when storing in cloud storage:

```
# Encrypt backup before uploading
gpg --symmetric --cipher-algo AES256 backup.sql
aws s3 cp backup.sql.gpg s3://backups/
```

For database schema backups, store them in version control with diffs to track schema evolution:

```
pg_dump --schema-only > schema-$(date +%Y%m%d).sql
git add schema-*.sql
git commit -m "Database schema backup $(date +%Y-%m-%d)"
```

## Disaster Recovery Planning

### Recovery Time Objective (RTO) and Recovery Point Objective (RPO)

Define these metrics for your backup strategy:

- **RTO**: Maximum acceptable downtime (e.g., 4 hours)
- **RPO**: Maximum acceptable data loss (e.g., 1 hour)

These determine backup frequency and retention:

```
If RPO = 1 hour: Backup at least every hour
If RTO = 4 hours: Keep at least 24+ hours of backups for flexibility
```

### Backup Disaster Recovery Checklist

- Document all backup procedures and locations
- Test recovery procedures quarterly
- Maintain redundant backup copies in different geographic regions
- Practice failover to backup systems
- Maintain current documentation of database credentials and access procedures
- Verify backup encryption and access controls

## Summary

Effective backup integration with WhoDB includes:

- Multiple backup strategies for different database types
- Automated scheduling with cron, Docker, or Kubernetes
- Cloud storage integration for durability and cost optimization
- Regular verification and testing procedures
- Comprehensive monitoring and alerting
- Clear retention policies and compliance requirements

Proper backup procedures ensure business continuity and provide peace of mind knowing your data is protected against any eventuality.

You're ready to implement enterprise-grade backup and disaster recovery for your databases

---


# resources/keyboard-shortcuts.mdx
## Keyboard Shortcuts Reference

Master WhoDB with comprehensive keyboard shortcuts for navigation, data editing, querying, and more

# Keyboard Shortcuts Reference

Speed up your WhoDB workflow with these keyboard shortcuts. Whether you're navigating databases, writing queries, or editing data, mastering these shortcuts will make you significantly more productive.

Bookmark this page for quick reference while working in WhoDB

## Global Shortcuts

These shortcuts work throughout WhoDB and are accessible from any view or context.

| Shortcut | Action | Notes |
|----------|--------|-------|
| `Cmd/Ctrl + K` | Open command palette | Quickly navigate to any feature or database |
| `Cmd/Ctrl + /` | Toggle sidebar | Show or hide the left navigation panel |
| `Cmd/Ctrl + F` | Find in current view | Search text on the current page |
| `Esc` | Close dialogs/modals | Exit any open dialog or context menu |
| `Cmd/Ctrl + Shift + ?` | Show keyboard shortcuts help | Display all available shortcuts |
| `Tab` | Focus next element | Navigate between interactive elements |
| `Shift + Tab` | Focus previous element | Navigate backwards between elements |
| `Enter` | Confirm/Submit | Accept dialog or complete action |
| `Cmd/Ctrl + ,` | Open settings | Access application preferences |
| `Cmd/Ctrl + Alt + D` | Toggle dark mode | Switch between light and dark theme |

## Data Grid Shortcuts

Use these shortcuts when viewing table data to navigate and edit records efficiently.

| Shortcut | Action | Notes |
|----------|--------|-------|
| `Cmd/Ctrl + A` | Select all rows | Select every row in the current table or filtered view |
| `Cmd/Ctrl + C` | Copy selected cell | Copy cell value to clipboard |
| `Cmd/Ctrl + X` | Cut selected cell | Cut and copy cell value |
| `Cmd/Ctrl + V` | Paste to cell | Paste clipboard content into cell |
| `Tab` | Move to next cell | Navigate right or to first cell in next row |
| `Shift + Tab` | Move to previous cell | Navigate left or to last cell in previous row |
| `Arrow Up` | Move up one row | Navigate to cell above current |
| `Arrow Down` | Move down one row | Navigate to cell below current |
| `Arrow Left` | Move left one column | Navigate to cell on the left |
| `Arrow Right` | Move right one column | Navigate to cell on the right |
| `Enter` | Edit selected row | Open edit dialog for current row |
| `Double-click` | Inline edit cell | Edit cell value directly in the grid |
| `Escape` | Cancel editing | Discard changes and exit edit mode |
| `Delete` | Clear cell value | Empty the current cell (in edit mode) |
| `Cmd/Ctrl + D` | Duplicate row | Create a copy of the current row |
| `Cmd/Ctrl + Shift + A` | Add new row | Open dialog to add a new record |
| `Cmd/Ctrl + Shift + Delete` | Delete row | Remove the current row (with confirmation) |
| `Page Down` | Scroll down | Load and view next page of results |
| `Page Up` | Scroll up | Load and view previous page of results |
| `Home` | Go to first cell | Jump to first column of current row |
| `End` | Go to last cell | Jump to last column of current row |
| `Cmd/Ctrl + Home` | Go to first cell | Jump to top-left corner of table |
| `Cmd/Ctrl + End` | Go to last cell | Jump to bottom-right corner of table |

## Scratchpad Query Editor Shortcuts

The Scratchpad is WhoDB's powerful query interface. Use these shortcuts to write and execute queries faster.

| Shortcut | Action | Notes |
|----------|--------|-------|
| `Cmd/Ctrl + Enter` | Execute query | Run the current cell's SQL query |
| `Cmd/Ctrl + S` | Save query | Save query to history |
| `Cmd/Ctrl + N` | New cell | Insert a new query cell below current |
| `Cmd/Ctrl + D` | Duplicate cell | Create a copy of the current cell |
| `Cmd/Ctrl + Shift + Delete` | Delete cell | Remove the current query cell |
| `Cmd/Ctrl + M` | Merge cells | Combine current and next cell |
| `Cmd/Ctrl + /` | Toggle comment | Comment or uncomment selected lines |
| `Cmd/Ctrl + Shift + F` | Format query | Auto-format SQL code with proper indentation |
| `Cmd/Ctrl + F` | Find in editor | Search within query text |
| `Cmd/Ctrl + H` | Find and replace | Search and replace text in queries |
| `Cmd/Ctrl + Z` | Undo | Revert last change in editor |
| `Cmd/Ctrl + Shift + Z` | Redo | Reapply last undone change |
| `Tab` | Insert indentation | Add spaces for code formatting |
| `Shift + Tab` | Remove indentation | Decrease indentation level |
| `Cmd/Ctrl + A` | Select all | Select all text in current cell |
| `Cmd/Ctrl + [` | Decrease font size | Make code smaller (if supported) |
| `Cmd/Ctrl + ]` | Increase font size | Make code larger (if supported) |
| `Up Arrow` | Previous query in history | Load previous query from history |
| `Down Arrow` | Next query in history | Load next query from history |
| `Cmd/Ctrl + Shift + H` | Open query history | Display full query history panel |
| `Escape` | Close autocomplete | Hide code suggestions dropdown |

## Navigation Shortcuts

Quickly navigate between different views and database objects.

| Shortcut | Action | Notes |
|----------|--------|-------|
| `Cmd/Ctrl + 1` | Go to Data view | Switch to table data view |
| `Cmd/Ctrl + 2` | Go to Explore view | Switch to schema explorer |
| `Cmd/Ctrl + 3` | Go to Scratchpad view | Switch to query editor |
| `Cmd/Ctrl + 4` | Go to Graph view | Switch to schema visualization |
| `Cmd/Ctrl + 5` | Go to Chat view | Switch to AI assistant (if enabled) |
| `Cmd/Ctrl + L` | Focus on table selector | Jump to select different table |
| `Cmd/Ctrl + Shift + L` | Focus on database selector | Jump to select different database |
| `Cmd/Ctrl + Shift + S` | Focus on schema selector | Jump to select different schema |
| `Alt + 1` | First open tab | Jump to first tab |
| `Alt + 2` | Second open tab | Jump to second tab |
| `Alt + N` | Nth open tab | Jump to Nth tab |
| `Cmd/Ctrl + W` | Close current tab | Close active view or connection |
| `Cmd/Ctrl + T` | New tab | Open new connection or table |
| `Cmd/Ctrl + Shift + T` | Reopen closed tab | Restore recently closed tab |

## Search & Filter Shortcuts

Efficiently search and filter data across WhoDB.

| Shortcut | Action | Notes |
|----------|--------|-------|
| `Cmd/Ctrl + F` | Quick search | Open search box in data view |
| `Escape` | Close search | Hide search box and clear results |
| `Enter` | Find next | Jump to next search result |
| `Shift + Enter` | Find previous | Jump to previous search result |
| `Cmd/Ctrl + Alt + F` | Advanced filter | Open WHERE conditions filter panel |
| `Cmd/Ctrl + Shift + C` | Clear filters | Remove all active filters and WHERE conditions |
| `Up` / `Down` | Navigate search results | Move between highlighted matches |

## Selection & Editing Shortcuts

Control how you select and modify data in WhoDB.

| Shortcut | Action | Notes |
|----------|--------|-------|
| `Cmd/Ctrl + A` | Select all | Select all rows or all text |
| `Shift + Click` | Range select | Select multiple rows between two clicks |
| `Cmd/Ctrl + Click` | Multi-select | Add or remove row from selection |
| `Cmd/Ctrl + Shift + A` | Deselect all | Clear all row selections |
| `Cmd/Ctrl + I` | Invert selection | Toggle selection of all rows |
| `Cmd/Ctrl + E` | Export selected | Open export dialog for selected rows |
| `Cmd/Ctrl + Shift + E` | Quick export | Export selected rows as CSV |
| `Cmd/Ctrl + P` | Print selected | Print selected rows (browser print dialog) |

## Export & Download Shortcuts

Quick access to export and download functionality.

| Shortcut | Action | Notes |
|----------|--------|-------|
| `Cmd/Ctrl + Shift + E` | Quick CSV export | Export visible data as CSV file |
| `Cmd/Ctrl + Shift + J` | Quick JSON export | Export visible data as JSON file |
| `Cmd/Ctrl + Alt + E` | Full export dialog | Open comprehensive export options |
| `Cmd/Ctrl + Shift + X` | Export as SQL | Export as SQL INSERT statements |

## Graph Visualization Shortcuts

Navigate and interact with the database schema graph view.

| Shortcut | Action | Notes |
|----------|--------|-------|
| `Space + Drag` | Pan canvas | Move around the graph |
| `Scroll Wheel` | Zoom in/out | Change zoom level of graph |
| `Cmd/Ctrl + 0` | Reset zoom | Fit entire schema in view |
| `Cmd/Ctrl + Plus` | Zoom in | Increase graph zoom level |
| `Cmd/Ctrl + Minus` | Zoom out | Decrease graph zoom level |
| `Cmd/Ctrl + F` | Find table in graph | Search for specific table |
| `Double-click node` | View table data | Navigate to table's data view |
| `Right-click node` | Node context menu | Show options for selected table |
| `Escape` | Deselect node | Remove selection from current node |

## AI Chat Shortcuts

Use these shortcuts with the AI assistant feature (when enabled).

| Shortcut | Action | Notes |
|----------|--------|-------|
| `Cmd/Ctrl + Shift + A` | Open AI chat | Toggle AI assistant panel |
| `Cmd/Ctrl + Alt + A` | Clear chat history | Delete all previous AI conversations |
| `Escape` | Close chat | Hide AI assistant panel |
| `Enter` | Send message | Submit query to AI assistant |
| `Shift + Enter` | New line | Add line break without sending |
| `Up / Down` | Previous/next message | Navigate through chat history |

## Accessibility Shortcuts

These shortcuts improve accessibility and usability for all users.

| Shortcut | Action | Notes |
|----------|--------|-------|
| `Alt + /` | Skip to main content | Jump over navigation |
| `Cmd/Ctrl + Alt + H` | Accessibility help | Show accessibility guide |
| `Tab` | Focus navigation | Move through focusable elements |
| `Shift + Tab` | Reverse focus | Move backwards through elements |
| `Space` | Activate button | Press focused button or checkbox |
| `Enter` | Activate link | Follow focused link |
| `Alt + Number` | Jump to heading | Navigate to specific heading level |

## Platform-Specific Notes

### macOS

- Use `Cmd` instead of `Ctrl` for all shortcuts
- `Option` is the equivalent of `Alt`
- Keyboard shortcuts work in most applications

### Windows & Linux

- Use `Ctrl` instead of `Cmd`
- `Alt` key available for Windows shortcuts
- Modifier combinations may vary by application

### Mobile & Tablet

- Most keyboard shortcuts not available
- Touch gestures replace keyboard actions
- On-screen keyboard may be limited

## Customizing Keyboard Shortcuts

You can customize keyboard shortcuts in the settings:

Press `Cmd/Ctrl + ,` to open the settings panel

Click on "Keyboard Shortcuts" in the settings menu

Find the shortcut you want to customize

Click the shortcut and enter your preferred key combination

Changes are saved automatically

Some shortcuts cannot be customized as they conflict with browser or OS shortcuts

## Tips for Keyboard Power Users

- Use `Tab` and `Shift + Tab` to navigate between elements
- Use `Enter` and `Space` to activate buttons
- Use arrow keys for precise navigation
- Combine with `Cmd/Ctrl` for faster jumps

- Combine shortcuts to create efficient workflows
- Example: `Cmd/Ctrl + F` then `Cmd/Ctrl + Shift + F` for advanced search
- Use history shortcuts to repeat common queries
- Save frequently used shortcuts in your muscle memory

- Use `Cmd/Ctrl + K` for quick navigation to any feature
- Memorize the numeric shortcuts (`Cmd/Ctrl + 1` through `5`) for view switching
- Master `Cmd/Ctrl + Enter` for fast query execution
- Use `Cmd/Ctrl + N` and `Cmd/Ctrl + D` to quickly organize Scratchpad

**Quick Data Export Workflow:**
1. `Cmd/Ctrl + A` - Select all rows
2. `Cmd/Ctrl + Shift + E` - Export as CSV

**Multi-Cell Query Workflow:**
1. `Cmd/Ctrl + N` - New cell
2. `Cmd/Ctrl + Enter` - Execute
3. `Cmd/Ctrl + D` - Duplicate for similar query

**Navigation Workflow:**
1. `Cmd/Ctrl + K` - Open command palette
2. Type table name
3. `Enter` - Navigate to table

## Keyboard Shortcut Cheat Sheet

`Cmd/Ctrl + K` - Command palette
`Cmd/Ctrl + /` - Toggle sidebar
`Esc` - Close dialog

`Cmd/Ctrl + A` - Select all
`Tab` - Next cell
`Enter` - Edit row

`Cmd/Ctrl + Enter` - Execute query
`Cmd/Ctrl + N` - New cell
`Cmd/Ctrl + D` - Duplicate cell

`Cmd/Ctrl + 1-5` - Switch views
`Cmd/Ctrl + L` - Select table
`Cmd/Ctrl + W` - Close tab

## Learning Resources

- Start with global shortcuts (`Cmd/Ctrl + K`, `Cmd/Ctrl + /`)
- Move to view-specific shortcuts based on your workflow
- Gradually add more shortcuts as they become useful
- Focus on the most frequently used actions

- Practice one shortcut at a time
- Use the shortcut repeatedly until it becomes automatic
- Keep this reference page open while learning
- Test yourself periodically

If a shortcut doesn't work:
- Check if your OS or browser has intercepted it
- Try the equivalent for your platform (Cmd vs Ctrl)
- Reset to default shortcuts in settings
- Check the settings panel for custom assignments

## Related Documentation

Learn basic WhoDB features and workflows

Master data viewing and editing techniques

Explore Scratchpad querying capabilities

Understand WhoDB interface navigation

Keyboard shortcuts can significantly boost your productivity. Start with a few favorites and gradually expand your repertoire as you become more comfortable with WhoDB.

---


# resources/glossary.mdx
## Database and WhoDB Glossary

Comprehensive glossary of database terminology and WhoDB-specific concepts with clear definitions

# Database and WhoDB Glossary

A comprehensive reference guide to database and WhoDB terminology. Whether you're new to databases or looking for a specific term, this glossary provides clear, concise definitions to help you understand database concepts and WhoDB features.

Use the search function (Cmd/Ctrl + F) to quickly find terms in this glossary

## A

**Aggregate Function**
A SQL function that performs calculations across multiple rows and returns a single value. Common examples include COUNT(), SUM(), AVG(), MIN(), and MAX(). These functions are typically used with GROUP BY clauses to summarize data.

**API (Application Programming Interface)**
A set of rules and tools for building software applications. WhoDB exposes functionality through a GraphQL API, allowing external applications and integrations to interact with database operations programmatically.

**Auto-Increment**
A database feature that automatically generates unique numeric values for a column, typically used for primary keys. Each new row receives the next sequential number without manual intervention.

**Alias**
An alternative name given to a table, column, or subquery in SQL, typically using the AS keyword. Aliases improve query readability and are essential when working with self-joins or multiple tables.

## B

**Backup**
A complete copy of database data created for recovery purposes. WhoDB's export features allow you to create backups in various formats (SQL, CSV, Excel) for data protection and migration.

**Batch Operation**
Processing multiple database records in a single transaction or operation, rather than individually. WhoDB supports batch operations for adding, editing, and deleting multiple rows efficiently.

**Boolean**
A data type that stores one of two values: TRUE or FALSE. Used for binary decisions and flags in databases, often represented as 1 or 0 in some systems.

## C

**Cardinality**
The relationship type between two tables in a database schema, describing how many records in one table can relate to records in another. Common cardinalities include one-to-one, one-to-many, and many-to-many.

**Cascade**
A referential integrity constraint that automatically propagates changes (updates or deletes) from parent to child records. When a parent row is deleted, CASCADE delete removes all related child rows.

**Chat Feature**
WhoDB's AI-powered natural language interface for querying databases. Users can describe their data needs in plain language, and the AI translates them into SQL queries or provides schema insights.

**ClickHouse**
An open-source columnar database management system optimized for analytical queries and real-time data. Supported in WhoDB for handling large volumes of time-series data and analytics workloads.

**Column**
A vertical structure in a table representing a specific attribute of data. Each column has a name and data type, defining what kind of values it can store.

**Composite Key**
A primary key consisting of two or more columns together. Used when a single column cannot uniquely identify rows, requiring a combination of columns for uniqueness.

**Constraint**
A rule applied to database columns or tables to enforce data integrity. Common constraints include PRIMARY KEY, FOREIGN KEY, UNIQUE, NOT NULL, and CHECK constraints.

**Connection Pool**
A collection of reusable database connections managed by WhoDB to improve performance. Rather than creating new connections for each operation, connections are reused when available.

**CSV (Comma-Separated Values)**
A plain-text file format for storing tabular data where rows are separated by newlines and columns by commas. WhoDB supports both importing and exporting data in CSV format.

## D

**Data Type**
A classification that specifies what kind of data a column can store, such as INTEGER, VARCHAR, DATE, BOOLEAN, or JSON. Data types ensure data integrity and optimize storage.

**Database**
An organized collection of structured data stored in a database management system. WhoDB supports connecting to multiple databases across different database engines.

**Database Engine**
The underlying software system that manages data storage, retrieval, and manipulation. Examples include PostgreSQL, MySQL, MongoDB, and SQLite.

**DBeaver**
A popular GUI database management tool. WhoDB offers a lighter-weight alternative for database exploration and management with modern UI/UX.

**DELETE**
A SQL statement that removes one or more rows from a table based on specified conditions. In WhoDB, deletions can be performed via the data grid interface or Scratchpad queries.

**Delimiter**
A character used to separate fields or values, most commonly a comma in CSV files. WhoDB allows customization of delimiters when exporting or importing data.

## E

**Elasticsearch**
A distributed search and analytics engine built on top of Apache Lucene. Supported in WhoDB for searching and analyzing large volumes of unstructured data.

**Entity-Relationship (ER) Model**
A method of visually representing database structures showing entities (tables) and their relationships. WhoDB's Graph view provides an interactive ER diagram of your database schema.

**Export**
The process of extracting data from WhoDB in various formats (CSV, Excel, JSON, SQL) for use in other applications or for backup purposes.

**Explore View**
One of WhoDB's primary views that displays detailed information about a table's structure including columns, data types, indexes, primary keys, and foreign key relationships.

## F

**Filter**
A mechanism to restrict which rows are displayed based on specified criteria. WhoDB supports filtering via the search box and WHERE condition builders.

**Foreign Key**
A column or combination of columns that references a primary key in another table, enforcing referential integrity and establishing relationships between tables.

**Format Query**
WhoDB's feature to automatically reorganize and indent SQL code for improved readability using keyboard shortcut Cmd/Ctrl + Shift + F.

**Full-Text Search**
A search capability that indexes and searches all text columns in a table. Available in databases that support full-text search like PostgreSQL with text search extensions.

## G

**Graph View**
WhoDB's visual representation of database schema showing tables as nodes and foreign key relationships as connecting lines. Interactive visualization allows panning, zooming, and node exploration.

**GraphQL**
A query language and runtime for APIs (Application Programming Interface) used throughout WhoDB for all backend communication, providing strongly-typed and flexible data querying.

**GORM**
Go Object-Relational Mapping library used in WhoDB's backend for database interactions, providing type safety and database abstraction.

**GROUP BY**
A SQL clause that groups rows with identical values in specified columns, typically used with aggregate functions to summarize data by categories.

## H

**Hash Function**
A cryptographic function used for password hashing and data integrity verification, ensuring passwords are never stored in plain text in WhoDB.

**HAVING**
A SQL clause that filters grouped data (rows returned by GROUP BY), similar to WHERE but applied after grouping and aggregation.

## I

**Index**
A database structure that speeds up data retrieval by creating a sorted key-value lookup. Indexes significantly improve query performance on frequently searched columns but require additional storage.

**INSERT**
A SQL statement that adds one or more new rows to a table. In WhoDB, rows can be added through the data grid interface or Scratchpad queries.

**Instance**
A single running copy of a database server. WhoDB can connect to multiple database instances simultaneously, either locally or remotely.

## J

**JOIN**
A SQL operation that combines rows from two or more tables based on related columns. Common types include INNER JOIN, LEFT JOIN, RIGHT JOIN, and FULL OUTER JOIN.

**JSON (JavaScript Object Notation)**
A lightweight data format using key-value pairs and hierarchical structures. WhoDB supports JSON data types in many databases and can export data as JSON.

**JSON Export**
WhoDB's capability to export database query results in JSON format, useful for APIs, web applications, and data processing pipelines.

## K

**Key**
A constraint or index used to uniquely identify rows (primary key) or establish relationships (foreign key) in a database table.

**Keyboard Shortcuts**
Quick key combinations in WhoDB for common operations. Examples: Cmd/Ctrl + Enter to execute queries, Cmd/Ctrl + A to select all rows.

## L

**LIMIT**
A SQL clause that restricts the number of rows returned by a query. Essential for performance optimization when working with large datasets.

## M

**MariaDB**
An open-source relational database that is a fork of MySQL, maintaining compatibility while adding new features. Supported in WhoDB's Community Edition.

**Mock Data**
Synthetic test data generated by WhoDB using realistic patterns for each data type. Useful for development, testing, and demonstration purposes without exposing real data.

**MongoDB**
A NoSQL document database that stores data in flexible JSON-like documents rather than rigid relational tables. Supported in WhoDB for document database exploration.

**MySQL**
A popular open-source relational database management system widely used in web applications. Fully supported in WhoDB.

## N

**NoSQL**
A category of databases that don't use traditional relational table structures. Examples include MongoDB, Redis, and Elasticsearch, all supported in WhoDB.

**NOT NULL**
A constraint ensuring a column always contains a value and cannot be empty or null. Critical for data integrity on required fields.

**Null**
A special value representing the absence of data or an unknown value, distinct from zero or empty string in databases.

## O

**OFFSET**
A SQL clause used with LIMIT to skip a specified number of rows before retrieving results, used for pagination in data browsing.

**ORDER BY**
A SQL clause that sorts query results by specified columns in ascending (ASC) or descending (DESC) order.

## P

**Pagination**
Dividing large datasets into smaller, manageable pages for display and navigation. WhoDB automatically implements pagination with configurable page sizes.

**Parameter**
A placeholder in a SQL query replaced with actual values at execution time, preventing SQL injection attacks. WhoDB uses parameterized queries internally for security.

**pgAdmin**
A popular GUI administration tool for PostgreSQL databases. WhoDB serves as a modern alternative with support for multiple database types.

**phpMyAdmin**
A widely-used web-based administration tool for MySQL. WhoDB offers similar functionality with enhanced UI and additional database support.

**Plugin**
WhoDB's architecture component for database engine support. Each database type (PostgreSQL, MySQL, etc.) is implemented as a plugin ensuring clean separation of concerns.

**PostgreSQL**
An advanced open-source relational database known for reliability, advanced features, and SQL compliance. Widely supported and popular with WhoDB users.

**Primary Key**
A column or combination of columns that uniquely identifies each row in a table. Each table should have exactly one primary key for data integrity.

## Q

**Query**
A request for data from a database written in SQL or another query language. WhoDB's Scratchpad is designed for writing and executing complex queries.

**Query History**
WhoDB's feature that automatically saves all executed queries for easy access and reuse, visible in the Scratchpad interface.

**Quick Start**
WhoDB's introductory guide helping new users connect to their first database and understand basic features in minutes.

## R

**Recursive Query**
A query that references itself, used for hierarchical data exploration like organizational structures or category trees. Supported in advanced SQL databases via Common Table Expressions (CTEs).

**Referential Integrity**
A database principle ensuring that foreign key values always reference valid primary key values in related tables, maintained through constraints.

**Redis**
An in-memory data structure store used for caching, messaging, and real-time analytics. Supported in WhoDB for exploring Redis data structures and keys.

**Relational Database**
A database organized into tables with relationships between them, following the relational model for data organization and querying.

**Relationship**
A connection between two tables established through foreign keys, representing how data in different tables relates to each other.

**Row**
A horizontal record in a database table containing values for all columns, representing a single instance of the entity the table describes.

## S

**Schema**
The blueprint of a database describing all tables, columns, relationships, constraints, and other structural elements. WhoDB visualizes schemas in the Explorer and Graph views.

**Schema Explorer**
WhoDB's sidebar view showing the complete database structure including databases, schemas, tables, views, and stored procedures in a hierarchical tree.

**Scratchpad**
WhoDB's primary query interface for writing and executing SQL queries, similar to Jupyter notebooks but for databases, supporting multiple cells and result visualization.

**Search**
WhoDB's capability to find specific data across tables using the quick search box or WHERE conditions. Also includes full-text search where database engines support it.

**SELECT**
A SQL statement that retrieves data from one or more tables, the most common query type in databases.

**SQL (Structured Query Language)**
The standardized language for querying and manipulating relational databases, universally supported across database engines.

**SQLite**
A lightweight, serverless relational database engine ideal for embedded applications, development, and testing. Fully supported in WhoDB.

**SSH (Secure Shell)**
A secure protocol for remote command execution. WhoDB supports SSH tunneling for securely accessing remote databases through a bastion host.

**SSL/TLS**
Cryptographic protocols for secure communication over networks. WhoDB supports SSL/TLS encryption for database connections over the internet.

**Storage Unit**
WhoDB's terminology for a connected database instance, representing a specific database server or service you can explore and interact with.

## T

**Table**
The primary data structure in relational databases consisting of columns (fields) and rows (records) organized in a grid format.

**Transaction**
A group of SQL statements treated as a single atomic unit that either all succeed or all fail together, ensuring data consistency.

**Trigger**
A special type of stored procedure automatically executed when specific database events occur (INSERT, UPDATE, DELETE), used for automated data management.

## U

**UPDATE**
A SQL statement that modifies existing row data in a table based on specified conditions. In WhoDB, rows can be updated through the data grid or Scratchpad.

**UNIQUE**
A constraint ensuring all values in a column are distinct with no duplicates allowed, while NULL values may be permitted (varies by database).

## V

**View**
A virtual table derived from one or more base tables, containing the result of a stored SELECT query. WhoDB displays database views in the schema explorer alongside tables.

**Visualization**
WhoDB's capability to represent data and schema structures graphically, including data tables, graphs, and interactive diagrams for easier comprehension.

## W

**WHERE**
A SQL clause specifying conditions that rows must meet to be included in query results, used for filtering data.

**WhoDB**
A modern, open-source database management tool providing a unified interface for multiple database types with visual schema exploration and powerful query capabilities.

**WHERE Conditions**
WhoDB's GUI interface for building complex WHERE clauses without writing SQL, supporting operators like `=`, `!=`, `>`, `<`, `>=`, `<=`, `LIKE`, and `IN`.

**Window Function**
An advanced SQL feature that performs calculations across a set of table rows related to the current row, useful for ranking, running totals, and analytics.

## X

**XML (eXtensible Markup Language)**
A markup language for storing and transporting structured data, supported as a data type in some advanced databases like PostgreSQL.

## Z

**Zone (Time Zone)**
A region's standard time offset used in datetime operations. WhoDB respects database time zones when displaying and storing temporal data.

---

## Glossary by Category

### Database Concepts

- **Database**: Organized collection of structured data
- **Table**: Grid structure with rows and columns
- **Row**: Single record in a table
- **Column**: Attribute or field in a table
- **Primary Key**: Unique identifier for rows
- **Foreign Key**: Reference to another table's primary key
- **Index**: Structure for faster data retrieval
- **Schema**: Blueprint of database structure

- **SELECT**: Retrieve data from tables
- **INSERT**: Add new rows to tables
- **UPDATE**: Modify existing row data
- **DELETE**: Remove rows from tables
- **JOIN**: Combine data from multiple tables
- **GROUP BY**: Organize rows into groups
- **ORDER BY**: Sort query results
- **WHERE**: Filter rows based on conditions

- **Integer**: Whole numbers
- **VARCHAR**: Variable-length text
- **Date**: Calendar dates
- **Boolean**: True/False values
- **JSON**: Hierarchical data structures
- **UUID**: Universally unique identifiers
- **Decimal**: Precise decimal numbers
- **Timestamp**: Date and time values

- **PostgreSQL**: Advanced open-source relational database
- **MySQL**: Popular web database
- **SQLite**: Lightweight embedded database
- **MongoDB**: NoSQL document database
- **Redis**: In-memory data structure store
- **Elasticsearch**: Search and analytics engine
- **MariaDB**: MySQL-compatible database
- **ClickHouse**: Columnar analytics database

### WhoDB-Specific Terms

- **Scratchpad**: Query editor interface
- **Graph View**: Visual schema representation
- **Schema Explorer**: Hierarchical structure browser
- **Chat Feature**: AI-powered query assistant
- **Mock Data**: Generated test data
- **Export**: Data download functionality
- **Batch Operations**: Multiple row modifications
- **Keyboard Shortcuts**: Quick action commands

- **Community Edition**: Free, open-source version
- **Enterprise Edition**: Commercial version with additional databases
- **Storage Unit**: Connected database instance
- **Connection Profile**: Saved database credentials

- **Inline Edit**: Direct cell value modification
- **Quick Export**: Rapid data download
- **Filter**: Restrict visible rows
- **Search**: Find specific data
- **Pagination**: Browse large datasets
- **Sort**: Arrange rows by column values

## Related Documentation

Get started with WhoDB basics

Master productivity shortcuts

View all supported database engines

Find answers to common questions

This glossary is regularly updated with new terms and definitions as WhoDB evolves. If you encounter a term not in this glossary, check the main documentation or community forums.

---


# resources/supported-databases.mdx
## Supported Databases

Complete list of databases supported by WhoDB with version compatibility, features, and limitations

# Supported Databases

WhoDB supports a comprehensive range of databases across relational, NoSQL, and data warehouse categories. This guide details version compatibility, supported features, and any limitations for each database type.

Check this page to verify your database version is supported before connecting to WhoDB

## Community Edition Databases

The Community Edition of WhoDB supports eight popular databases at no cost.

### PostgreSQL

The most advanced open-source relational database with powerful features and SQL compliance.

**Version Support:**
- PostgreSQL 9.6 through 16+
- Latest versions recommended for full feature support

| Feature | Support | Notes |
|---------|---------|-------|
| Basic connectivity | ✓ | Full connection support |
| Schema navigation | ✓ | Multiple schemas supported |
| Data manipulation | ✓ | INSERT, UPDATE, DELETE operations |
| Transactions | ✓ | ACID compliance, explicit transactions |
| Views | ✓ | Query and manage views |
| Stored procedures | ✓ | Create and execute functions |
| Foreign keys | ✓ | Full referential integrity |
| Full-text search | ✓ | Text search with GiST/GIN indexes |
| Array types | ✓ | Native array support |
| JSON/JSONB | ✓ | Advanced JSON operations |
| Window functions | ✓ | Ranking and analytic functions |
| CTEs | ✓ | Common Table Expressions |
| Mock data generation | ✓ | Realistic data for testing |
| Export (CSV, Excel, JSON, SQL) | ✓ | All formats supported |

**Recommended Connection Settings:**
- Use SSL mode "require" or "verify-full" for remote connections
- Enable connection pooling for multiple concurrent users
- Set statement_timeout for long-running queries

**Limitations:**
- Connection through SSH tunnel requires proper SSH key setup
- Some advanced PL/pgSQL debugging features not available
- Custom data types require specific JSON serialization

### MySQL

The world's most popular open-source relational database, widely used in web applications.

**Version Support:**
- MySQL 5.7 through 8.4+
- MariaDB 10.3 through 11.0+
- Percona Server 5.7+

| Feature | Support | Notes |
|---------|---------|-------|
| Basic connectivity | ✓ | Full connection support |
| Multiple databases | ✓ | Switch between databases easily |
| Data manipulation | ✓ | INSERT, UPDATE, DELETE operations |
| Transactions | ✓ | ACID support in InnoDB |
| Views | ✓ | Query and manage views |
| Stored procedures | ✓ | Execute stored routines |
| Foreign keys | ✓ | Supported in InnoDB tables |
| Triggers | ✓ | Create and manage triggers |
| Full-text search | ✓ | FULLTEXT indexes on MyISAM/InnoDB |
| JSON | ✓ | JSON data type and functions |
| Partitioning | ✓ | Query partitioned tables |
| Mock data generation | ✓ | Compatible data generation |
| Export (CSV, Excel, JSON, SQL) | ✓ | All formats supported |

**Recommended Connection Settings:**
- Use SSL mode "required" for encrypted connections
- Set max_allowed_packet if working with large data
- Use connection pooling to manage concurrent connections

**Limitations:**
- MyISAM tables don't support transactions
- UNSIGNED data types handled as regular integers in some contexts
- Some JSON path expressions limited compared to PostgreSQL

### SQLite

A lightweight, embedded SQL database engine ideal for development, testing, and mobile applications.

**Version Support:**
- SQLite 3.8 through 3.45+
- All recent versions supported

| Feature | Support | Notes |
|---------|---------|-------|
| Basic connectivity | ✓ | Local file path required |
| Schema navigation | ✓ | Single database file |
| Data manipulation | ✓ | INSERT, UPDATE, DELETE operations |
| Transactions | ✓ | ACID compliance |
| Views | ✓ | Create and query views |
| Triggers | ✓ | Create and manage triggers |
| Foreign keys | ✓ | Must be enabled with PRAGMA |
| Indexes | ✓ | Full index support |
| JSON | ✓ | JSON1 extension support |
| Full-text search | ✓ | FTS5 module support |
| Virtual tables | ✓ | Custom table implementations |
| Mock data generation | ✓ | Supported |
| Export (CSV, Excel, JSON, SQL) | ✓ | All formats supported |

**Recommended Connection Settings:**
- Store .db files in persistent volumes when using Docker
- Use appropriate permissions (600 or 644) for database files
- Consider WAL mode for concurrent access

**Limitations:**
- Single writer at a time (concurrent writes limited)
- No true user/permission system
- Limited optimization for very large datasets (GB+ sizes)
- No native encryption support

### MongoDB

A flexible NoSQL document database for unstructured and semi-structured data.

**Version Support:**
- MongoDB 3.6 through 7.0+
- MongoDB Atlas supported
- Community and Enterprise editions

| Feature | Support | Notes |
|---------|---------|-------|
| Basic connectivity | ✓ | Connection string support |
| Database browsing | ✓ | List all databases |
| Collection exploration | ✓ | Browse collections and documents |
| Document viewing | ✓ | View JSON documents |
| Document insertion | ✓ | Add new documents |
| Document editing | ✓ | Modify existing documents |
| Document deletion | ✓ | Remove documents |
| Aggregation queries | ✓ | Support for aggregation pipelines |
| Indexes | ✓ | Create and manage indexes |
| Export (JSON, CSV) | ✓ | JSON primary, CSV for tabular data |
| Mock data generation | ✓ | Generate realistic test documents |
| Authentication | ✓ | Username/password, X.509 certs |
| Atlas support | ✓ | Connect to MongoDB Atlas clusters |

**Recommended Connection Settings:**
- Use connection strings with appropriate SSL settings
- Enable authentication for production clusters
- Use MongoDB Atlas for cloud deployments

**Limitations:**
- No traditional SQL WHERE conditions, uses MongoDB query syntax
- Document collections with varying schemas display column inference
- Export to SQL not supported (MongoDB-specific)
- Real-time query execution required (no transaction support like RDBMS)

### Redis

An in-memory data structure store used for caching, messaging, and real-time analytics.

**Version Support:**
- Redis 4.0 through 7.2+
- Redis Cluster supported
- Redis Sentinel compatible

| Feature | Support | Notes |
|---------|---------|-------|
| Basic connectivity | ✓ | TCP connection support |
| Key browsing | ✓ | Navigate key space |
| Key-value viewing | ✓ | Display key values |
| Data type handling | ✓ | Strings, lists, sets, hashes, sorted sets |
| Key operations | ✓ | SET, GET, DEL, EXPIRE |
| TTL management | ✓ | View and manage key expiration |
| Export (JSON) | ✓ | Export as JSON format |
| Mock data generation | ✓ | Generate test data structures |
| Authentication | ✓ | Password and ACL support |
| Cluster mode | ✓ | Connect to Redis clusters |

**Recommended Connection Settings:**
- Use SSL for remote Redis connections
- Enable authentication in production
- Set connection timeout appropriately

**Limitations:**
- No traditional data grid view (Redis uses key-value structure)
- No SQL queries possible (uses Redis commands)
- Limited transaction support compared to RDBMS
- Export to CSV not applicable (Redis structure incompatible)
- Real-time data updates not visible without refresh

### Elasticsearch

A distributed search and analytics engine for large-scale data exploration.

**Version Support:**
- Elasticsearch 7.10 through 8.10+
- OpenSearch 1.0+ (compatible)

| Feature | Support | Notes |
|---------|---------|-------|
| Index browsing | ✓ | List and navigate indexes |
| Document viewing | ✓ | Display documents with highlighting |
| Full-text search | ✓ | Query indexes with ES query syntax |
| Aggregations | ✓ | Analytics and faceting |
| Mappings | ✓ | View index field mappings |
| Export (JSON) | ✓ | Export documents as JSON |
| Mock data generation | ✓ | Generate documents |
| Authentication | ✓ | Basic auth and API keys |
| Cluster info | ✓ | View cluster and shard status |

**Recommended Connection Settings:**
- Use HTTPS for remote connections
- Authenticate with appropriate credentials
- Set timeout for large aggregations

**Limitations:**
- No INSERT/UPDATE/DELETE operations via WhoDB UI
- Query syntax is Elasticsearch-specific, not SQL
- Aggregations limited to built-in capabilities
- Document editing requires direct API calls

### ClickHouse

A columnar database management system for analytical queries and real-time data.

**Version Support:**
- ClickHouse 21.8 through 24.1+
- Cloud deployments supported

| Feature | Support | Notes |
|---------|---------|-------|
| Table browsing | ✓ | List all tables |
| Column exploration | ✓ | View column structures |
| Query execution | ✓ | Run SQL-like queries |
| Data viewing | ✓ | Display query results |
| Export (CSV, JSON) | ✓ | Export analytical results |
| Mock data generation | ✓ | Generate test datasets |
| Aggregations | ✓ | Fast analytical queries |
| Dictionary support | ✓ | Access dictionary tables |

**Recommended Connection Settings:**
- Use appropriate timeout for analytical queries
- Set memory limits for large result sets
- Use HTTP protocol for web deployments

**Limitations:**
- No INSERT/UPDATE/DELETE through WhoDB interface
- Optimized for analytical workloads, not transactional
- Some advanced data types have limited support
- Real-time changes not visible without refresh

## Enterprise Edition Databases

Enterprise Edition extends support to commercial and specialized databases.

### Oracle Database

Enterprise-grade relational database system with advanced features.

**Version Support:**
- Oracle 19c, 21c
- Oracle 23c (latest)
- Cloud databases supported (Oracle Cloud Infrastructure)

| Feature | Support | Notes |
|---------|---------|-------|
| Connection | ✓ | TNS and direct connection |
| Schema navigation | ✓ | Multiple schemas per database |
| All RDBMS features | ✓ | Full SQL support |
| Stored procedures | ✓ | PL/SQL execution |
| Partitioning | ✓ | Query partitioned tables |
| Advanced features | ✓ | Advanced security, compression |
| Export | ✓ | All formats supported |

### Microsoft SQL Server

Microsoft's enterprise relational database system.

**Version Support:**
- SQL Server 2017 through 2022
- Azure SQL Database supported

| Feature | Support | Notes |
|---------|---------|-------|
| Connection | ✓ | Named pipes, TCP/IP |
| Schema navigation | ✓ | Multiple schemas |
| T-SQL execution | ✓ | Full T-SQL support |
| Stored procedures | ✓ | Execute stored procedures |
| Views | ✓ | Indexed views supported |
| Export | ✓ | All formats supported |

### Amazon DynamoDB

NoSQL database service with seamless scalability.

**Version Support:**
- Latest API version
- AWS regions worldwide

| Feature | Support | Notes |
|---------|---------|-------|
| Table exploration | ✓ | Browse tables |
| Item viewing | ✓ | Display items |
| Query execution | ✓ | Scan and query operations |
| Export (JSON) | ✓ | Export items as JSON |

### Amazon Athena

Interactive query service for data in Amazon S3.

**Version Support:**
- Presto/Trino compatibility
- Latest engine versions

| Feature | Support | Notes |
|---------|---------|-------|
| Query execution | ✓ | SQL queries on S3 data |
| Result viewing | ✓ | Display query results |
| Export | ✓ | Export results |

### Snowflake

Cloud data warehouse platform optimized for analytics.

**Version Support:**
- All current Snowflake versions
- All cloud regions supported

| Feature | Support | Notes |
|---------|---------|-------|
| Connection | ✓ | OAuth, username/password |
| Schema navigation | ✓ | Multiple databases and schemas |
| Query execution | ✓ | Full SQL support |
| Export | ✓ | All formats supported |

## Feature Comparison Matrix

| Feature | PostgreSQL | MySQL | SQLite | MongoDB | Redis | Elasticsearch | ClickHouse | Oracle | SQL Server | Snowflake |
|---------|-----------|-------|--------|---------|-------|---------------|-----------|--------|-----------|-----------|
| Insert/Update/Delete | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | ✓ |
| Transactions | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | ✓ | ✓ |
| Foreign Keys | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ | ✓ | ✓ | ✓ | ✓ |
| Views | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ | ✓ | ✓ | ✓ | ✓ |
| Stored Procedures | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✓ | ✓ | ✓ | ✓ |
| Full-Text Search | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ |
| JSON Support | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Export (SQL) | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✓ | ✓ | ✓ |
| Mock Data Generation | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Graph Visualization | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | ✓ | ✓ |

## Connection Requirements

Required information:
- Host address (IP or hostname)
- Port (default: 5432)
- Database name
- Username
- Password

Optional:
- SSL mode (disable, allow, prefer, require)
- SSH tunnel configuration
- Connection pooling settings

Required information:
- Host address
- Port (default: 3306)
- Username
- Password

Optional:
- Database name (can select after connection)
- SSL settings
- SSH tunnel configuration

Required information:
- File path to .db file

Optional:
- Use in-memory databases for testing

Required information:
- Connection string (mongodb://...) or
- Host, port, username, password

Optional:
- Replica set configuration
- Authentication database
- SSL/TLS settings

Required information:
- Host address
- Port (default: 6379)

Optional:
- Password
- Database number
- SSL settings
- Cluster mode

Required information:
- Host address (usually localhost or domain)
- Port (default: 9200)

Optional:
- Username and password
- SSL settings
- API key authentication

Required information:
- Host address
- Port (HTTP: 8123, TCP: 9000)
- Database name (optional)

Optional:
- Username and password
- SSL settings

## Version Upgrade Guide

When upgrading your database to a newer version:

Verify your new database version is listed in this documentation

Connect with WhoDB to ensure compatibility

Confirm all features you use are still available

Ensure you're running the latest WhoDB version for best compatibility

## Known Limitations by Database

- Some advanced data types require custom serialization
- Materialized views have limited refresh capabilities
- Connection through SSH requires proper key setup

- Unsigned integers displayed as regular integers in some contexts
- JSON path expressions more limited than PostgreSQL
- MyISAM tables don't support transactions

- Single writer at a time (concurrent writes limited)
- No true user/permission system
- Limited optimization for datasets over 1GB
- No native encryption

- Traditional SQL WHERE conditions not applicable
- Mixed schema collections require column inference
- No export to SQL format
- No transaction support like RDBMS

- Not suitable for complex relational data
- Limited aggregation capabilities
- Requires refresh to see real-time updates
- No SQL query support

- Read-only access only (no INSERT/UPDATE/DELETE)
- Elasticsearch-specific query syntax required
- Not designed for transactional operations

- Read-only for analytical queries only
- Designed for analytical workloads, not transactions
- Limited support for some advanced data types

## Recommended Database Versions

For optimal WhoDB experience and security:

| Database | Recommended Version | Minimum Version |
|----------|-------------------|-----------------|
| PostgreSQL | 14+ | 9.6 |
| MySQL | 8.0+ | 5.7 |
| SQLite | 3.40+ | 3.8 |
| MongoDB | 6.0+ | 3.6 |
| Redis | 7.0+ | 4.0 |
| Elasticsearch | 8.0+ | 7.10 |
| ClickHouse | 23.0+ | 21.8 |

## Migration Between Databases

WhoDB's export capabilities make it easy to migrate data between database types:

Access the database you're exporting from

Export table data in JSON or CSV format

Switch to your destination database

Set up corresponding table structures

Use database-specific import tools to load exported data

## Getting Help with Database Issues

Learn connection troubleshooting

See common questions answered

Report compatibility issues

Ask the community

This page is kept up-to-date with the latest database versions and WhoDB compatibility information. Check back periodically for updates.

---


# resources/changelog.mdx
## WhoDB Changelog

Track WhoDB updates, new features, improvements, and bug fixes with our comprehensive changelog

# WhoDB Changelog

Stay informed about the latest features, improvements, and bug fixes in WhoDB. This changelog documents all significant updates across Community Edition (CE) and Enterprise Edition (EE).

Subscribe to GitHub releases for instant notifications of new WhoDB versions

## v1.5.0 (Latest Release)

**Release Date:** Q4 2024

### New Features

- **AI Chat Enhancements**: Improved schema awareness and multi-table query generation
- **Keyboard Shortcut Customization**: Configure custom keyboard shortcuts in settings
- **Advanced Export Options**: New export presets and quick-export templates
- **Table Comparison Tool**: Compare schema and data between similar tables
- **Saved Queries Feature**: Save frequently used queries in organized collections
- **Dark Mode Enhancement**: Improved visual hierarchy and contrast ratios

### Improvements

- Performance optimization for large dataset pagination (50% faster)
- Enhanced Scratchpad multi-cell workflow with better cell organization
- Improved Graph visualization for schemas with 100+ tables
- Better error messages with actionable solutions
- Faster database connection establishment
- Reduced memory footprint on frontend by 30%
- Improved accessibility with better keyboard navigation
- Enhanced search functionality with fuzzy matching

### Bug Fixes

- Fixed data grid column header overflow issues
- Resolved Scratchpad query result caching problems
- Fixed Graph view zoom level persistence
- Corrected WHERE condition operator behavior for NULL values
- Fixed export encoding issues with special characters
- Resolved connection profile import errors
- Fixed sidebar collapse state not persisting
- Corrected mock data generation for specific PostgreSQL types

### Database Support Updates

- **PostgreSQL**: Added support for v16 with improved JSON functions
- **MongoDB**: Enhanced document preview with better formatting
- **ClickHouse**: Added support for dictionary tables and functions
- **Elasticsearch**: Improved index mapping visualization

### Known Issues

- EE: Oracle Database connection pooling requires manual configuration
- Large Excel exports (10k+ rows) may take longer than CSV
- Graph visualization performance on schemas with 500+ tables

---

## v1.4.5

**Release Date:** Q3 2024

### New Features

- **SSH Tunnel Connection Support**: Securely connect to databases through bastion hosts
- **Export to Excel (XLSX)**: Native Excel format with formatting support
- **Connection Profiles**: Save and manage multiple database connections
- **Quick Copy**: One-click copy of table data to clipboard

### Improvements

- Scratchpad syntax highlighting now supports 15+ database dialects
- Reduced initial load time by 40%
- Better handling of very large result sets (100k+ rows)
- Improved WHERE condition builder UI
- Enhanced error recovery and retry logic

### Bug Fixes

- Fixed date format inconsistencies in exports
- Corrected Scratchpad cell ordering issues
- Fixed PostgreSQL array type display
- Resolved MySQL timezone handling
- Fixed sidebar scrolling performance

### Database Support Updates

- **MySQL**: Added support for v8.3
- **MariaDB**: Added support for v11.0

---

## v1.4.0

**Release Date:** Q2 2024

### New Features

- **AI Chat Integration**: Natural language database querying with multiple LLM providers
  - Ollama support (local, private)
  - OpenAI integration
  - Anthropic Claude support
- **Mock Data Generation**: Generate realistic test data with customizable options
- **Global Search**: Search across all databases and tables
- **Query History**: Automatic saving and searching of executed queries
- **Multiple Scratchpad Cells**: Organize related queries in notebooks

### Improvements

- Graph visualization now handles bidirectional relationships better
- Enhanced schema explorer with search functionality
- Improved connection error messages
- Better support for large schemas (500+ tables)
- Faster data grid rendering with virtualization
- Enhanced keyboard navigation throughout app

### Bug Fixes

- Fixed Graph view rendering on Safari
- Corrected WHERE clause generation for LIKE operators
- Fixed inline cell editing with special characters
- Resolved connection timeout issues
- Fixed pagination state on data grid

### Database Support Updates

- **Redis**: Added support for Redis 7.0+
- **Elasticsearch**: Added support for v8.0+

---

## v1.3.0

**Release Date:** Q1 2024

### New Features

- **Graph Visualization**: Interactive schema relationship diagrams
  - Pan and zoom controls
  - Node details on click
  - Relationship cardinality indicators
- **Advanced WHERE Conditions**: Multi-condition filter builder
- **Data Export (CSV, JSON, SQL)**: Multiple export formats with customization
- **Inline Row Editing**: Quick cell edit with double-click
- **Table Search**: Search across table columns with highlighting

### Improvements

- Sidebar now shows table record counts
- Enhanced column header information tooltips
- Better handling of NULL values in data grid
- Improved mobile responsiveness
- Faster schema loading for databases with 200+ tables

### Bug Fixes

- Fixed data grid sorting on columns with NULL values
- Corrected foreign key relationship detection
- Fixed export file naming with special characters
- Resolved page size dropdown issues
- Fixed schema caching for schema changes

---

## v1.2.0

**Release Date:** Q4 2023

### New Features

- **Scratchpad Query Editor**: Multi-cell notebook-style query interface
  - Syntax highlighting
  - Query history
  - Result visualization
- **Explore View**: Detailed table structure information
  - Column definitions
  - Indexes and keys
  - Constraints display
- **Where Condition Builder**: GUI for building filter conditions
- **Column Sorting**: Multi-column sort support with visual indicators
- **Pagination**: Configurable page sizes (10, 25, 50, 100)

### Improvements

- Significantly improved query execution performance
- Better handling of very wide tables (50+ columns)
- Enhanced error messages with helpful suggestions
- Improved connection stability with reconnection logic

### Bug Fixes

- Fixed sidebar tree node expansion persistence
- Corrected data types display for edge cases
- Fixed connection credential storage security
- Resolved data grid horizontal scrolling issues

---

## v1.1.0

**Release Date:** Q3 2023

### New Features

- **Schema Explorer**: Hierarchical view of database structure
  - Databases and schemas
  - Tables, views, and stored procedures
  - Tree-based navigation
- **Add/Edit/Delete Rows**: Basic data manipulation interface
- **SSL/TLS Support**: Secure database connections
- **Connection Pooling**: Configurable connection pool settings

### Improvements

- Faster database connection establishment
- Better handling of special characters in data
- Improved UI responsiveness
- Better error handling and user feedback

### Bug Fixes

- Fixed connection timeout handling
- Corrected data type detection
- Fixed data grid refresh issues
- Resolved theme switching bugs

---

## v1.0.0 (Initial Release)

**Release Date:** Q2 2023

### Features

- **Multi-Database Support**: Connect to PostgreSQL, MySQL, SQLite, MongoDB
- **Data Grid View**: View and navigate table data
- **Sidebar Navigation**: Browse database structure
- **Connection Management**: Save and switch between database connections
- **Browser-Based Interface**: No installation required (with Docker)
- **Lightweight Design**: Minimal resource usage

### Database Support

- PostgreSQL 9.6+
- MySQL 5.7+
- SQLite 3.8+
- MongoDB 3.6+

### Known Limitations (Fixed in Later Versions)

- Limited export capabilities (JSON only)
- No query editor (added in v1.2.0)
- No schema visualization (added in v1.3.0)
- Limited keyboard shortcuts (expanded in v1.4.0+)

---

## Version Comparison

| Feature | v1.0 | v1.1 | v1.2 | v1.3 | v1.4 | v1.5 |
|---------|------|------|------|------|------|------|
| Multi-Database Support | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Data Grid | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Scratchpad Editor | ✗ | ✗ | ✓ | ✓ | ✓ | ✓ |
| Graph Visualization | ✗ | ✗ | ✗ | ✓ | ✓ | ✓ |
| AI Chat | ✗ | ✗ | ✗ | ✗ | ✓ | ✓ |
| Mock Data | ✗ | ✗ | ✗ | ✗ | ✓ | ✓ |
| Export (Multiple Formats) | ✗ | ✗ | ✗ | ✓ | ✓ | ✓ |
| Query History | ✗ | ✗ | ✗ | ✗ | ✓ | ✓ |
| Custom Keyboard Shortcuts | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ |

## Upcoming Features (Roadmap)

- Schema version control and migration tracking
- Collaborative workspace with real-time sync
- Advanced query optimization suggestions
- Enhanced mobile app with offline support
- Integration with popular data tools (Jupyter, Tableau)

- Query performance analysis and profiling
- Automated schema documentation generation
- Data masking for sensitive information
- API endpoint generation from queries
- Team management and role-based access

- GraphQL query builder (visual)
- Data lineage tracking
- Integration with dbt for data transformation
- Workflow automation and scheduling
- Multi-tenant support
- Advanced caching strategies

## Release Process

WhoDB follows semantic versioning (MAJOR.MINOR.PATCH):

- **Major Version**: Significant features, breaking changes, or major improvements
- **Minor Version**: New features and improvements without breaking changes
- **Patch Version**: Bug fixes and security updates

## Getting Latest Updates

Download latest version and pre-releases

Learn how to install the latest WhoDB

Discuss upcoming features with community

Report bugs or request features

## Breaking Changes History

- No breaking changes
- Full backward compatibility maintained

- Query history storage format changed
- Old history entries may need to be re-saved
- No API breaking changes

- Keyboard shortcuts restructured
- Custom shortcuts will be reset to defaults
- No database compatibility changes

- ChatDB/Ollama parameter format updated
- Old configurations will auto-migrate
- Connection profile format enhanced but backward compatible

- No breaking changes
- Full backward compatibility with v1.4 profiles
- All features remain compatible

## Migration Guide Between Versions

Export your connection profiles and settings

Check if your version has any breaking changes (see above)

Follow the installation guide for your platform

Test database connections after upgrade

Review and re-configure settings if needed

## Support Matrix

| Version | Release Date | Support End | LTS |
|---------|-------------|-------------|-----|
| v1.0.x | Q2 2023 | Q1 2024 | ✗ |
| v1.1.x | Q3 2023 | Q2 2024 | ✗ |
| v1.2.x | Q4 2023 | Q3 2024 | ✓ |
| v1.3.x | Q1 2024 | Q4 2024 | ✗ |
| v1.4.x | Q2 2024 | Q2 2025 | ✓ |
| v1.5.x | Q4 2024 | Q4 2025 | ✓ |

**LTS (Long Term Support)** versions receive security updates and critical bug fixes for extended periods.

## Feedback & Feature Requests

Have suggestions for new features or improvements? We'd love to hear from you:

Join the community discussion

Propose new features

Report issues you encounter

Contact support team

This changelog is updated with each WhoDB release. Check back regularly for the latest news and improvements.

---