# Update Diagram
Source: https://docs.chartdb.io/docs/api/diagram/update-diagram
POST /api/update_diagram/{diagram_id}
Update an existing database diagram
The Update Diagram endpoint allows you to modify an existing database diagram's properties.
## Authorizations
Bearer authentication header of the form `Bearer `, where `` is your auth token from ChartDB starts with `ch_`.
## Path Parameters
The unique identifier of the diagram.
## Request Body
New name for the diagram.
Updated diagram settings in JSON format. (See
[Examples](/docs/api/examples/postgresql) for more information)
```bash cURL theme={null}
curl -X POST https://api.chartdb.io/api/update_diagram/123456789 \
-H "Authorization: Bearer ch_123456789" \
-H "Content-Type: application/json" \
-d '{
"diagram_name": "Updated E-commerce Diagram",
"json_metadata": {
"fk_info": [],
"pk_info": [],
"columns": [],
"indexes": [],
"tables": [],
"views": [],
"database_name": "postgres"
}
}'
```
```json Response theme={null}
{
"status": "success",
"message": "Diagram updated successfully"
}
```
# Embeded Diagrams
Source: https://docs.chartdb.io/docs/api/embeded-diagrams
API endpoints for managing database diagrams
## Private Access with API Keys
You can access private diagrams without authentication by using an API key in the diagram's URL. This is particularly useful for:
* Embedding private diagrams in documentation
* Sharing diagrams with specific users without requiring them to log in
* Integrating diagrams in internal tools
### Getting the Embedded Link
1. Go to your API Keys settings in ChartDB
2. Find the API key associated with your diagram
3. Click the three dots menu (⋮) and select "Copy Embedded Link"
### Usage
Simply append your API key as a query parameter to the diagram URL:
```
https://app.chartdb.io/diagram/{diagram_id}?api_key={your_api_key}
```
Example:
```
https://app.chartdb.io/diagram/9df7f9d1e30c442c94bd04?api_key=ch_529b512d_4bcd79e15f8ef1b1019f5317
```
### Benefits
* **No Authentication Required**: Users can view private diagrams directly through the URL
* **Granular Access Control**: Each API key can be scoped to specific diagrams
* **Secure Sharing**: Share diagrams privately without exposing them publicly
* **Easy Integration**: Perfect for embedding in documentation, wikis, or internal tools
### Security Considerations
* Keep your API keys secure and rotate them periodically
* Each API key should only have access to the diagrams it needs
* Monitor API key usage through your ChartDB dashboard
# Import Cloudflare D1
Source: https://docs.chartdb.io/docs/api/examples/cloudflare-d1
Import your Cloudflare D1 database schema into ChartDB
## Download Import Script
Download our Cloudflare D1 import script:
```bash theme={null}
curl -O https://app.chartdb.io/bash-scripts/cloudflare-d1-import.sh
chmod +x cloudflare-d1-import.sh
```
## Usage
Run the script with your Cloudflare D1 database name and ChartDB information:
```bash theme={null}
./cloudflare-d1-import.sh \
-d your-database-name \
-a your-chartdb-api-key \
-t your-diagram-id
```
For remote databases, add the `-r` flag:
```bash theme={null}
./cloudflare-d1-import.sh \
-d your-database-name \
-a your-chartdb-api-key \
-t your-diagram-id \
-r
```
### Required Parameters
* `-d` Cloudflare D1 database name
* `-a` ChartDB API key
* `-t` Target diagram ID
### Optional Parameters
* `-r` Use remote database (if not specified, uses local database)
The script will connect to your Cloudflare D1 database, extract the schema, and automatically import it into your ChartDB diagram.
## Automated Daily Updates
To automatically update your diagram daily using cron, add the following line to your crontab:
```bash theme={null}
# Run at 2 AM every day
0 2 * * * /path/to/cloudflare-d1-import.sh -d database_name -a api_key -t diagram_id >> /path/to/d1-import.log 2>&1
```
To edit your crontab:
```bash theme={null}
crontab -e
```
**Note:** Replace `/path/to/cloudflare-d1-import.sh` with the absolute path to
the script and adjust the database name accordingly. The log file path
(`/path/to/d1-import.log`) should be in a directory where your user has write
permissions.
# Import MySQL
Source: https://docs.chartdb.io/docs/api/examples/mysql
Import your MySQL database schema into ChartDB
## Download Import Script
Download our MySQL import script:
```bash theme={null}
curl -O https://app.chartdb.io/bash-scripts/mysql-import.sh
chmod +x mysql-import.sh
```
## Usage
Run the script with your database credentials and ChartDB information:
```bash theme={null}
./mysql-import.sh \
-h your-database-host \
-P 3306 \
-u your-database-user \
-p your-database-password \
-d your-database-name \
-a your-chartdb-api-key \
-t your-diagram-id
```
### Required Parameters
* `-h` Database hostname
* `-P` Database port (default: 3306)
* `-u` Database username
* `-p` Database password
* `-d` Database name
* `-a` ChartDB API key
* `-t` Target diagram ID
The script will connect to your database, extract the schema, and automatically import it into your ChartDB diagram.
## Automated Daily Updates
To automatically update your diagram daily using cron, add the following line to your crontab:
```bash theme={null}
# Run at 2 AM every day
0 2 * * * /path/to/mysql-import.sh -h host -P 3306 -u user -p pass -d dbname -a api_key -t diagram_id >> /path/to/mysql-import.log 2>&1
```
To edit your crontab:
```bash theme={null}
crontab -e
```
**Note:** Replace `/path/to/mysql-import.sh` with the absolute path to the
script and adjust the credentials accordingly. The log file path
(`/path/to/mysql-import.log`) should be in a directory where your user has
write permissions.
# Import PostgreSQL
Source: https://docs.chartdb.io/docs/api/examples/postgresql
Import your PostgreSQL database schema into ChartDB using a single query
## Download Import Script
Download our PostgreSQL import script:
```bash theme={null}
curl -O https://app.chartdb.io/bash-scripts/psql-import.sh
chmod +x psql-import.sh
```
## Usage
Run the script with your database credentials and ChartDB information:
```bash theme={null}
./psql-import.sh \
-h your-database-host \
-p 5432 \
-U your-database-user \
-d your-database-name \
-W your-database-password \
-a your-chartdb-api-key \
-t your-diagram-id
```
### Required Parameters
* `-h` Database hostname
* `-p` Database port (default: 5432)
* `-U` Database username
* `-d` Database name
* `-W` Database password
* `-a` ChartDB API key
* `-t` Target diagram ID
The script will connect to your database, extract the schema, and automatically import it into your ChartDB diagram.
## Automated Daily Updates
To automatically update your diagram daily using cron, add the following line to your crontab:
```bash theme={null}
# Run at 2 AM every day
0 2 * * * /path/to/psql-import.sh -h host -p 5432 -U user -W pass -d dbname -a api_key -t diagram_id >> /path/to/psql-import.log 2>&1
```
To edit your crontab:
```bash theme={null}
crontab -e
```
**Note:** Replace `/path/to/psql-import.sh` with the absolute path to the
script and adjust the credentials accordingly. The log file path
(`/path/to/psql-import.log`) should be in a directory where your user has
write permissions.
# Import SQLite
Source: https://docs.chartdb.io/docs/api/examples/sqlite
Import your SQLite database schema into ChartDB
## Download Import Script
Download our SQLite import script:
```bash theme={null}
curl -O https://app.chartdb.io/bash-scripts/sqlite-import.sh
chmod +x sqlite-import.sh
```
## Usage
Run the script with your database file path and ChartDB information:
```bash theme={null}
./sqlite-import.sh \
-f your-database-file.sqlite \
-a your-chartdb-api-key \
-t your-diagram-id
```
### Required Parameters
* `-f` SQLite database file path
* `-a` ChartDB API key
* `-t` Target diagram ID
The script will connect to your database file, extract the schema, and automatically import it into your ChartDB diagram.
## Automated Daily Updates
To automatically update your diagram daily using cron, add the following line to your crontab:
```bash theme={null}
# Run at 2 AM every day
0 2 * * * /path/to/sqlite-import.sh -f /path/to/database.sqlite -a api_key -t diagram_id >> /path/to/sqlite-import.log 2>&1
```
To edit your crontab:
```bash theme={null}
crontab -e
```
**Note:** Replace `/path/to/sqlite-import.sh` with the absolute path to the
script and adjust the file path accordingly. The log file path
(`/path/to/sqlite-import.log`) should be in a directory where your user has
write permissions.
# ChartDB API – Programmatically Manage & Auto‑Update Diagrams
Source: https://docs.chartdb.io/docs/api/introduction
Use the ChartDB API to automate ER‑diagram creation and updates. Authenticated via API key, JSON‑formatted, ideal for CI/CD workflows and internal tools.
## Introduction
The ChartDB API enables programmatic access to manage and auto-update your database diagrams.
It supports authenticated requests using API keys, follows standard HTTP methods and response codes,
and accepts JSON-formatted request and response bodies. Use the API to sync your schema changes,
automate diagram generation, and integrate ChartDB into your CI/CD workflows or internal tools.
## Base URL
All API requests should be made to the following base URL:
```bash theme={null}
https://api.chartdb.io/api
```
## Authentication
To authenticate your requests, include an API key in the Authorization header:
```bash theme={null}
Authorization: Bearer ch_123456789
```
Your API key can be found in your ChartDB dashboard settings. Keep it secure and never share it publicly.
## Response Format
All responses are returned in JSON format. Successful responses will include `status` & `message` fields:
```json theme={null}
{
"status": "success",
"message": "Diagram updated successfully"
}
```
Error responses will include `status` & `message` fields:
```json theme={null}
{
"status": "error",
"message": "Invalid API key"
}
```
## Request Format
For POST and PUT requests, send data in JSON format with the appropriate `Content-Type` header:
```bash theme={null}
Content-Type: application/json
```
## API Versioning
The current version is `api`. We include versioning in the URL path:
```bash theme={null}
https://api.chartdb.ioo/api/update_diagram
```
Future versions will be announced with appropriate migration guides and deprecation notices.
# Cloud vs. Self-Hosted
Source: https://docs.chartdb.io/docs/cloud-vs-self-hosted
Understand the differences between ChartDB Cloud and Self-Hosted options
ChartDB is an open-source tool that offers flexible deployment options to suit your workflow and security requirements. Whether you choose the convenience of ChartDB Cloud or the control of self-hosting ChartDB, you gain powerful database visualization features.
Visualize databases instantly in your browser. Best for ease of use and team
accessibility.
Maintain complete control over your data and setup. Ideal for security and
customization.
## Feature Comparison
| Feature | ChartDB Cloud | ChartDB Self-Hosted |
| ------------------- | -------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
| **Deployment** | Hosted by ChartDB ([app.chartdb.io](https://app.chartdb.io)) | Host it yourself (Docker, npm) |
| **Data Control** | Private cloud diagrams | Full control over your data and server |
| **Collaboration** | Team features (private diagrams, user sharing, versioning) | Share diagrams manually via export/import |
| **User Management** | 1 user (Free/Pro), 3-25+ users (Teams plans) | No built-in user accounts |
| **Support** | Community Discord (Free), Email support (Pro), Priority support (Teams+) | Community |
| **AI Features** | AI Assistant included in Teams plans; optional with OpenAI API key on other plans | Optional, OpenAI API key required |
| **Privacy** | Secure cloud storage | Host within your secure environment |
| **Customization** | Basic UI settings within the app | Modify source code for deep customization |
| **Automated Sync** | Programmatically sync database changes to diagrams via API (no credentials required) | N/A |
| **Embedding** | Embed interactive diagrams in documentation, websites, or applications | N/A |
| **Table Limits** | 10 tables (Free), up to 100 (Pro), up to 200 (Teams) [Pricing →](https://chartdb.io/pricing) | No table limits - visualize as many tables as you need |
| **Cost** | Free tier available; paid plans for Pro and Teams [Pricing →](https://chartdb.io/pricing) | Free (open-source), infrastructure costs for self-hosting |
Some features like embedding, automated sync, and team collaboration are exclusive to ChartDB Cloud. Supporting these features locally comes with a lot of additional complexity, syncing, and maintaining a consistent experience across different environments. For now, we've prioritized making the cloud version as seamless as possible so we can move fast, iterate, and offer the best experience.
## Making the Right Choice
**Choose ChartDB Cloud if:**
* You need a quick and easy way to visualize databases.
* You prefer a managed solution without server setup.
* Team collaboration and private diagrams are important.
* You want to get started right away with minimal hassle.
* [Learn more about pricing →](https://chartdb.io/pricing)
**Choose ChartDB Self-Hosted if:**
* You need unlimited tables with no caps or restrictions.
* Data security and compliance are critical requirements.
* You require complete control over your data and infrastructure.
* Offline access is necessary.
* You need extensive customization or integration capabilities.
* Note: Cloud features like team collaboration and private diagrams are not directly available in the self-hosted version.
Start with ChartDB Cloud to experience its benefits immediately with team
collaboration and private diagrams, or consider self-hosting for full control.
[Learn more about pricing →](https://chartdb.io/pricing)
# Visualize Dependencies
Source: https://docs.chartdb.io/docs/diagrams/dependencies
Explore and understand database relationships between Views and Tables in ChartDB
In ChartDB, dependencies focus on database Views and their underlying Tables.
ChartDB automatically detects and displays dependencies based on the imported
database schema, specifically from View definitions. You cannot manually
create or edit dependencies directly within ChartDB at this time.
## View Dependencies
ChartDB provides a dedicated "Dependencies" section in the sidebar to easily explore these relationships.
Navigate to the **Dependencies** section in the ChartDB sidebar.
Select a View from the Dependencies list. ChartDB will highlight the selected View and the Tables it depends on directly on your database diagram canvas.
# Editing Layouts, Modifying Colors, and Filtering Tables
Source: https://docs.chartdb.io/docs/diagrams/layouts-colors-filters
Organize diagram layouts, apply visual styling, and implement filtering to create create and insightful database diagrams.
Automatically reorganize your diagram for optimal readability.
Align tables precisely to a grid for a cleaner diagram.
Instantly identify and resolve table overlaps.
Navigate your diagram with zoom in, zoom out, show all, and 100% zoom options.
Show or hide the sidebar to maximize your canvas workspace.
Customize table colors to visually group and highlight elements in your diagram.
Filter the table list in the sidebar to easily find specific tables.
## Reorder Diagram
The **Reorder Diagram** button helps you automatically arrange tables in your diagram based on their relationships, optimizing for visibility.
Use this feature to quickly structure your diagram after adding or moving tables.
Click the **Reorder Diagram** button in the top-left toolbar of the canvas.
In the confirmation dialog, click **Reorder** to proceed. ChartDB will intelligently rearrange your tables.
## Snap to Grid
The **Snap to Grid** feature ensures tables align to a grid, creating a more organized and visually consistent diagram. Click the **Snap to Grid** button in the toolbar to enable or disable grid snapping. When enabled, the button is highlighted, and dragging tables will automatically snap them to the grid. To temporarily snap tables to the grid while the feature is disabled, hold down the `Shift` key while dragging.
## Overlap Highlighting
**Overlap Highlighting** helps you identify tables that are overlapping in your diagram. The button will automatically appear when at least two tables are overlapping. Clicking the **Overlap Highlighting** button will navigate you to the area of the diagram where the overlap occurs, allowing you to manually resolve the overlap or use the **Reorder Diagram** feature.
## Zoom Controls
ChartDB provides intuitive zoom controls at the bottom center of the canvas to navigate your diagram effectively.
Click the **Show All** button to fit the entire diagram within the viewport.
**Shortcut:** Use **`Command + 0`**.
Use the **Zoom In** and **Zoom Out** buttons for incremental zoom adjustments.
Click the zoom level indicator (e.g., `66%`) to reset the zoom level to 100%.
## Sidebar Visibility
Maximize your canvas workspace by showing or hiding the sidebar as needed.
1. Click **View** in the top menu bar.
2. Select **Hide Sidebar** or **Show Sidebar** to toggle visibility.
Use the shortcut **`Command + B`** to quickly toggle the sidebar's visibility.
## Table Colors
Customize table colors to visually organize and highlight elements within your database diagram.
Click on the table on the canvas or select it from the sidebar.
In the sidebar, within the table details, locate and open the color picker control.
Select your desired color from the color picker. The table color will update in real-time.
Use color to visually group related tables or highlight key entities for better diagram comprehension.
## Sidebar Filters
Filter the table list in the sidebar to quickly locate and manage specific tables, especially in large database schemas.
Filtering in ChartDB currently only affects the table list in the sidebar. It does not filter tables directly on the canvas diagram.
Click into the **Filter tables** input field at the top of the sidebar. Shortcut: **Command + F**
Type your filter term into the input field. The sidebar table list will dynamically update to show matching tables. **Delete the text in the filter input to clear the filter** and show all tables again.
# Creating Relationships with Foreign Keys
Source: https://docs.chartdb.io/docs/diagrams/relationships
Learn how to create database relationships in ChartDB using foreign keys.
In ChartDB, relationships between tables are established through **foreign keys**.
This means a column in one table (the **primary table**) points to a column in another table (the **referencing table**), linking records that belong together.
For a relationship to be valid, the [field
types](/diagrams/tables-fields#data-type) of the columns you choose in both the
primary and referencing tables **must match**.
## Create Relationships
ChartDB offers three user-friendly ways to create relationships between your database tables:
This is the most straightforward and visually intuitive method for creating relationships.
To create a relationship, simply click and drag from a connection dot on your primary table to a column in your target table.
ChartDB will highlight compatible columns as you drag.
This method provides an alternative, especially useful when tables are positioned far apart on the canvas, making drag and drop less convenient.
Right-click on any table and select "Create Relationship" from the menu.
In the sidebar that appears, select your primary and referencing tables, choose the columns to connect, and click Save to create the relationship.
ChartDB also provides a dedicated "Relationship" menu within its interface, offering a structured approach to relationship management.
Navigate to the "Relationship" menu and click "Add Relationship".
In the configuration sidebar, select your primary and referencing tables, choose the columns to connect, and click Save to create the relationship.
## View Relationship Cardinality
By default, cardinality indicators are not visible on the diagram. To display the cardinality of relationships:
* In the top menu bar of ChartDB, click on the **View** dropdown menu and click on the **Show Cardinality** option from the dropdown.
Once enabled, ChartDB will display cardinality indicators (e.g., 1, N) on the relationship lines in your diagram, visually representing the type of relationship (one-to-many, many-to-many, etc.).
## Modify Relationship Cardinality
ChartDB supports 4 relationship cardinalities, including **One-to-One**, **One-to-Many**, **Many-to-One**, and **Many-to-Many**.
To adjust the **cardinality** of a relationship:
1. **Select the Relationship:** Click on the relationship line connecting the two tables in your diagram. This will typically open the relationship details in the sidebar.
2. **Modify Cardinality:** In the sidebar, you will find a dropdown menu labeled "Cardinality" or similar. Use this menu to select the new cardinality for the relationship.
Once you've created a relationship, ChartDB immediately visualizes it on your diagram. You'll see a line connecting the related tables, clearly indicating the established link.
# Adding Tables and Fields
Source: https://docs.chartdb.io/docs/diagrams/tables-fields
Learn how to add and manage tables and fields in ChartDB to visualize your database schema effectively.
ChartDB provides a visual and intuitive way to add tables and fields to your database diagram.
## Add New Tables
To add new tables, you can use the sidebar or interact directly with the canvas.
First, **navigate to the *Tables* section** in the ChartDB sidebar and click the 'Add Table' icon.
This instantly creates a new table in your diagram with a randomly generated name and a single default field.
1. **Access table actions** by clicking the settings icon next to the table name in the sidebar.
2. **Select 'Duplicate Table'** from the table actions menu.
You can also quickly add or duplicate tables directly on the canvas using the right-click context menu.
1. **Right-click anywhere on the canvas.**
2. **Select 'New Table'** to add a brand new table, or **'Duplicate Table'** after selecting an existing table to clone it.
You can also **edit table names directly on the canvas** by double-clicking the table name. This provides a quick and intuitive way to rename tables as you are visualizing your diagram.
## Managing Schemas
In ChartDB, tables are organized within **Schemas**. Schemas act like folders, providing a way to structure and group your tables, especially in databases like PostgreSQL and MSSQL that support them.
Schemas will only be displayed in the sidebar if your imported database is configured with schemas.
By default, only the `public` schema is displayed when schemas are present in your database.
New tables added using the **Add Table** functionality will be placed within the currently active schema, which defaults to the `public` schema if not otherwise specified.
## Add New Fields
Whether you're refining your schema or adding new attributes, here’s how to add fields to your tables in ChartDB.
1. **Scroll to the bottom** of the specific table's details section in the
sidebar. 2. **Click the 'Add Field' button**.
1. **Expand the 'Fields' section** for the specific table in the sidebar.
2. **Click the '+' icon** next to the 'Fields' header.
Similar to tables, you can **edit field names directly on the canvas** by double-clicking the field name within a table. This allows for in-place editing of field names directly in your diagram.
This will instantly add a new field to your selected table with a default name (e.g., `field_4`) and a default data type.
## Configure Field Attributes
Once you've added a new field or need to modify an existing one, ChartDB provides configurable field attributes.
To rename a field, simply click on the current field name in the sidebar and type in the new desired name.
To change a field's data type, click the dropdown menu next to the current data type. This will display a list of available data types supported by your imported database.
Available data types are determined by the database you imported into ChartDB.
By default, new fields are often set as nullable. To toggle a field's
nullability, click the 'N' icon next to the field.
To designate a field as a primary key, click the 'P' icon next to the field.
Click the three dots icon next to the field to access additional attributes. Check the 'Unique' checkbox to enforce a unique constraint. Use the 'Comments' text area to add field-level comments.
Field-level comments are distinct from table-level comments, which can be added in the table's top-level settings.
# Export DBML
Source: https://docs.chartdb.io/docs/export/dbml
Export your diagram as a DBML script in ChartDB
The Export DBML feature requires an upgraded subscription on ChartDB Cloud. See our [Cloud vs Self-Hosted](/docs/cloud-vs-self-hosted) documentation for more details.
In the editor's side panel, locate the top of the tables list on the left
Click the **arrows button ("\<>")**
The **DBML** script representing your diagram will be displayed. You can copy it for use elsewhere.
# Export Image
Source: https://docs.chartdb.io/docs/export/image
Save your database diagrams as PNG, JPG, or SVG images in ChartDB.
Save your diagrams as high-quality images in PNG, JPG, and SVG formats. This is perfect for presentations, documentation, or sharing your database design with colleagues.
## How to Export
Navigate to the **File** menu in the top left corner, click **View all Options...**, and hover over **Export as**. A submenu will appear showing PNG, JPG, and SVG format options.
In the Export Image modal, you can select your preferred format (PNG, JPG, or SVG) and choose a scale factor (1x to 4x). Click **Export** to save your image.
The exported image is **based on the current view in your viewport**. Make
sure you have zoomed and panned your diagram to show the desired tables and
relationships before exporting.
## Customize Your Diagram
Want to fine-tune the appearance of your diagram before exporting? ChartDB offers powerful customization options for layouts, colors, and filters.
Learn how to customize your diagram's appearance to create visually appealing
exports.
# Export Diagram as JSON
Source: https://docs.chartdb.io/docs/export/json
Learn how to export your ChartDB diagram as a JSON file for safekeeping and future modifications.
Exporting your current diagram a JSON file is useful for creating backups of your work or saving a specific state.
You can later re-import and continue editing.
The Export Diagram (JSON) feature requires an upgraded subscription on ChartDB Cloud. See our [Cloud vs Self-Hosted](/docs/cloud-vs-self-hosted) documentation for more details.
Navigate to the **Share** menu located in the top navigation bar. From the
dropdown menu, select **Export Diagram**.
In the "Export Diagram" interface, you will see the option to export as
JSON. Click the **Export** button.
Your browser will prompt you to save a `.json` file. This file contains your
diagram's data in JSON format.
The JSON file exported using this feature is **specifically designed for
re-importing diagrams back into ChartDB**. This JSON format is **different**
from the JSON structure used when initially importing a database schema into
ChartDB via SQL scripts. Do not attempt to use exported diagram JSON files for
database schema import via SQL script methods.
## Next Steps
Now that you have successfully exported your diagram as a JSON file, save it in a secure and accessible location on your local machine. When you are ready to resume working on your diagram or wish to make further edits, you can easily import this JSON file back into ChartDB.
Learn how to import your exported JSON diagram back into ChartDB to continue
editing and visualizing your database.
# Export Diagram as SQL
Source: https://docs.chartdb.io/docs/export/sql
Generate SQL scripts from your ChartDB diagrams to quickly deploy your database schema.
The SQL scripts generated by ChartDB are **schema definitions based on your
diagram**. They are intended to help you create the structure in your
database. **These scripts are not meant for data migration or direct execution
on production databases without review.**
Navigate to the **File** menu in the top navigation bar and hover over **Export SQL**.
Select your preferred SQL dialect from the submenu:
- Generic SQL: Provides standard SQL compatible across most database systems.
- PostgreSQL, MySQL, SQL Server, MariaDB, SQLite: Generates SQL tailored to specific database systems.
PostgreSQL, MySQL, SQL Server, MariaDB, and SQLite exports are available **exclusively for signed-in users**. Generic SQL export is available for all users.
A modal window will appear displaying the generated SQL script in a text area. Click the **Copy** button to copy the script to your clipboard.
# Import ClickHouse
Source: https://docs.chartdb.io/docs/import/clickhouse
Import ClickHouse database schema into ChartDB
ChartDB never stores or accesses your data - the import query only retrieves schema metadata.
## Quick Start
1. Click **File** > **New** in the ChartDB editor
2. Select **ClickHouse** from the database options
Execute the provided query using either:
* Your database client (e.g., ClickHouse UI, DBeaver, etc..)
* Direct ClickHouse connection
1. Copy the JSON output from the query
2. Paste into ChartDB's import field
3. Click **Import** to generate your diagram
## Import Methods
```sql ClickHouse theme={null}
-- Regular ClickHouse Database Import Query
WITH
cols AS (
SELECT arrayStringConcat(arrayMap(col_tuple ->
concat('{"schema":"', col_tuple.1, '"',
',"table":"', col_tuple.2, '"',
',"name":"', col_tuple.3, '"',
',"ordinal_position":"', toString(col_tuple.4), '"',
',"type":"', col_tuple.5, '"',
',"nullable":"', if(col_tuple.6 = 'NULLABLE', 'true', 'false'), '"',
',"default":"', if(col_tuple.7 = '', 'null', col_tuple.7), '"',
',"comment":', if(col_tuple.8 = '', '""', toString(toJSONString(col_tuple.8))), '}'),
groupArray((
col.database,
col.table,
col.name,
col.position,
col.type,
col.default_kind,
col.default_expression,
col.comment
))
), ',') AS cols_metadata
FROM system.columns AS col
JOIN system.tables AS tbl
ON col.database = tbl.database AND col.table = tbl.name
WHERE lower(col.database) NOT IN ('system', 'information_schema')
AND lower(col.table) NOT LIKE '.inner_id.%'
AND tbl.is_temporary = 0 -- Exclude temporary tables if desired
),
tbl_sizes AS (
SELECT database, table, sum(bytes_on_disk) AS size
FROM system.parts
GROUP BY database, table
),
tbls AS (
SELECT arrayStringConcat(arrayMap(tbl_tuple ->
concat('{"schema":"', tbl_tuple.1, '"',
',"table":"', tbl_tuple.2, '"',
',"rows":', toString(tbl_tuple.3),
',"type":"', tbl_tuple.4, '"',
',"engine":"', tbl_tuple.5, '"',
',"collation":"",',
'"size":', toString(tbl_tuple.6), ',',
'"comment":', if(tbl_tuple.7 = '', '""', toString(toJSONString(tbl_tuple.7))), '}'),
groupArray((
tbl.database, -- tbl_tuple.1
tbl.name, -- tbl_tuple.2
tbl.total_rows, -- tbl_tuple.3
tbl.type, -- tbl_tuple.4
tbl.engine, -- tbl_tuple.5
coalesce(ts.size, 0), -- tbl_tuple.6
tbl.comment -- tbl_tuple.7
))
), ',') AS tbls_metadata
FROM (
SELECT
tbl.database,
tbl.name,
coalesce(tbl.total_rows, 0) as total_rows,
-- Determine the type based on the engine
if(tbl.engine = 'View', 'VIEW',
if(tbl.engine = 'MaterializedView', 'MATERIALIZED VIEW', 'TABLE')) AS type,
tbl.engine,
tbl.comment
FROM system.tables AS tbl
WHERE lower(tbl.database) NOT IN ('system', 'information_schema')
AND lower(tbl.name) NOT LIKE '.inner_id.%'
AND tbl.is_temporary = 0
) AS tbl
LEFT JOIN tbl_sizes AS ts
ON tbl.database = ts.database AND tbl.name = ts.table
-- GROUP BY tbl.database, tbl.name, tbl.total_rows, tbl.type, tbl.engine, tbl.comment, ts.size
),
indexes AS (
SELECT arrayStringConcat(arrayMap((db, tbl, name) ->
concat('{"schema":"', db, '"',
',"table":"', tbl, '"',
',"name":"', name, '"',
',"index_type":"",',
'"cardinality":"",',
'"size":"",',
'"unique":"false"}'),
groupArray((idx.database, idx.table, idx.name))
), ',') AS indexes_metadata
FROM system.data_skipping_indices AS idx
WHERE lower(idx.database) NOT IN ('system', 'information_schema')
AND lower(idx.table) NOT LIKE '.inner_id.%'
),
views AS (
SELECT arrayStringConcat(arrayMap((db, name, definition) ->
concat('{"schema":"', db, '"',
',"view_name":"', name, '"',
',"view_definition":"',
base64Encode(replaceAll(replaceAll(definition, '\\', '\\\\'), '"', '\\"')), '"}'),
groupArray((vw.database, vw.name, vw.create_table_query))
), ',') AS views_metadata
FROM system.tables AS vw
WHERE vw.engine in ('View', 'MaterializedView')
AND lower(vw.database) NOT IN ('system', 'information_schema')
),
pks AS (
SELECT
col.database AS schema_name,
col.table AS table_name,
groupArray(col.name) AS pk_columns,
concat('PRIMARY KEY(', arrayStringConcat(groupArray(col.name), ', '), ')') AS pk_def
FROM system.columns AS col
WHERE col.is_in_primary_key = 1
AND lower(col.database) NOT IN ('system', 'information_schema')
AND lower(col.table) NOT LIKE '.inner_id.%'
GROUP BY col.database, col.table
),
pks_metadata AS (
SELECT arrayStringConcat(arrayMap(pk_tuple ->
concat('{"schema":"', pk_tuple.1, '"',
',"table":"', pk_tuple.2, '"',
',"column":"', pk_tuple.3, '"',
',"pk_def":"', pk_tuple.4, '"}'),
groupArray((
pks.schema_name,
pks.table_name,
arrayJoin(pks.pk_columns),
pks.pk_def
))
), ',') AS pk_metadata
FROM pks
)
SELECT
concat('{
"fk_info": [],',
'"pk_info": [', COALESCE((SELECT pk_metadata FROM pks_metadata), ''), '],',
'"columns": [', COALESCE((SELECT cols_metadata FROM cols), ''),
'], "indexes": [', COALESCE((SELECT indexes_metadata FROM indexes), ''),
'], "tables":[', COALESCE((SELECT tbls_metadata FROM tbls), ''),
'], "views":[', COALESCE((SELECT views_metadata FROM views), ''),
'], "database_name": "', currentDatabase(), '", "version": "', version(), '"}'
) AS metadata_json_to_import;
```
## Troubleshooting
Find solutions for frequently encountered import problems and their resolutions
# Import CockroachDB
Source: https://docs.chartdb.io/docs/import/cockroachdb
Import CockroachDB database schema into ChartDB
ChartDB never stores or accesses your data - the import query only retrieves schema metadata.
## Quick Start
1. Click **File** > **New** in the ChartDB editor
2. Select **CockroachDB** from the database options
Execute the provided query using either:
* Your database client (e.g., CockroachDB UI, DBeaver, etc..)
* Direct CockroachDB connection
1. Copy the JSON output from the query
2. Paste into ChartDB's import field
3. Click **Import** to generate your diagram
## Import Methods
```sql CockroachDB theme={null}
-- Regular CockroachDB Database Import Query
WITH fk_info AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', replace(schema_name::TEXT, '"', ''), '"',
',"table":"', replace(table_name::TEXT, '"', ''), '"',
',"column":"', replace(fk_column::TEXT, '"', ''), '"',
',"foreign_key_name":"', foreign_key_name::TEXT, '"',
',"reference_schema":"', COALESCE(reference_schema::TEXT, 'public'), '"',
',"reference_table":"', reference_table::TEXT, '"',
',"reference_column":"', reference_column::TEXT, '"',
',"fk_def":"', replace(fk_def::TEXT, '"', ''),
'"}')), ',') as fk_metadata
FROM (
SELECT c.conname AS foreign_key_name,
n.nspname AS schema_name,
CASE
WHEN position('.' in conrelid::regclass::text) > 0
THEN split_part(conrelid::regclass::text, '.', 2)
ELSE conrelid::regclass::text
END AS table_name,
a.attname AS fk_column,
nr.nspname AS reference_schema,
CASE
WHEN position('.' in confrelid::regclass::text) > 0
THEN split_part(confrelid::regclass::text, '.', 2)
ELSE confrelid::regclass::text
END AS reference_table,
af.attname AS reference_column,
pg_get_constraintdef(c.oid) as fk_def
FROM
pg_constraint AS c
JOIN
pg_attribute AS a ON a.attnum = ANY(c.conkey) AND a.attrelid = c.conrelid
JOIN
pg_class AS cl ON cl.oid = c.conrelid
JOIN
pg_namespace AS n ON n.oid = cl.relnamespace
JOIN
pg_attribute AS af ON af.attnum = ANY(c.confkey) AND af.attrelid = c.confrelid
JOIN
pg_class AS clf ON clf.oid = c.confrelid
JOIN
pg_namespace AS nr ON nr.oid = clf.relnamespace
WHERE
c.contype = 'f'
AND connamespace::regnamespace::text NOT IN ('information_schema', 'pg_catalog')
AND connamespace::regnamespace::text NOT IN ('pg_extension', 'crdb_internal')
) AS x
), pk_info AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', replace(schema_name::TEXT, '"', ''), '"',
',"table":"', replace(pk_table::TEXT, '"', ''), '"',
',"column":"', replace(pk_column::TEXT, '"', ''), '"',
',"pk_def":"', replace(pk_def::TEXT, '"', ''),
'"}')), ',') AS pk_metadata
FROM (
SELECT connamespace::regnamespace::text AS schema_name,
CASE
WHEN strpos(conrelid::regclass::text, '.') > 0
THEN split_part(conrelid::regclass::text, '.', 2)
ELSE conrelid::regclass::text
END AS pk_table,
unnest(string_to_array(substring(pg_get_constraintdef(oid) FROM '\((.*?)\)'), ',')) AS pk_column,
pg_get_constraintdef(oid) as pk_def
FROM
pg_constraint
WHERE
contype = 'p'
AND connamespace::regnamespace::text NOT IN ('information_schema', 'pg_catalog')
AND connamespace::regnamespace::text NOT IN ('pg_extension', 'crdb_internal')
) AS y
),
indexes_cols AS (
SELECT tnsp.nspname AS schema_name,
trel.relname AS table_name,
null AS index_size,
irel.relname AS index_name,
am.amname AS index_type,
a.attname AS col_name,
(CASE WHEN i.indisunique = TRUE THEN 'true' ELSE 'false' END) AS is_unique,
irel.reltuples AS cardinality,
1 + Array_position(i.indkey, a.attnum) AS column_position,
CASE o.OPTION & 1 WHEN 1 THEN 'DESC' ELSE 'ASC' END AS direction,
CASE WHEN indpred IS NOT NULL THEN 'true' ELSE 'false' END AS is_partial_index
FROM pg_index AS i
JOIN pg_class AS trel ON trel.oid = i.indrelid
JOIN pg_namespace AS tnsp ON trel.relnamespace = tnsp.oid
JOIN pg_class AS irel ON irel.oid = i.indexrelid
JOIN pg_am AS am ON irel.relam = am.oid
CROSS JOIN LATERAL unnest (i.indkey)
WITH ORDINALITY AS c (colnum, ordinality) LEFT JOIN LATERAL unnest (i.indoption)
WITH ORDINALITY AS o (option, ordinality)
ON c.ordinality = o.ordinality JOIN pg_attribute AS a ON trel.oid = a.attrelid AND a.attnum = c.colnum
WHERE tnsp.nspname NOT LIKE 'pg_%'
GROUP BY tnsp.nspname, trel.relname, irel.relname, am.amname, i.indisunique, i.indexrelid, irel.reltuples, a.attname, Array_position(i.indkey, a.attnum), o.OPTION, i.indpred
),
cols AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', cols.table_schema::TEXT,
'","table":"', cols.table_name::TEXT,
'","name":"', cols.column_name::TEXT,
'","ordinal_position":"', cols.ordinal_position::TEXT,
'","type":"', LOWER(replace(cols.data_type::TEXT, '"', '')),
'","character_maximum_length":"', COALESCE(cols.character_maximum_length::TEXT, 'null'),
'","precision":',
CASE
WHEN cols.data_type = 'numeric' OR cols.data_type = 'decimal'
THEN CONCAT('{"precision":', COALESCE(cols.numeric_precision::TEXT, 'null'),
',"scale":', COALESCE(cols.numeric_scale::TEXT, 'null'), '}')
ELSE 'null'
END,
',"nullable":', CASE WHEN (cols.IS_NULLABLE = 'YES') THEN 'true' ELSE 'false' END::TEXT,
',"default":"', COALESCE(replace(replace(cols.column_default::TEXT, '"', '\"'), '\x', '\\x'), ''),
'","collation":"', COALESCE(cols.COLLATION_NAME::TEXT, ''),
'","comment":"', COALESCE(replace(replace(dsc.description::TEXT, '"', '\"'), '\x', '\\x'), ''),
'"}')), ',') AS cols_metadata
FROM information_schema.columns cols
LEFT JOIN pg_catalog.pg_class c
ON c.relname = cols.table_name
JOIN pg_catalog.pg_namespace n
ON n.oid = c.relnamespace AND n.nspname = cols.table_schema
LEFT JOIN pg_catalog.pg_description dsc ON dsc.objoid = c.oid
AND dsc.objsubid = cols.ordinal_position
WHERE cols.table_schema NOT IN ('information_schema', 'pg_catalog')
AND cols.table_schema NOT IN ('pg_extension', 'crdb_internal')
), indexes_metadata AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', schema_name::TEXT,
'","table":"', table_name::TEXT,
'","name":"', index_name::TEXT,
'","column":"', replace(col_name::TEXT, '"', E'"'),
'","index_type":"', index_type::TEXT,
'","cardinality":', COALESCE(cardinality::TEXT, '0'),
',"size":', COALESCE(index_size::TEXT, 'null'),
',"unique":', is_unique::TEXT,
',"is_partial_index":', is_partial_index::TEXT,
',"column_position":', column_position::TEXT,
',"direction":"', LOWER(direction::TEXT),
'"}')), ',') AS indexes_metadata
FROM indexes_cols x
WHERE schema_name NOT IN ('pg_extension', 'crdb_internal')
), tbls AS (
SELECT array_to_string(array_agg(CONCAT('{',
'"schema":"', tbls.TABLE_SCHEMA::TEXT, '",',
'"table":"', tbls.TABLE_NAME::TEXT, '",',
'"rows":', COALESCE((SELECT s.n_live_tup::TEXT
FROM pg_stat_user_tables s
WHERE tbls.TABLE_SCHEMA = s.schemaname AND tbls.TABLE_NAME = s.relname),
'0'), ', "type":"', tbls.TABLE_TYPE::TEXT, '",', '"engine":"",', '"collation":"",',
'"comment":"', COALESCE(replace(replace(dsc.description::TEXT, '"', '\"'), '\x', '\\x'), ''),
'"}'
)),
',') AS tbls_metadata
FROM information_schema.tables tbls
LEFT JOIN pg_catalog.pg_class c ON c.relname = tbls.TABLE_NAME
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
AND n.nspname = tbls.TABLE_SCHEMA
LEFT JOIN pg_catalog.pg_description dsc ON dsc.objoid = c.oid
AND dsc.objsubid = 0
WHERE tbls.TABLE_SCHEMA NOT IN ('information_schema', 'pg_catalog')
AND tbls.table_schema NOT IN ('pg_extension', 'crdb_internal')
), config AS (
SELECT array_to_string(
array_agg(CONCAT('{"name":"', conf.name, '","value":"', replace(conf.setting, '"', E'"'), '"}')),
',') AS config_metadata
FROM pg_settings conf
), views AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', views.schemaname::TEXT,
'","view_name":"', viewname::TEXT,
'","view_definition":"', encode(convert_to(REPLACE(definition::TEXT, '"', '\"'), 'UTF8'), 'base64'),
'"}')),
',') AS views_metadata
FROM pg_views views
WHERE views.schemaname NOT IN ('information_schema', 'pg_catalog')
AND views.schemaname NOT IN ('pg_extension', 'crdb_internal')
)
SELECT CONCAT('{ "fk_info": [', COALESCE(fk_metadata, ''),
'], "pk_info": [', COALESCE(pk_metadata, ''),
'], "columns": [', COALESCE(cols_metadata, ''),
'], "indexes": [', COALESCE(indexes_metadata, ''),
'], "tables":[', COALESCE(tbls_metadata, ''),
'], "views":[', COALESCE(views_metadata, ''),
'], "database_name": "', CURRENT_DATABASE(), '', '", "version": "', '',
'"}') AS metadata_json_to_import
FROM fk_info, pk_info, cols, indexes_metadata, tbls, config, views;
```
## Troubleshooting
Find solutions for frequently encountered import problems and their resolutions
# Import DBML
Source: https://docs.chartdb.io/docs/import/dbml
Import a **DBML** script into an existing diagram or a new empty diagram in ChartDB.
When importing into an existing diagram, **tables with the same schema and
name will be overwritten**.
Click **File → New → Empty Diagram**
Only complete this step if you want to import DBML into a new diagram.
Click **File → Import → .dbml**
Paste your **DBML** script and click **Import**
# Import Diagram from JSON
Source: https://docs.chartdb.io/docs/import/json
Import a previously exported ChartDB diagram from a JSON file to continue editing and visualizing your database.
The **Import Diagram** feature is designed to work exclusively with JSON files
that have been exported from ChartDB using the [**Export
Diagram**](/export/json) feature. This feature is for importing saved ChartDB
diagrams, not for importing database schemas directly.
ChartDB lets you import a database diagram from a JSON file that was previously exported from ChartDB.
This feature restores a saved diagram, allowing you to continue your work and make further modifications.
Navigate to the **Share** menu in the top navigation bar. From the dropdown,
select **Import Diagram**.
Click in the designated upload area or drag and drop your previously
exported `.json` file into the dialog.
Once the JSON file is uploaded, click the **Import** button. ChartDB will
then load the diagram from the JSON file, displaying your database schema
and relationships.
# Import MariaDB
Source: https://docs.chartdb.io/docs/import/mariadb
Import your MariaDB database into ChartDB
ChartDB never stores or accesses your data - the import query only retrieves
schema metadata.
## Quick Start
1. Click **File** > **New** in the ChartDB editor
2. Select **MariaDB** from the database options
Execute the provided query using either: - Your database client (e.g., MariaDB Workbench, DBeaver) - Direct mysql connection
1. Copy the JSON output from the query
2. Paste into ChartDB's import field
3. Click **Import** to generate your diagram
## Import Methods
```sql MariaDB theme={null}
-- Regular MariaDB Database Import Query
WITH fk_info AS (
(
SELECT (@fk_info:=NULL),
(
SELECT (0)
FROM (
SELECT kcu.table_schema,
kcu.table_name,
kcu.column_name AS fk_column,
kcu.constraint_name AS foreign_key_name,
kcu.referenced_table_schema AS reference_schema,
kcu.referenced_table_name AS reference_table,
kcu.referenced_column_name AS reference_column,
Concat('FOREIGN KEY (', kcu.column_name, ') REFERENCES ', kcu.referenced_table_name, '(', kcu.referenced_column_name, ') ', 'ON UPDATE ', rc.update_rule, ' ON DELETE ', rc.delete_rule) AS fk_def
FROM information_schema.key_column_usage kcu
JOIN information_schema.referential_constraints rc
ON kcu.constraint_name = rc.constraint_name
AND kcu.table_schema = rc.constraint_schema
AND kcu.table_name = rc.table_name
WHERE kcu.referenced_table_name IS NOT NULL) AS fk
WHERE table_schema LIKE Ifnull(NULL, '%')
AND table_schema = Database()
AND (
0x00) IN (@fk_info:=concat_ws(',', @fk_info, Concat('{"schema":"',table_schema, '","table":"',table_name, '","column":"', Ifnull(fk_column, ''), '","foreign_key_name":"', Ifnull(foreign_key_name, ''), '","reference_schema":"', Ifnull(reference_schema, ''), '","reference_table":"', Ifnull(reference_table, ''), '","reference_column":"', Ifnull(reference_column, ''), '","fk_def":"', Ifnull(fk_def, ''), '"}'))))) ), pk_info AS (
(
SELECT (@pk_info:=NULL),
(
SELECT (0)
FROM (
SELECT table_schema,
table_name AS pk_table,
column_name AS pk_column,
(
SELECT concat('PRIMARY KEY (', group_concat(inc.column_name ORDER BY inc.ordinal_position separator ', '), ')')
FROM information_schema.key_column_usage AS inc
WHERE inc.constraint_name = 'PRIMARY'
AND outc.table_schema = inc.table_schema
AND outc.table_name = inc.table_name) AS pk_def
FROM information_schema.key_column_usage AS outc
WHERE constraint_name = 'PRIMARY'
GROUP BY table_schema,
table_name,
column_name
ORDER BY table_schema,
table_name,
ordinal_position) AS pk
WHERE table_schema LIKE ifnull(NULL, '%')
AND table_schema = DATABASE()
AND (
0x00) IN (@pk_info:=concat_ws(',', @pk_info, concat('{"schema":"', table_schema, '","table":"', pk_table, '","column":"', pk_column, '","pk_def":"', ifnull(pk_def, ''), '"}'))))) ), cols AS (
(
SELECT (@cols := NULL),
(
SELECT (0)
FROM information_schema.columns cols
WHERE cols.table_schema LIKE ifnull(NULL, '%')
AND cols.table_schema = DATABASE()
AND (
0x00) IN (@cols := concat_ws(',', @cols, concat( '{"schema":"', cols.table_schema, '","table":"', cols.table_name, '","name":"', replace(cols.column_name, '"', '\"'), '","type":"', lower(cols.data_type), '","character_maximum_length":"', ifnull(cols.character_maximum_length, 'null'), '","precision":',
CASE
WHEN cols.data_type IN ('decimal',
'numeric') THEN concat('{"precision":', ifnull(cols.numeric_precision, 'null'), ',"scale":', ifnull(cols.numeric_scale, 'null'), '}')
ELSE 'null'
END, ',"ordinal_position":"', cols.ordinal_position, '","nullable":',IF(cols.is_nullable = 'YES', 'true', 'false'), ',"default":"', ifnull(replace(replace(cols.column_default, '\\', ''), '"', '\"'), ''), '","collation":"', ifnull(cols.collation_name, ''), '"}' ))))) ), indexes AS (
(
SELECT (@indexes:=NULL),
(
SELECT (0)
FROM information_schema.STATISTICS indexes
WHERE table_schema LIKE ifnull(NULL, '%')
AND table_schema = DATABASE()
AND (
0x00) IN (@indexes:=concat_ws(',', @indexes, concat('{"schema":"',indexes.table_schema, '","table":"',indexes.table_name, '","name":"', indexes.index_name, '","size":"',
(
SELECT ifnull(sum(stat_value \* @@innodb_page_size), -1) AS size_in_bytes
FROM mysql.innodb_index_stats
WHERE stat_name = 'size'
AND index_name != 'PRIMARY'
AND index_name = indexes.index_name
AND table_name = indexes.table_name
AND database_name = indexes.table_schema), '","column":"', indexes.column_name, '","index_type":"', lower(indexes.index_type), '","cardinality":', indexes.cardinality, ',"direction":"', (
CASE
WHEN indexes.collation = 'D' THEN 'desc'
ELSE 'asc'
END), '","unique":', IF(indexes.non_unique = 1, 'false', 'true'), '}'))))) ), tbls AS (
(
SELECT (@tbls:=NULL),
(
SELECT (0)
FROM information_schema.tables tbls
WHERE table_schema LIKE ifnull(NULL, '%')
AND table_schema = DATABASE()
AND (
0x00) IN (@tbls:=concat_ws(',', @tbls, concat('{', '"schema":"', `table_schema`, '",', '"table":"', `table_name`, '",', '"rows":', ifnull(`table_rows`, 0), ', "type":"', ifnull(`table_type`, ''), '",', '"engine":"', ifnull(`engine`, ''), '",', '"collation":"', ifnull(`table_collation`, ''), '"}'))))) ), views AS (
(
SELECT (@views:=NULL),
(
SELECT (0)
FROM information_schema.views views
WHERE table_schema LIKE ifnull(NULL, '%')
AND table_schema = DATABASE()
AND (
0x00) IN (@views:=concat_ws(',', @views, concat('{', '"schema":"', `table_schema`, '",', '"view_name":"', `table_name`, '",', '"view_definition":"', replace(replace(to_base64(view_definition), ' ', ''), ' ', ''), '"}'))) ) ) )
(
SELECT cast(concat('{"fk_info": [',ifnull(@fk_info,''), '], "pk_info": [', ifnull(@pk_info, ''), '], "columns": [',ifnull(@cols,''), '], "indexes": [',ifnull(@indexes,''), '], "tables":[',ifnull(@tbls,''), '], "views":[',ifnull(@views,''), '], "database_name": "', DATABASE(), '", "version": "', version(), '"}') AS char) AS metadata_json_to_import
FROM fk_info,
pk_info,
cols,
indexes,
tbls,
views);
```
## Troubleshooting
Find solutions for frequently encountered import problems and their
resolutions
# Import MySQL
Source: https://docs.chartdb.io/docs/import/mysql
Import your MySQL database schema into ChartDB
ChartDB never stores or accesses your data - the import query only retrieves schema metadata.
## Quick Start
1. Click **File** > **New** in the ChartDB editor
2. Select **MySQL** from the database options
Select your MySQL edition:
* **Regular** - Standard MySQL installations
* **V5.7** - MySQL 5.7 installations
Execute the provided query using either:
* Your database client (e.g., MySQL Workbench, DBeaver)
* Direct mysql connection
1. Copy the JSON output from the query
2. Paste into ChartDB's import field
3. Click **Import** to generate your diagram
## Import Methods
```sql MySQL theme={null}
-- Regular MySQL Database Import Query
WITH fk_info as (
(SELECT (@fk_info:=NULL),
(SELECT (0)
FROM (SELECT kcu.table_schema,
kcu.table_name,
kcu.column_name as fk_column,
kcu.constraint_name as foreign_key_name,
kcu.referenced_table_schema as reference_schema,
kcu.referenced_table_name as reference_table,
kcu.referenced_column_name as reference_column,
CONCAT('FOREIGN KEY (', kcu.column_name, ') REFERENCES ',
kcu.referenced_table_name, '(', kcu.referenced_column_name, ') ',
'ON UPDATE ', rc.update_rule,
' ON DELETE ', rc.delete_rule) AS fk_def
FROM
information_schema.key_column_usage kcu
JOIN
information_schema.referential_constraints rc
ON kcu.constraint_name = rc.constraint_name
AND kcu.table_schema = rc.constraint_schema
AND kcu.table_name = rc.table_name
WHERE
kcu.referenced_table_name IS NOT NULL) as fk
WHERE table_schema LIKE IFNULL(NULL, '%')
AND table_schema = DATABASE()
AND (0x00) IN (@fk_info:=CONCAT_WS(',', @fk_info, CONCAT('{"schema":"',table_schema,
'","table":"',table_name,
'","column":"', IFNULL(fk_column, ''),
'","foreign_key_name":"', IFNULL(foreign_key_name, ''),
'","reference_schema":"', IFNULL(reference_schema, ''),
'","reference_table":"', IFNULL(reference_table, ''),
'","reference_column":"', IFNULL(reference_column, ''),
'","fk_def":"', IFNULL(fk_def, ''),
'"}')))))
), pk_info AS (
(SELECT (@pk_info:=NULL),
(SELECT (0)
FROM (SELECT TABLE_SCHEMA,
TABLE_NAME AS pk_table,
COLUMN_NAME AS pk_column,
(SELECT CONCAT('PRIMARY KEY (', GROUP_CONCAT(inc.COLUMN_NAME ORDER BY inc.ORDINAL_POSITION SEPARATOR ', '), ')')
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE as inc
WHERE inc.CONSTRAINT_NAME = 'PRIMARY' and
outc.TABLE_SCHEMA = inc.TABLE_SCHEMA and
outc.TABLE_NAME = inc.TABLE_NAME) AS pk_def
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE as outc
WHERE CONSTRAINT_NAME = 'PRIMARY'
GROUP BY TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME
ORDER BY TABLE_SCHEMA, TABLE_NAME, MIN(ORDINAL_POSITION)) AS pk
WHERE table_schema LIKE IFNULL(NULL, '%')
AND table_schema = DATABASE()
AND (0x00) IN (@pk_info:=CONCAT_WS(',', @pk_info, CONCAT('{"schema":"', table_schema,
'","table":"', pk_table,
'","column":"', pk_column,
'","pk_def":"', IFNULL(pk_def, ''),
'"}')))))
), cols as
(
(SELECT (@cols := NULL),
(SELECT (0)
FROM information_schema.columns cols
WHERE cols.table_schema LIKE IFNULL(NULL, '%')
AND cols.table_schema = DATABASE()
AND (0x00) IN (@cols := CONCAT_WS(',', @cols, CONCAT(
'{"schema":"', cols.table_schema,
'","table":"', cols.table_name,
'","name":"', REPLACE(cols.column_name, '"', '\"'),
'","type":"', LOWER(cols.data_type),
'","character_maximum_length":"', IFNULL(cols.character_maximum_length, 'null'),
'","precision":',
CASE
WHEN cols.data_type IN ('decimal', 'numeric')
THEN CONCAT('{"precision":', IFNULL(cols.numeric_precision, 'null'),
',"scale":', IFNULL(cols.numeric_scale, 'null'), '}')
ELSE 'null'
END,
',"ordinal_position":"', cols.ordinal_position,
'","nullable":', IF(cols.is_nullable = 'YES', 'true', 'false'),
',"default":"', IFNULL(REPLACE(REPLACE(cols.column_default, '\\', ''), '"', 'ֿֿֿ\"'), ''),
'","collation":"', IFNULL(cols.collation_name, ''), '"}'
)))))
), indexes as (
(SELECT (@indexes:=NULL),
(SELECT (0)
FROM information_schema.statistics indexes
WHERE table_schema LIKE IFNULL(NULL, '%')
AND table_schema = DATABASE()
AND (0x00) IN (@indexes:=CONCAT_WS(',', @indexes, CONCAT('{"schema":"',indexes.table_schema,
'","table":"',indexes.table_name,
'","name":"', indexes.index_name,
'","size":"',
(SELECT IFNULL(SUM(stat_value * @@innodb_page_size), -1) AS size_in_bytes
FROM mysql.innodb_index_stats
WHERE stat_name = 'size'
AND index_name != 'PRIMARY'
AND index_name = indexes.index_name
AND TABLE_NAME = indexes.table_name
AND database_name = indexes.table_schema),
'","column":"', indexes.column_name,
'","index_type":"', LOWER(indexes.index_type),
'","cardinality":', indexes.cardinality,
',"direction":"', (CASE WHEN indexes.collation = 'D' THEN 'desc' ELSE 'asc' END),
'","column_position":', indexes.seq_in_index,
',"unique":', IF(indexes.non_unique = 1, 'false', 'true'), '}')))))
), tbls as
(
(SELECT (@tbls:=NULL),
(SELECT (0)
FROM information_schema.tables tbls
WHERE table_schema LIKE IFNULL(NULL, '%')
AND table_schema = DATABASE()
AND (0x00) IN (@tbls:=CONCAT_WS(',', @tbls, CONCAT('{', '"schema":"', `TABLE_SCHEMA`, '",',
'"table":"', `TABLE_NAME`, '",',
'"rows":', IFNULL(`TABLE_ROWS`, 0),
', "type":"', IFNULL(`TABLE_TYPE`, ''), '",',
'"engine":"', IFNULL(`ENGINE`, ''), '",',
'"collation":"', IFNULL(`TABLE_COLLATION`, ''), '"}')))))
), views as (
(SELECT (@views:=NULL),
(SELECT (0)
FROM information_schema.views views
WHERE table_schema LIKE IFNULL(NULL, '%')
AND table_schema = DATABASE()
AND (0x00) IN (@views:=CONCAT_WS(',', @views, CONCAT('{', '"schema":"', `TABLE_SCHEMA`, '",',
'"view_name":"', `TABLE_NAME`, '",',
'"view_definition":"', REPLACE(REPLACE(TO_BASE64(VIEW_DEFINITION), ' ', ''), '
', ''), '"}'))) ) )
)
(SELECT CAST(CONCAT('{"fk_info": [',IFNULL(@fk_info,''),
'], "pk_info": [', IFNULL(@pk_info, ''),
'], "columns": [',IFNULL(@cols,''),
'], "indexes": [',IFNULL(@indexes,''),
'], "tables":[',IFNULL(@tbls,''),
'], "views":[',IFNULL(@views,''),
'], "database_name": "', DATABASE(),
'", "version": "', VERSION(), '"}') AS CHAR) AS metadata_json_to_import
FROM fk_info, pk_info, cols, indexes, tbls, views);
```
```sql MySQL V5.7 theme={null}
-- MySQL 5.7 Database Import Query
SET SESSION group_concat_max_len = 1000000; -- large enough value to handle your expected result size
SELECT CAST(CONCAT(
'{"fk_info": [',
IFNULL((SELECT GROUP_CONCAT(
CONCAT('{"schema":"', cast(fk.table_schema as CHAR),
'","table":"', fk.table_name,
'","column":"', IFNULL(fk.fk_column, ''),
'","foreign_key_name":"', IFNULL(fk.foreign_key_name, ''),
'","reference_table":"', IFNULL(fk.reference_table, ''),
'","reference_schema":"', IFNULL(fk.reference_schema, ''),
'","reference_column":"', IFNULL(fk.reference_column, ''),
'","fk_def":"', IFNULL(fk.fk_def, ''), '"}')
) FROM (
SELECT kcu.table_schema,
kcu.table_name,
kcu.column_name AS fk_column,
kcu.constraint_name AS foreign_key_name,
kcu.referenced_table_schema as reference_schema,
kcu.referenced_table_name AS reference_table,
kcu.referenced_column_name AS reference_column,
CONCAT('FOREIGN KEY (', kcu.column_name, ') REFERENCES ',
kcu.referenced_table_name, '(', kcu.referenced_column_name, ') ',
'ON UPDATE ', rc.update_rule,
' ON DELETE ', rc.delete_rule) AS fk_def
FROM information_schema.key_column_usage kcu
JOIN information_schema.referential_constraints rc
ON kcu.constraint_name = rc.constraint_name
AND kcu.table_schema = rc.constraint_schema
AND kcu.table_name = rc.table_name
WHERE kcu.referenced_table_name IS NOT NULL
AND kcu.table_schema = DATABASE()
) AS fk), ''),
'], "pk_info": [',
IFNULL((SELECT GROUP_CONCAT(
CONCAT('{"schema":"', cast(pk.TABLE_SCHEMA as CHAR),
'","table":"', pk.pk_table,
'","column":"', pk.pk_column,
'","pk_def":"', IFNULL(pk.pk_def, ''), '"}')
) FROM (
SELECT TABLE_SCHEMA,
TABLE_NAME AS pk_table,
COLUMN_NAME AS pk_column,
(SELECT CONCAT('PRIMARY KEY (', GROUP_CONCAT(inc.COLUMN_NAME ORDER BY inc.ORDINAL_POSITION SEPARATOR ', '), ')')
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE as inc
WHERE inc.CONSTRAINT_NAME = 'PRIMARY' and
outc.TABLE_SCHEMA = inc.TABLE_SCHEMA and
outc.TABLE_NAME = inc.TABLE_NAME) AS pk_def
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE as outc
WHERE CONSTRAINT_NAME = 'PRIMARY'
and table_schema LIKE IFNULL(NULL, '%')
AND table_schema = DATABASE()
GROUP BY TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME
) AS pk), ''),
'], "columns": [',
IFNULL((SELECT GROUP_CONCAT(
CONCAT('{"schema":"', cast(cols.table_schema as CHAR),
'","table":"', cols.table_name,
'","name":"', REPLACE(cols.column_name, '"', '\"'),
'","type":"', LOWER(cols.data_type),
'","character_maximum_length":"', IFNULL(cols.character_maximum_length, 'null'),
'","precision":',
IF(cols.data_type IN ('decimal', 'numeric'),
CONCAT('{"precision":', IFNULL(cols.numeric_precision, 'null'),
',"scale":', IFNULL(cols.numeric_scale, 'null'), '}'), 'null'),
',"ordinal_position":"', cols.ordinal_position,
'","nullable":', IF(cols.is_nullable = 'YES', 'true', 'false'),
',"default":"', IFNULL(REPLACE(REPLACE(cols.column_default, '\\', ''), '"', '\"'), ''),
'","collation":"', IFNULL(cols.collation_name, ''), '"}')
) FROM (
SELECT cols.table_schema,
cols.table_name,
cols.column_name,
LOWER(cols.data_type) AS data_type,
cols.character_maximum_length,
cols.numeric_precision,
cols.numeric_scale,
cols.ordinal_position,
cols.is_nullable,
cols.column_default,
cols.collation_name
FROM information_schema.columns cols
WHERE cols.table_schema = DATABASE()
) AS cols), ''),
'], "indexes": [',
IFNULL((SELECT GROUP_CONCAT(
CONCAT('{"schema":"', cast(idx.table_schema as CHAR),
'","table":"', idx.table_name,
'","name":"', idx.index_name,
'","size":"', IFNULL(
(SELECT SUM(stat_value * @@innodb_page_size)
FROM mysql.innodb_index_stats
WHERE stat_name = 'size'
AND index_name != 'PRIMARY'
AND index_name = idx.index_name
AND TABLE_NAME = idx.table_name
AND database_name = idx.table_schema), -1),
'","column":"', idx.column_name,
'","index_type":"', LOWER(idx.index_type),
'","cardinality":', idx.cardinality,
',"direction":"', (CASE WHEN idx.collation = 'D' THEN 'desc' ELSE 'asc' END),
'","column_position":', idx.seq_in_index,
',"unique":', IF(idx.non_unique = 1, 'false', 'true'), '}')
) FROM (
SELECT indexes.table_schema,
indexes.table_name,
indexes.index_name,
indexes.column_name,
LOWER(indexes.index_type) AS index_type,
indexes.cardinality,
indexes.collation,
indexes.non_unique,
indexes.seq_in_index
FROM information_schema.statistics indexes
WHERE indexes.table_schema = DATABASE()
) AS idx), ''),
'], "tables":[',
IFNULL((SELECT GROUP_CONCAT(
CONCAT('{"schema":"', cast(tbls.TABLE_SCHEMA as CHAR),
'","table":"', tbls.TABLE_NAME,
'","rows":', IFNULL(tbls.TABLE_ROWS, 0),
',"type":"', IFNULL(tbls.TABLE_TYPE, ''),
'","engine":"', IFNULL(tbls.ENGINE, ''),
'","collation":"', IFNULL(tbls.TABLE_COLLATION, ''), '"}')
) FROM (
SELECT `TABLE_SCHEMA`,
`TABLE_NAME`,
`TABLE_ROWS`,
`TABLE_TYPE`,
`ENGINE`,
`TABLE_COLLATION`
FROM information_schema.tables tbls
WHERE tbls.table_schema = DATABASE()
) AS tbls), ''),
'], "views":[',
IFNULL((SELECT GROUP_CONCAT(
CONCAT('{"schema":"', cast(vws.TABLE_SCHEMA as CHAR),
'","view_name":"', vws.view_name,
'","view_definition":"', view_definition, '"}')
) FROM (
SELECT `TABLE_SCHEMA`,
`TABLE_NAME` AS view_name,
REPLACE(REPLACE(TO_BASE64(`VIEW_DEFINITION`), ' ', ''), '
', '') AS view_definition
FROM information_schema.views vws
WHERE vws.table_schema = DATABASE()
) AS vws), ''),
'], "database_name": "', DATABASE(),
'", "version": "', VERSION(), '"}') AS CHAR) AS metadata_json_to_import
```
## Troubleshooting
Find solutions for frequently encountered import problems and their resolutions
# Import PostgreSQL
Source: https://docs.chartdb.io/docs/import/postgresql
Import your PostgreSQL database schema into ChartDB using a single query
ChartDB never stores or accesses your data - the import query only retrieves schema metadata.
## Quick Start
1. Click **File** > **New** in the ChartDB editor
2. Select **PostgreSQL** from the database options
Select your PostgreSQL edition:
* **Regular** - Standard PostgreSQL installations
* **Supabase** - Supabase-hosted PostgreSQL databases
* **Timescale** - TimescaleDB installations
Execute the provided query using either:
* Your database client (e.g., pgAdmin, DBeaver)
* Direct psql connection
* Supabase SQL Editor (for Supabase databases)
1. Copy the JSON output from the query
2. Paste into ChartDB's import field
3. Click **Import** to generate your diagram
## Import Methods
Execute the appropriate query based on your PostgreSQL edition:
```sql PostgreSQL theme={null}
-- Regular PostgreSQL Database Import Query
WITH fk_info AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', replace(schema_name, '"', ''), '"',
',"table":"', replace(table_name::text, '"', ''), '"',
',"column":"', replace(fk_column::text, '"', ''), '"',
',"foreign_key_name":"', foreign_key_name, '"',
',"reference_schema":"', COALESCE(reference_schema, 'public'), '"',
',"reference_table":"', reference_table, '"',
',"reference_column":"', reference_column, '"',
',"fk_def":"', replace(fk_def, '"', ''),
'"}')), ',') as fk_metadata
FROM (
SELECT c.conname AS foreign_key_name,
n.nspname AS schema_name,
CASE
WHEN position('.' in conrelid::regclass::text) > 0
THEN split_part(conrelid::regclass::text, '.', 2)
ELSE conrelid::regclass::text
END AS table_name,
a.attname AS fk_column,
nr.nspname AS reference_schema,
CASE
WHEN position('.' in confrelid::regclass::text) > 0
THEN split_part(confrelid::regclass::text, '.', 2)
ELSE confrelid::regclass::text
END AS reference_table,
af.attname AS reference_column,
pg_get_constraintdef(c.oid) as fk_def
FROM
pg_constraint AS c
JOIN
pg_attribute AS a ON a.attnum = ANY(c.conkey) AND a.attrelid = c.conrelid
JOIN
pg_class AS cl ON cl.oid = c.conrelid
JOIN
pg_namespace AS n ON n.oid = cl.relnamespace
JOIN
pg_attribute AS af ON af.attnum = ANY(c.confkey) AND af.attrelid = c.confrelid
JOIN
pg_class AS clf ON clf.oid = c.confrelid
JOIN
pg_namespace AS nr ON nr.oid = clf.relnamespace
WHERE
c.contype = 'f'
AND connamespace::regnamespace::text NOT IN ('information_schema', 'pg_catalog')
) AS x
), pk_info AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', replace(schema_name, '"', ''), '"',
',"table":"', replace(pk_table, '"', ''), '"',
',"column":"', replace(pk_column, '"', ''), '"',
',"pk_def":"', replace(pk_def, '"', ''),
'"}')), ',') AS pk_metadata
FROM (
SELECT connamespace::regnamespace::text AS schema_name,
CASE
WHEN strpos(conrelid::regclass::text, '.') > 0
THEN split_part(conrelid::regclass::text, '.', 2)
ELSE conrelid::regclass::text
END AS pk_table,
unnest(string_to_array(substring(pg_get_constraintdef(oid) FROM '\((.*?)\)'), ',')) AS pk_column,
pg_get_constraintdef(oid) as pk_def
FROM
pg_constraint
WHERE
contype = 'p'
AND connamespace::regnamespace::text NOT IN ('information_schema', 'pg_catalog')
) AS y
),
indexes_cols AS (
SELECT tnsp.nspname AS schema_name,
trel.relname AS table_name,
pg_relation_size('"' || tnsp.nspname || '".' || '"' || irel.relname || '"') AS index_size,
irel.relname AS index_name,
am.amname AS index_type,
a.attname AS col_name,
(CASE WHEN i.indisunique = TRUE THEN 'true' ELSE 'false' END) AS is_unique,
irel.reltuples AS cardinality,
1 + Array_position(i.indkey, a.attnum) AS column_position,
CASE o.OPTION & 1 WHEN 1 THEN 'DESC' ELSE 'ASC' END AS direction,
CASE WHEN indpred IS NOT NULL THEN 'true' ELSE 'false' END AS is_partial_index
FROM pg_index AS i
JOIN pg_class AS trel ON trel.oid = i.indrelid
JOIN pg_namespace AS tnsp ON trel.relnamespace = tnsp.oid
JOIN pg_class AS irel ON irel.oid = i.indexrelid
JOIN pg_am AS am ON irel.relam = am.oid
CROSS JOIN LATERAL unnest (i.indkey)
WITH ORDINALITY AS c (colnum, ordinality) LEFT JOIN LATERAL unnest (i.indoption)
WITH ORDINALITY AS o (option, ordinality)
ON c.ordinality = o.ordinality JOIN pg_attribute AS a ON trel.oid = a.attrelid AND a.attnum = c.colnum
WHERE tnsp.nspname NOT LIKE 'pg_%'
GROUP BY tnsp.nspname, trel.relname, irel.relname, am.amname, i.indisunique, i.indexrelid, irel.reltuples, a.attname, Array_position(i.indkey, a.attnum), o.OPTION, i.indpred
),
cols AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', cols.table_schema,
'","table":"', cols.table_name,
'","name":"', cols.column_name,
'","ordinal_position":"', cols.ordinal_position,
'","type":"', LOWER(replace(cols.data_type, '"', '')),
'","character_maximum_length":"', COALESCE(cols.character_maximum_length::text, 'null'),
'","precision":',
CASE
WHEN cols.data_type = 'numeric' OR cols.data_type = 'decimal'
THEN CONCAT('{"precision":', COALESCE(cols.numeric_precision::text, 'null'),
',"scale":', COALESCE(cols.numeric_scale::text, 'null'), '}')
ELSE 'null'
END,
',"nullable":', CASE WHEN (cols.IS_NULLABLE = 'YES') THEN 'true' ELSE 'false' END,
',"default":"', COALESCE(replace(replace(cols.column_default, '"', '\"'), '\x', '\\x'), ''),
'","collation":"', COALESCE(cols.COLLATION_NAME, ''),
'","comment":"', COALESCE(replace(replace(dsc.description, '"', '\"'), '\x', '\\x'), ''),
'"}')), ',') AS cols_metadata
FROM information_schema.columns cols
LEFT JOIN pg_catalog.pg_class c
ON c.relname = cols.table_name
JOIN pg_catalog.pg_namespace n
ON n.oid = c.relnamespace AND n.nspname = cols.table_schema
LEFT JOIN pg_catalog.pg_description dsc ON dsc.objoid = c.oid
AND dsc.objsubid = cols.ordinal_position
WHERE cols.table_schema NOT IN ('information_schema', 'pg_catalog')
), indexes_metadata AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', schema_name,
'","table":"', table_name,
'","name":"', index_name,
'","column":"', replace(col_name :: TEXT, '"', E'"'),
'","index_type":"', index_type,
'","cardinality":', cardinality,
',"size":', index_size,
',"unique":', is_unique,
',"is_partial_index":', is_partial_index,
',"column_position":', column_position,
',"direction":"', LOWER(direction),
'"}')), ',') AS indexes_metadata
FROM indexes_cols x
), tbls AS (
SELECT array_to_string(array_agg(CONCAT('{',
'"schema":"', tbls.TABLE_SCHEMA, '",',
'"table":"', tbls.TABLE_NAME, '",',
'"rows":', COALESCE((SELECT s.n_live_tup
FROM pg_stat_user_tables s
WHERE tbls.TABLE_SCHEMA = s.schemaname AND tbls.TABLE_NAME = s.relname),
0), ', "type":"', tbls.TABLE_TYPE, '",', '"engine":"",', '"collation":"",',
'"comment":"', COALESCE(replace(replace(dsc.description, '"', '\"'), '\x', '\\x'), ''),
'"}'
)),
',') AS tbls_metadata
FROM information_schema.tables tbls
LEFT JOIN pg_catalog.pg_class c ON c.relname = tbls.TABLE_NAME
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
AND n.nspname = tbls.TABLE_SCHEMA
LEFT JOIN pg_catalog.pg_description dsc ON dsc.objoid = c.oid
AND dsc.objsubid = 0
WHERE tbls.TABLE_SCHEMA NOT IN ('information_schema', 'pg_catalog')
), config AS (
SELECT array_to_string(
array_agg(CONCAT('{"name":"', conf.name, '","value":"', replace(conf.setting, '"', E'"'), '"}')),
',') AS config_metadata
FROM pg_settings conf
), views AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', views.schemaname,
'","view_name":"', viewname,
'","view_definition":"', encode(convert_to(REPLACE(definition, '"', '\"'), 'UTF8'), 'base64'),
'"}')),
',') AS views_metadata
FROM pg_views views
WHERE views.schemaname NOT IN ('information_schema', 'pg_catalog')
)
SELECT CONCAT('{ "fk_info": [', COALESCE(fk_metadata, ''),
'], "pk_info": [', COALESCE(pk_metadata, ''),
'], "columns": [', COALESCE(cols_metadata, ''),
'], "indexes": [', COALESCE(indexes_metadata, ''),
'], "tables":[', COALESCE(tbls_metadata, ''),
'], "views":[', COALESCE(views_metadata, ''),
'], "database_name": "', CURRENT_DATABASE(), '', '", "version": "', '',
'"}') AS metadata_json_to_import
FROM fk_info, pk_info, cols, indexes_metadata, tbls, config, views;
```
Execute the appropriate query based on your PostgreSQL edition:
```sql Timescale theme={null}
-- TimescaleDB Database Import Query
WITH fk_info_timescale AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', replace(schema_name, '"', ''), '"',
',"table":"', replace(table_name::text, '"', ''), '"',
',"column":"', replace(fk_column::text, '"', ''), '"',
',"foreign_key_name":"', foreign_key_name, '"',
',"reference_schema":"', COALESCE(reference_schema, 'public'), '"',
',"reference_table":"', reference_table, '"',
',"reference_column":"', reference_column, '"',
',"fk_def":"', replace(fk_def, '"', ''),
'"}')), ',') as fk_metadata
FROM (
SELECT c.conname AS foreign_key_name,
n.nspname AS schema_name,
CASE
WHEN position('.' in conrelid::regclass::text) > 0
THEN split_part(conrelid::regclass::text, '.', 2)
ELSE conrelid::regclass::text
END AS table_name,
a.attname AS fk_column,
nr.nspname AS reference_schema,
CASE
WHEN position('.' in confrelid::regclass::text) > 0
THEN split_part(confrelid::regclass::text, '.', 2)
ELSE confrelid::regclass::text
END AS reference_table,
af.attname AS reference_column,
pg_get_constraintdef(c.oid) as fk_def
FROM
pg_constraint AS c
JOIN
pg_attribute AS a ON a.attnum = ANY(c.conkey) AND a.attrelid = c.conrelid
JOIN
pg_class AS cl ON cl.oid = c.conrelid
JOIN
pg_namespace AS n ON n.oid = cl.relnamespace
JOIN
pg_attribute AS af ON af.attnum = ANY(c.confkey) AND af.attrelid = c.confrelid
JOIN
pg_class AS clf ON clf.oid = c.confrelid
JOIN
pg_namespace AS nr ON nr.oid = clf.relnamespace
WHERE
c.contype = 'f'
AND connamespace::regnamespace::text NOT IN ('information_schema', 'pg_catalog')
AND connamespace::regnamespace::text !~ '^(timescaledb_|_timescaledb_)'
) AS x
), pk_info AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', replace(schema_name, '"', ''), '"',
',"table":"', replace(pk_table, '"', ''), '"',
',"column":"', replace(pk_column, '"', ''), '"',
',"pk_def":"', replace(pk_def, '"', ''),
'"}')), ',') AS pk_metadata
FROM (
SELECT connamespace::regnamespace::text AS schema_name,
CASE
WHEN strpos(conrelid::regclass::text, '.') > 0
THEN split_part(conrelid::regclass::text, '.', 2)
ELSE conrelid::regclass::text
END AS pk_table,
unnest(string_to_array(substring(pg_get_constraintdef(oid) FROM '\((.*?)\)'), ',')) AS pk_column,
pg_get_constraintdef(oid) as pk_def
FROM
pg_constraint
WHERE
contype = 'p'
AND connamespace::regnamespace::text NOT IN ('information_schema', 'pg_catalog')
AND connamespace::regnamespace::text !~ '^(timescaledb_|_timescaledb_)'
) AS y
),
indexes_cols AS (
SELECT tnsp.nspname AS schema_name,
trel.relname AS table_name,
pg_relation_size('"' || tnsp.nspname || '".' || '"' || irel.relname || '"') AS index_size,
irel.relname AS index_name,
am.amname AS index_type,
a.attname AS col_name,
(CASE WHEN i.indisunique = TRUE THEN 'true' ELSE 'false' END) AS is_unique,
irel.reltuples AS cardinality,
1 + Array_position(i.indkey, a.attnum) AS column_position,
CASE o.OPTION & 1 WHEN 1 THEN 'DESC' ELSE 'ASC' END AS direction,
CASE WHEN indpred IS NOT NULL THEN 'true' ELSE 'false' END AS is_partial_index
FROM pg_index AS i
JOIN pg_class AS trel ON trel.oid = i.indrelid
JOIN pg_namespace AS tnsp ON trel.relnamespace = tnsp.oid
JOIN pg_class AS irel ON irel.oid = i.indexrelid
JOIN pg_am AS am ON irel.relam = am.oid
CROSS JOIN LATERAL unnest (i.indkey)
WITH ORDINALITY AS c (colnum, ordinality) LEFT JOIN LATERAL unnest (i.indoption)
WITH ORDINALITY AS o (option, ordinality)
ON c.ordinality = o.ordinality JOIN pg_attribute AS a ON trel.oid = a.attrelid AND a.attnum = c.colnum
WHERE tnsp.nspname NOT LIKE 'pg_%'
GROUP BY tnsp.nspname, trel.relname, irel.relname, am.amname, i.indisunique, i.indexrelid, irel.reltuples, a.attname, Array_position(i.indkey, a.attnum), o.OPTION, i.indpred
),
cols AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', cols.table_schema,
'","table":"', cols.table_name,
'","name":"', cols.column_name,
'","ordinal_position":"', cols.ordinal_position,
'","type":"', LOWER(replace(cols.data_type, '"', '')),
'","character_maximum_length":"', COALESCE(cols.character_maximum_length::text, 'null'),
'","precision":',
CASE
WHEN cols.data_type = 'numeric' OR cols.data_type = 'decimal'
THEN CONCAT('{"precision":', COALESCE(cols.numeric_precision::text, 'null'),
',"scale":', COALESCE(cols.numeric_scale::text, 'null'), '}')
ELSE 'null'
END,
',"nullable":', CASE WHEN (cols.IS_NULLABLE = 'YES') THEN 'true' ELSE 'false' END,
',"default":"', COALESCE(replace(replace(cols.column_default, '"', '\"'), '\x', '\\x'), ''),
'","collation":"', COALESCE(cols.COLLATION_NAME, ''),
'","comment":"', COALESCE(replace(replace(dsc.description, '"', '\"'), '\x', '\\x'), ''),
'"}')), ',') AS cols_metadata
FROM information_schema.columns cols
LEFT JOIN pg_catalog.pg_class c
ON c.relname = cols.table_name
JOIN pg_catalog.pg_namespace n
ON n.oid = c.relnamespace AND n.nspname = cols.table_schema
LEFT JOIN pg_catalog.pg_description dsc ON dsc.objoid = c.oid
AND dsc.objsubid = cols.ordinal_position
WHERE cols.table_schema NOT IN ('information_schema', 'pg_catalog')
AND cols.table_schema !~ '^(timescaledb_|_timescaledb_)'
AND cols.table_name !~ '^(pg_stat_)'
), indexes_metadata AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', schema_name,
'","table":"', table_name,
'","name":"', index_name,
'","column":"', replace(col_name :: TEXT, '"', E'"'),
'","index_type":"', index_type,
'","cardinality":', cardinality,
',"size":', index_size,
',"unique":', is_unique,
',"is_partial_index":', is_partial_index,
',"column_position":', column_position,
',"direction":"', LOWER(direction),
'"}')), ',') AS indexes_metadata
FROM indexes_cols x
WHERE schema_name !~ '^(timescaledb_|_timescaledb_)'
), tbls AS (
SELECT array_to_string(array_agg(CONCAT('{',
'"schema":"', tbls.TABLE_SCHEMA, '",',
'"table":"', tbls.TABLE_NAME, '",',
'"rows":', COALESCE((SELECT s.n_live_tup
FROM pg_stat_user_tables s
WHERE tbls.TABLE_SCHEMA = s.schemaname AND tbls.TABLE_NAME = s.relname),
0), ', "type":"', tbls.TABLE_TYPE, '",', '"engine":"",', '"collation":"",',
'"comment":"', COALESCE(replace(replace(dsc.description, '"', '\"'), '\x', '\\x'), ''),
'"}'
)),
',') AS tbls_metadata
FROM information_schema.tables tbls
LEFT JOIN pg_catalog.pg_class c ON c.relname = tbls.TABLE_NAME
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
AND n.nspname = tbls.TABLE_SCHEMA
LEFT JOIN pg_catalog.pg_description dsc ON dsc.objoid = c.oid
AND dsc.objsubid = 0
WHERE tbls.TABLE_SCHEMA NOT IN ('information_schema', 'pg_catalog')
AND tbls.table_schema !~ '^(timescaledb_|_timescaledb_)'
AND tbls.table_name !~ '^(pg_stat_)'
), config AS (
SELECT array_to_string(
array_agg(CONCAT('{"name":"', conf.name, '","value":"', replace(conf.setting, '"', E'"'), '"}')),
',') AS config_metadata
FROM pg_settings conf
), views AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', views.schemaname,
'","view_name":"', viewname,
'","view_definition":"', encode(convert_to(REPLACE(definition, '"', '\"'), 'UTF8'), 'base64'),
'"}')),
',') AS views_metadata
FROM pg_views views
WHERE views.schemaname NOT IN ('information_schema', 'pg_catalog')
AND views.schemaname !~ '^(timescaledb_|_timescaledb_)'
)
SELECT CONCAT('{ "fk_info": [', COALESCE(fk_metadata, ''),
'], "pk_info": [', COALESCE(pk_metadata, ''),
'], "columns": [', COALESCE(cols_metadata, ''),
'], "indexes": [', COALESCE(indexes_metadata, ''),
'], "tables":[', COALESCE(tbls_metadata, ''),
'], "views":[', COALESCE(views_metadata, ''),
'], "database_name": "', CURRENT_DATABASE(), '', '", "version": "', '',
'"}') AS metadata_json_to_import
FROM fk_info_timescale, pk_info, cols, indexes_metadata, tbls, config, views;
```
1. Navigate to the Supabase SQL Editor
2. Create a new query
3. Paste and run the Supabase query from the Database Client tab
4. Click "Copy cell content" on the results
```sql Supabase theme={null}
-- Supabase PostgreSQL Database Import Query
WITH fk_info_supabase AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', replace(schema_name, '"', ''), '"',
',"table":"', replace(table_name::text, '"', ''), '"',
',"column":"', replace(fk_column::text, '"', ''), '"',
',"foreign_key_name":"', foreign_key_name, '"',
',"reference_schema":"', COALESCE(reference_schema, 'public'), '"',
',"reference_table":"', reference_table, '"',
',"reference_column":"', reference_column, '"',
',"fk_def":"', replace(fk_def, '"', ''),
'"}')), ',') as fk_metadata
FROM (
SELECT c.conname AS foreign_key_name,
n.nspname AS schema_name,
CASE
WHEN position('.' in conrelid::regclass::text) > 0
THEN split_part(conrelid::regclass::text, '.', 2)
ELSE conrelid::regclass::text
END AS table_name,
a.attname AS fk_column,
nr.nspname AS reference_schema,
CASE
WHEN position('.' in confrelid::regclass::text) > 0
THEN split_part(confrelid::regclass::text, '.', 2)
ELSE confrelid::regclass::text
END AS reference_table,
af.attname AS reference_column,
pg_get_constraintdef(c.oid) as fk_def
FROM
pg_constraint AS c
JOIN
pg_attribute AS a ON a.attnum = ANY(c.conkey) AND a.attrelid = c.conrelid
JOIN
pg_class AS cl ON cl.oid = c.conrelid
JOIN
pg_namespace AS n ON n.oid = cl.relnamespace
JOIN
pg_attribute AS af ON af.attnum = ANY(c.confkey) AND af.attrelid = c.confrelid
JOIN
pg_class AS clf ON clf.oid = c.confrelid
JOIN
pg_namespace AS nr ON nr.oid = clf.relnamespace
WHERE
c.contype = 'f'
AND connamespace::regnamespace::text NOT IN ('information_schema', 'pg_catalog')
AND connamespace::regnamespace::text NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')
) AS x
), pk_info AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', replace(schema_name, '"', ''), '"',
',"table":"', replace(pk_table, '"', ''), '"',
',"column":"', replace(pk_column, '"', ''), '"',
',"pk_def":"', replace(pk_def, '"', ''),
'"}')), ',') AS pk_metadata
FROM (
SELECT connamespace::regnamespace::text AS schema_name,
CASE
WHEN strpos(conrelid::regclass::text, '.') > 0
THEN split_part(conrelid::regclass::text, '.', 2)
ELSE conrelid::regclass::text
END AS pk_table,
unnest(string_to_array(substring(pg_get_constraintdef(oid) FROM '\((.*?)\)'), ',')) AS pk_column,
pg_get_constraintdef(oid) as pk_def
FROM
pg_constraint
WHERE
contype = 'p'
AND connamespace::regnamespace::text NOT IN ('information_schema', 'pg_catalog')
AND connamespace::regnamespace::text NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')
) AS y
),
indexes_cols AS (
SELECT tnsp.nspname AS schema_name,
trel.relname AS table_name,
pg_relation_size('"' || tnsp.nspname || '".' || '"' || irel.relname || '"') AS index_size,
irel.relname AS index_name,
am.amname AS index_type,
a.attname AS col_name,
(CASE WHEN i.indisunique = TRUE THEN 'true' ELSE 'false' END) AS is_unique,
irel.reltuples AS cardinality,
1 + Array_position(i.indkey, a.attnum) AS column_position,
CASE o.OPTION & 1 WHEN 1 THEN 'DESC' ELSE 'ASC' END AS direction,
CASE WHEN indpred IS NOT NULL THEN 'true' ELSE 'false' END AS is_partial_index
FROM pg_index AS i
JOIN pg_class AS trel ON trel.oid = i.indrelid
JOIN pg_namespace AS tnsp ON trel.relnamespace = tnsp.oid
JOIN pg_class AS irel ON irel.oid = i.indexrelid
JOIN pg_am AS am ON irel.relam = am.oid
CROSS JOIN LATERAL unnest (i.indkey)
WITH ORDINALITY AS c (colnum, ordinality) LEFT JOIN LATERAL unnest (i.indoption)
WITH ORDINALITY AS o (option, ordinality)
ON c.ordinality = o.ordinality JOIN pg_attribute AS a ON trel.oid = a.attrelid AND a.attnum = c.colnum
WHERE tnsp.nspname NOT LIKE 'pg_%'
GROUP BY tnsp.nspname, trel.relname, irel.relname, am.amname, i.indisunique, i.indexrelid, irel.reltuples, a.attname, Array_position(i.indkey, a.attnum), o.OPTION, i.indpred
),
cols AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', cols.table_schema,
'","table":"', cols.table_name,
'","name":"', cols.column_name,
'","ordinal_position":"', cols.ordinal_position,
'","type":"', LOWER(replace(cols.data_type, '"', '')),
'","character_maximum_length":"', COALESCE(cols.character_maximum_length::text, 'null'),
'","precision":',
CASE
WHEN cols.data_type = 'numeric' OR cols.data_type = 'decimal'
THEN CONCAT('{"precision":', COALESCE(cols.numeric_precision::text, 'null'),
',"scale":', COALESCE(cols.numeric_scale::text, 'null'), '}')
ELSE 'null'
END,
',"nullable":', CASE WHEN (cols.IS_NULLABLE = 'YES') THEN 'true' ELSE 'false' END,
',"default":"', COALESCE(replace(replace(cols.column_default, '"', '\"'), '\x', '\\x'), ''),
'","collation":"', COALESCE(cols.COLLATION_NAME, ''),
'","comment":"', COALESCE(replace(replace(dsc.description, '"', '\"'), '\x', '\\x'), ''),
'"}')), ',') AS cols_metadata
FROM information_schema.columns cols
LEFT JOIN pg_catalog.pg_class c
ON c.relname = cols.table_name
JOIN pg_catalog.pg_namespace n
ON n.oid = c.relnamespace AND n.nspname = cols.table_schema
LEFT JOIN pg_catalog.pg_description dsc ON dsc.objoid = c.oid
AND dsc.objsubid = cols.ordinal_position
WHERE cols.table_schema NOT IN ('information_schema', 'pg_catalog')
AND cols.table_schema NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')
), indexes_metadata AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', schema_name,
'","table":"', table_name,
'","name":"', index_name,
'","column":"', replace(col_name :: TEXT, '"', E'"'),
'","index_type":"', index_type,
'","cardinality":', cardinality,
',"size":', index_size,
',"unique":', is_unique,
',"is_partial_index":', is_partial_index,
',"column_position":', column_position,
',"direction":"', LOWER(direction),
'"}')), ',') AS indexes_metadata
FROM indexes_cols x
WHERE schema_name NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')
), tbls AS (
SELECT array_to_string(array_agg(CONCAT('{',
'"schema":"', tbls.TABLE_SCHEMA, '",',
'"table":"', tbls.TABLE_NAME, '",',
'"rows":', COALESCE((SELECT s.n_live_tup
FROM pg_stat_user_tables s
WHERE tbls.TABLE_SCHEMA = s.schemaname AND tbls.TABLE_NAME = s.relname),
0), ', "type":"', tbls.TABLE_TYPE, '",', '"engine":"",', '"collation":"",',
'"comment":"', COALESCE(replace(replace(dsc.description, '"', '\"'), '\x', '\\x'), ''),
'"}'
)),
',') AS tbls_metadata
FROM information_schema.tables tbls
LEFT JOIN pg_catalog.pg_class c ON c.relname = tbls.TABLE_NAME
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
AND n.nspname = tbls.TABLE_SCHEMA
LEFT JOIN pg_catalog.pg_description dsc ON dsc.objoid = c.oid
AND dsc.objsubid = 0
WHERE tbls.TABLE_SCHEMA NOT IN ('information_schema', 'pg_catalog')
AND tbls.table_schema NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')
), config AS (
SELECT array_to_string(
array_agg(CONCAT('{"name":"', conf.name, '","value":"', replace(conf.setting, '"', E'"'), '"}')),
',') AS config_metadata
FROM pg_settings conf
), views AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', views.schemaname,
'","view_name":"', viewname,
'","view_definition":"', encode(convert_to(REPLACE(definition, '"', '\"'), 'UTF8'), 'base64'),
'"}')),
',') AS views_metadata
FROM pg_views views
WHERE views.schemaname NOT IN ('information_schema', 'pg_catalog')
AND views.schemaname NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')
)
SELECT CONCAT('{ "fk_info": [', COALESCE(fk_metadata, ''),
'], "pk_info": [', COALESCE(pk_metadata, ''),
'], "columns": [', COALESCE(cols_metadata, ''),
'], "indexes": [', COALESCE(indexes_metadata, ''),
'], "tables":[', COALESCE(tbls_metadata, ''),
'], "views":[', COALESCE(views_metadata, ''),
'], "database_name": "', CURRENT_DATABASE(), '', '", "version": "', '',
'"}') AS metadata_json_to_import
FROM fk_info_supabase, pk_info, cols, indexes_metadata, tbls, config, views;
```
Connect and run the appropriate query via psql based on your PostgreSQL edition. Replace the following in the command:
* `hostname`: Your database host (e.g., localhost)
* `port`: Database port (default: 5432)
* `username`: Database user
* `database_name`: Name of your database
PostgreSQL connection strings typically follow this format:
```
postgresql://username:password@hostname:port/database_name
```
The `pbcopy` command for copying query results is only available on macOS. For other operating systems, please use your system's clipboard command or copy the output manually.
```bash PostgreSQL theme={null}
psql -h HOST_NAME -p PORT -U USER_NAME -d DATABASE_NAME -c "
/* PostgreSQL edition */
WITH fk_info AS (
SELECT array_to_string(array_agg(CONCAT('{\"schema\":\"', replace(schema_name, '\"', ''), '\"',
',\"table\":\"', replace(table_name::text, '\"', ''), '\"',
',\"column\":\"', replace(fk_column::text, '\"', ''), '\"',
',\"foreign_key_name\":\"', foreign_key_name, '\"',
',\"reference_schema\":\"', COALESCE(reference_schema, 'public'), '\"',
',\"reference_table\":\"', reference_table, '\"',
',\"reference_column\":\"', reference_column, '\"',
',\"fk_def\":\"', replace(fk_def, '\"', ''),
'\"}')), ',') as fk_metadata
FROM (
SELECT c.conname AS foreign_key_name,
n.nspname AS schema_name,
CASE
WHEN position('.' in conrelid::regclass::text) > 0
THEN split_part(conrelid::regclass::text, '.', 2)
ELSE conrelid::regclass::text
END AS table_name,
a.attname AS fk_column,
nr.nspname AS reference_schema,
CASE
WHEN position('.' in confrelid::regclass::text) > 0
THEN split_part(confrelid::regclass::text, '.', 2)
ELSE confrelid::regclass::text
END AS reference_table,
af.attname AS reference_column,
pg_get_constraintdef(c.oid) as fk_def
FROM
pg_constraint AS c
JOIN
pg_attribute AS a ON a.attnum = ANY(c.conkey) AND a.attrelid = c.conrelid
JOIN
pg_class AS cl ON cl.oid = c.conrelid
JOIN
pg_namespace AS n ON n.oid = cl.relnamespace
JOIN
pg_attribute AS af ON af.attnum = ANY(c.confkey) AND af.attrelid = c.confrelid
JOIN
pg_class AS clf ON clf.oid = c.confrelid
JOIN
pg_namespace AS nr ON nr.oid = clf.relnamespace
WHERE
c.contype = 'f'
AND connamespace::regnamespace::text NOT IN ('information_schema', 'pg_catalog')
) AS x
), pk_info AS (
SELECT array_to_string(array_agg(CONCAT('{\"schema\":\"', replace(schema_name, '\"', ''), '\"',
',\"table\":\"', replace(pk_table, '\"', ''), '\"',
',\"column\":\"', replace(pk_column, '\"', ''), '\"',
',\"pk_def\":\"', replace(pk_def, '\"', ''),
'\"}')), ',') AS pk_metadata
FROM (
SELECT connamespace::regnamespace::text AS schema_name,
CASE
WHEN strpos(conrelid::regclass::text, '.') > 0
THEN split_part(conrelid::regclass::text, '.', 2)
ELSE conrelid::regclass::text
END AS pk_table,
unnest(string_to_array(substring(pg_get_constraintdef(oid) FROM '\((.*?)\)'), ',')) AS pk_column,
pg_get_constraintdef(oid) as pk_def
FROM
pg_constraint
WHERE
contype = 'p'
AND connamespace::regnamespace::text NOT IN ('information_schema', 'pg_catalog')
) AS y
),
indexes_cols AS (
SELECT tnsp.nspname AS schema_name,
trel.relname AS table_name,
pg_relation_size('\"' || tnsp.nspname || '\".' || '\"' || irel.relname || '\"') AS index_size,
irel.relname AS index_name,
am.amname AS index_type,
a.attname AS col_name,
(CASE WHEN i.indisunique = TRUE THEN 'true' ELSE 'false' END) AS is_unique,
irel.reltuples AS cardinality,
1 + Array_position(i.indkey, a.attnum) AS column_position,
CASE o.OPTION & 1 WHEN 1 THEN 'DESC' ELSE 'ASC' END AS direction,
CASE WHEN indpred IS NOT NULL THEN 'true' ELSE 'false' END AS is_partial_index
FROM pg_index AS i
JOIN pg_class AS trel ON trel.oid = i.indrelid
JOIN pg_namespace AS tnsp ON trel.relnamespace = tnsp.oid
JOIN pg_class AS irel ON irel.oid = i.indexrelid
JOIN pg_am AS am ON irel.relam = am.oid
CROSS JOIN LATERAL unnest (i.indkey)
WITH ORDINALITY AS c (colnum, ordinality) LEFT JOIN LATERAL unnest (i.indoption)
WITH ORDINALITY AS o (option, ordinality)
ON c.ordinality = o.ordinality JOIN pg_attribute AS a ON trel.oid = a.attrelid AND a.attnum = c.colnum
WHERE tnsp.nspname NOT LIKE 'pg_%'
GROUP BY tnsp.nspname, trel.relname, irel.relname, am.amname, i.indisunique, i.indexrelid, irel.reltuples, a.attname, Array_position(i.indkey, a.attnum), o.OPTION, i.indpred
),
cols AS (
SELECT array_to_string(array_agg(CONCAT('{\"schema\":\"', cols.table_schema,
'\",\"table\":\"', cols.table_name,
'\",\"name\":\"', cols.column_name,
'\",\"ordinal_position\":\"', cols.ordinal_position,
'\",\"type\":\"', LOWER(replace(cols.data_type, '\"', '')),
'\",\"character_maximum_length\":\"', COALESCE(cols.character_maximum_length::text, 'null'),
'\",\"precision\":',
CASE
WHEN cols.data_type = 'numeric' OR cols.data_type = 'decimal'
THEN CONCAT('{\"precision\":', COALESCE(cols.numeric_precision::text, 'null'),
',\"scale\":', COALESCE(cols.numeric_scale::text, 'null'), '}')
ELSE 'null'
END,
',\"nullable\":', CASE WHEN (cols.IS_NULLABLE = 'YES') THEN 'true' ELSE 'false' END,
',\"default\":\"', COALESCE(replace(replace(cols.column_default, '\"', '\\\"'), '\\x', '\\\\x'), ''),
'\",\"collation\":\"', COALESCE(cols.COLLATION_NAME, ''),
'\",\"comment\":\"', COALESCE(replace(replace(dsc.description, '\"', '\\\"'), '\\x', '\\\\x'), ''),
'\"}')), ',') AS cols_metadata
FROM information_schema.columns cols
LEFT JOIN pg_catalog.pg_class c
ON c.relname = cols.table_name
JOIN pg_catalog.pg_namespace n
ON n.oid = c.relnamespace AND n.nspname = cols.table_schema
LEFT JOIN pg_catalog.pg_description dsc ON dsc.objoid = c.oid
AND dsc.objsubid = cols.ordinal_position
WHERE cols.table_schema NOT IN ('information_schema', 'pg_catalog')
), indexes_metadata AS (
SELECT array_to_string(array_agg(CONCAT('{\"schema\":\"', schema_name,
'\",\"table\":\"', table_name,
'\",\"name\":\"', index_name,
'\",\"column\":\"', replace(col_name :: TEXT, '\"', E'\"'),
'\",\"index_type\":\"', index_type,
'\",\"cardinality\":', cardinality,
',\"size\":', index_size,
',\"unique\":', is_unique,
',\"is_partial_index\":', is_partial_index,
',\"column_position\":', column_position,
',\"direction\":\"', LOWER(direction),
'\"}')), ',') AS indexes_metadata
FROM indexes_cols x
), tbls AS (
SELECT array_to_string(array_agg(CONCAT('{',
'\"schema\":\"', tbls.TABLE_SCHEMA, '\",',
'\"table\":\"', tbls.TABLE_NAME, '\",',
'\"rows\":', COALESCE((SELECT s.n_live_tup
FROM pg_stat_user_tables s
WHERE tbls.TABLE_SCHEMA = s.schemaname AND tbls.TABLE_NAME = s.relname),
0), ', \"type\":\"', tbls.TABLE_TYPE, '\",', '\"engine\":\"\",', '\"collation\":\"\",',
'\"comment\":\"', COALESCE(replace(replace(dsc.description, '\"', '\\\"'), '\\x', '\\\\x'), ''),
'\"}'
)),
',') AS tbls_metadata
FROM information_schema.tables tbls
LEFT JOIN pg_catalog.pg_class c ON c.relname = tbls.TABLE_NAME
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
AND n.nspname = tbls.TABLE_SCHEMA
LEFT JOIN pg_catalog.pg_description dsc ON dsc.objoid = c.oid
AND dsc.objsubid = 0
WHERE tbls.TABLE_SCHEMA NOT IN ('information_schema', 'pg_catalog')
), config AS (
SELECT array_to_string(
array_agg(CONCAT('{\"name\":\"', conf.name, '\",\"value\":\"', replace(conf.setting, '\"', E'\"'), '\"}')),
',') AS config_metadata
FROM pg_settings conf
), views AS (
SELECT array_to_string(array_agg(CONCAT('{\"schema\":\"', views.schemaname,
'\",\"view_name\":\"', viewname,
'\",\"view_definition\":\"', encode(convert_to(REPLACE(definition, '\"', '\\\"'), 'UTF8'), 'base64'),
'\"}')),
',') AS views_metadata
FROM pg_views views
WHERE views.schemaname NOT IN ('information_schema', 'pg_catalog')
)
SELECT CONCAT('{ \"fk_info\": [', COALESCE(fk_metadata, ''),
'], \"pk_info\": [', COALESCE(pk_metadata, ''),
'], \"columns\": [', COALESCE(cols_metadata, ''),
'], \"indexes\": [', COALESCE(indexes_metadata, ''),
'], \"tables\":[', COALESCE(tbls_metadata, ''),
'], \"views\":[', COALESCE(views_metadata, ''),
'], \"database_name\": \"', CURRENT_DATABASE(), '', '\", \"version\": \"', '',
'\"}') AS metadata_json_to_import
FROM fk_info, pk_info, cols, indexes_metadata, tbls, config, views;
" -t -A | pbcopy; LG='\033[0;32m'; NC='\033[0m'; echo "You got the resultset ($(pbpaste | wc -c | xargs) characters) in Copy/Paste. ${LG}Go back & paste in ChartDB :)${NC}";
```
```bash Supabase theme={null}
psql -h HOST_NAME -p PORT -U USER_NAME -d DATABASE_NAME -c "
/* Supabase edition */
WITH fk_info_supabase AS (
SELECT array_to_string(array_agg(CONCAT('{\"schema\":\"', replace(schema_name, '\"', ''), '\"',
',\"table\":\"', replace(table_name::text, '\"', ''), '\"',
',\"column\":\"', replace(fk_column::text, '\"', ''), '\"',
',\"foreign_key_name\":\"', foreign_key_name, '\"',
',\"reference_schema\":\"', COALESCE(reference_schema, 'public'), '\"',
',\"reference_table\":\"', reference_table, '\"',
',\"reference_column\":\"', reference_column, '\"',
',\"fk_def\":\"', replace(fk_def, '\"', ''),
'\"}')), ',') as fk_metadata
FROM (
SELECT c.conname AS foreign_key_name,
n.nspname AS schema_name,
CASE
WHEN position('.' in conrelid::regclass::text) > 0
THEN split_part(conrelid::regclass::text, '.', 2)
ELSE conrelid::regclass::text
END AS table_name,
a.attname AS fk_column,
nr.nspname AS reference_schema,
CASE
WHEN position('.' in confrelid::regclass::text) > 0
THEN split_part(confrelid::regclass::text, '.', 2)
ELSE confrelid::regclass::text
END AS reference_table,
af.attname AS reference_column,
pg_get_constraintdef(c.oid) as fk_def
FROM
pg_constraint AS c
JOIN
pg_attribute AS a ON a.attnum = ANY(c.conkey) AND a.attrelid = c.conrelid
JOIN
pg_class AS cl ON cl.oid = c.conrelid
JOIN
pg_namespace AS n ON n.oid = cl.relnamespace
JOIN
pg_attribute AS af ON af.attnum = ANY(c.confkey) AND af.attrelid = c.confrelid
JOIN
pg_class AS clf ON clf.oid = c.confrelid
JOIN
pg_namespace AS nr ON nr.oid = clf.relnamespace
WHERE
c.contype = 'f'
AND connamespace::regnamespace::text NOT IN ('information_schema', 'pg_catalog')
AND connamespace::regnamespace::text NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')
) AS x
), pk_info AS (
SELECT array_to_string(array_agg(CONCAT('{\"schema\":\"', replace(schema_name, '\"', ''), '\"',
',\"table\":\"', replace(pk_table, '\"', ''), '\"',
',\"column\":\"', replace(pk_column, '\"', ''), '\"',
',\"pk_def\":\"', replace(pk_def, '\"', ''),
'\"}')), ',') AS pk_metadata
FROM (
SELECT connamespace::regnamespace::text AS schema_name,
CASE
WHEN strpos(conrelid::regclass::text, '.') > 0
THEN split_part(conrelid::regclass::text, '.', 2)
ELSE conrelid::regclass::text
END AS pk_table,
unnest(string_to_array(substring(pg_get_constraintdef(oid) FROM '\((.*?)\)'), ',')) AS pk_column,
pg_get_constraintdef(oid) as pk_def
FROM
pg_constraint
WHERE
contype = 'p'
AND connamespace::regnamespace::text NOT IN ('information_schema', 'pg_catalog')
AND connamespace::regnamespace::text NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')
) AS y
),
indexes_cols AS (
SELECT tnsp.nspname AS schema_name,
trel.relname AS table_name,
pg_relation_size('\"' || tnsp.nspname || '\".' || '\"' || irel.relname || '\"') AS index_size,
irel.relname AS index_name,
am.amname AS index_type,
a.attname AS col_name,
(CASE WHEN i.indisunique = TRUE THEN 'true' ELSE 'false' END) AS is_unique,
irel.reltuples AS cardinality,
1 + Array_position(i.indkey, a.attnum) AS column_position,
CASE o.OPTION & 1 WHEN 1 THEN 'DESC' ELSE 'ASC' END AS direction,
CASE WHEN indpred IS NOT NULL THEN 'true' ELSE 'false' END AS is_partial_index
FROM pg_index AS i
JOIN pg_class AS trel ON trel.oid = i.indrelid
JOIN pg_namespace AS tnsp ON trel.relnamespace = tnsp.oid
JOIN pg_class AS irel ON irel.oid = i.indexrelid
JOIN pg_am AS am ON irel.relam = am.oid
CROSS JOIN LATERAL unnest (i.indkey)
WITH ORDINALITY AS c (colnum, ordinality) LEFT JOIN LATERAL unnest (i.indoption)
WITH ORDINALITY AS o (option, ordinality)
ON c.ordinality = o.ordinality JOIN pg_attribute AS a ON trel.oid = a.attrelid AND a.attnum = c.colnum
WHERE tnsp.nspname NOT LIKE 'pg_%'
GROUP BY tnsp.nspname, trel.relname, irel.relname, am.amname, i.indisunique, i.indexrelid, irel.reltuples, a.attname, Array_position(i.indkey, a.attnum), o.OPTION, i.indpred
),
cols AS (
SELECT array_to_string(array_agg(CONCAT('{\"schema\":\"', cols.table_schema,
'\",\"table\":\"', cols.table_name,
'\",\"name\":\"', cols.column_name,
'\",\"ordinal_position\":\"', cols.ordinal_position,
'\",\"type\":\"', LOWER(replace(cols.data_type, '\"', '')),
'\",\"character_maximum_length\":\"', COALESCE(cols.character_maximum_length::text, 'null'),
'\",\"precision\":',
CASE
WHEN cols.data_type = 'numeric' OR cols.data_type = 'decimal'
THEN CONCAT('{\"precision\":', COALESCE(cols.numeric_precision::text, 'null'),
',\"scale\":', COALESCE(cols.numeric_scale::text, 'null'), '}')
ELSE 'null'
END,
',\"nullable\":', CASE WHEN (cols.IS_NULLABLE = 'YES') THEN 'true' ELSE 'false' END,
',\"default\":\"', COALESCE(replace(replace(cols.column_default, '\"', '\\\"'), '\\x', '\\\\x'), ''),
'\",\"collation\":\"', COALESCE(cols.COLLATION_NAME, ''),
'\",\"comment\":\"', COALESCE(replace(replace(dsc.description, '\"', '\\\"'), '\\x', '\\\\x'), ''),
'\"}')), ',') AS cols_metadata
FROM information_schema.columns cols
LEFT JOIN pg_catalog.pg_class c
ON c.relname = cols.table_name
JOIN pg_catalog.pg_namespace n
ON n.oid = c.relnamespace AND n.nspname = cols.table_schema
LEFT JOIN pg_catalog.pg_description dsc ON dsc.objoid = c.oid
AND dsc.objsubid = cols.ordinal_position
WHERE cols.table_schema NOT IN ('information_schema', 'pg_catalog')
AND cols.table_schema NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')
), indexes_metadata AS (
SELECT array_to_string(array_agg(CONCAT('{\"schema\":\"', schema_name,
'\",\"table\":\"', table_name,
'\",\"name\":\"', index_name,
'\",\"column\":\"', replace(col_name :: TEXT, '\"', E'\"'),
'\",\"index_type\":\"', index_type,
'\",\"cardinality\":', cardinality,
',\"size\":', index_size,
',\"unique\":', is_unique,
',\"is_partial_index\":', is_partial_index,
',\"column_position\":', column_position,
',\"direction\":\"', LOWER(direction),
'\"}')), ',') AS indexes_metadata
FROM indexes_cols x
WHERE schema_name NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')
), tbls AS (
SELECT array_to_string(array_agg(CONCAT('{',
'\"schema\":\"', tbls.TABLE_SCHEMA, '\",',
'\"table\":\"', tbls.TABLE_NAME, '\",',
'\"rows\":', COALESCE((SELECT s.n_live_tup
FROM pg_stat_user_tables s
WHERE tbls.TABLE_SCHEMA = s.schemaname AND tbls.TABLE_NAME = s.relname),
0), ', \"type\":\"', tbls.TABLE_TYPE, '\",', '\"engine\":\"\",', '\"collation\":\"\",',
'\"comment\":\"', COALESCE(replace(replace(dsc.description, '\"', '\\\"'), '\\x', '\\\\x'), ''),
'\"}'
)),
',') AS tbls_metadata
FROM information_schema.tables tbls
LEFT JOIN pg_catalog.pg_class c ON c.relname = tbls.TABLE_NAME
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
AND n.nspname = tbls.TABLE_SCHEMA
LEFT JOIN pg_catalog.pg_description dsc ON dsc.objoid = c.oid
AND dsc.objsubid = 0
WHERE tbls.TABLE_SCHEMA NOT IN ('information_schema', 'pg_catalog')
AND tbls.table_schema NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')
), config AS (
SELECT array_to_string(
array_agg(CONCAT('{\"name\":\"', conf.name, '\",\"value\":\"', replace(conf.setting, '\"', E'\"'), '\"}')),
',') AS config_metadata
FROM pg_settings conf
), views AS (
SELECT array_to_string(array_agg(CONCAT('{\"schema\":\"', views.schemaname,
'\",\"view_name\":\"', viewname,
'\",\"view_definition\":\"', encode(convert_to(REPLACE(definition, '\"', '\\\"'), 'UTF8'), 'base64'),
'\"}')),
',') AS views_metadata
FROM pg_views views
WHERE views.schemaname NOT IN ('information_schema', 'pg_catalog')
AND views.schemaname NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')
)
SELECT CONCAT('{ \"fk_info\": [', COALESCE(fk_metadata, ''),
'], \"pk_info\": [', COALESCE(pk_metadata, ''),
'], \"columns\": [', COALESCE(cols_metadata, ''),
'], \"indexes\": [', COALESCE(indexes_metadata, ''),
'], \"tables\":[', COALESCE(tbls_metadata, ''),
'], \"views\":[', COALESCE(views_metadata, ''),
'], \"database_name\": \"', CURRENT_DATABASE(), '', '\", \"version\": \"', '',
'\"}') AS metadata_json_to_import
FROM fk_info_supabase, pk_info, cols, indexes_metadata, tbls, config, views;
" -t -A | pbcopy; LG='\033[0;32m'; NC='\033[0m'; echo "You got the resultset ($(pbpaste | wc -c | xargs) characters) in Copy/Paste. ${LG}Go back & paste in ChartDB :)${NC}";
```
```bash Timescale theme={null}
psql -h HOST_NAME -p PORT -U USER_NAME -d DATABASE_NAME -c "
/* Timescale edition */
WITH fk_info_timescale AS (
SELECT array_to_string(array_agg(CONCAT('{\"schema\":\"', replace(schema_name, '\"', ''), '\"',
',\"table\":\"', replace(table_name::text, '\"', ''), '\"',
',\"column\":\"', replace(fk_column::text, '\"', ''), '\"',
',\"foreign_key_name\":\"', foreign_key_name, '\"',
',\"reference_schema\":\"', COALESCE(reference_schema, 'public'), '\"',
',\"reference_table\":\"', reference_table, '\"',
',\"reference_column\":\"', reference_column, '\"',
',\"fk_def\":\"', replace(fk_def, '\"', ''),
'\"}')), ',') as fk_metadata
FROM (
SELECT c.conname AS foreign_key_name,
n.nspname AS schema_name,
CASE
WHEN position('.' in conrelid::regclass::text) > 0
THEN split_part(conrelid::regclass::text, '.', 2)
ELSE conrelid::regclass::text
END AS table_name,
a.attname AS fk_column,
nr.nspname AS reference_schema,
CASE
WHEN position('.' in confrelid::regclass::text) > 0
THEN split_part(confrelid::regclass::text, '.', 2)
ELSE confrelid::regclass::text
END AS reference_table,
af.attname AS reference_column,
pg_get_constraintdef(c.oid) as fk_def
FROM
pg_constraint AS c
JOIN
pg_attribute AS a ON a.attnum = ANY(c.conkey) AND a.attrelid = c.conrelid
JOIN
pg_class AS cl ON cl.oid = c.conrelid
JOIN
pg_namespace AS n ON n.oid = cl.relnamespace
JOIN
pg_attribute AS af ON af.attnum = ANY(c.confkey) AND af.attrelid = c.confrelid
JOIN
pg_class AS clf ON clf.oid = c.confrelid
JOIN
pg_namespace AS nr ON nr.oid = clf.relnamespace
WHERE
c.contype = 'f'
AND connamespace::regnamespace::text NOT IN ('information_schema', 'pg_catalog')
AND connamespace::regnamespace::text !~ '^(timescaledb_|_timescaledb_)'
) AS x
), pk_info AS (
SELECT array_to_string(array_agg(CONCAT('{\"schema\":\"', replace(schema_name, '\"', ''), '\"',
',\"table\":\"', replace(pk_table, '\"', ''), '\"',
',\"column\":\"', replace(pk_column, '\"', ''), '\"',
',\"pk_def\":\"', replace(pk_def, '\"', ''),
'\"}')), ',') AS pk_metadata
FROM (
SELECT connamespace::regnamespace::text AS schema_name,
CASE
WHEN strpos(conrelid::regclass::text, '.') > 0
THEN split_part(conrelid::regclass::text, '.', 2)
ELSE conrelid::regclass::text
END AS pk_table,
unnest(string_to_array(substring(pg_get_constraintdef(oid) FROM '\((.*?)\)'), ',')) AS pk_column,
pg_get_constraintdef(oid) as pk_def
FROM
pg_constraint
WHERE
contype = 'p'
AND connamespace::regnamespace::text NOT IN ('information_schema', 'pg_catalog')
AND connamespace::regnamespace::text !~ '^(timescaledb_|_timescaledb_)'
) AS y
),
indexes_cols AS (
SELECT tnsp.nspname AS schema_name,
trel.relname AS table_name,
pg_relation_size('\"' || tnsp.nspname || '\".' || '\"' || irel.relname || '\"') AS index_size,
irel.relname AS index_name,
am.amname AS index_type,
a.attname AS col_name,
(CASE WHEN i.indisunique = TRUE THEN 'true' ELSE 'false' END) AS is_unique,
irel.reltuples AS cardinality,
1 + Array_position(i.indkey, a.attnum) AS column_position,
CASE o.OPTION & 1 WHEN 1 THEN 'DESC' ELSE 'ASC' END AS direction,
CASE WHEN indpred IS NOT NULL THEN 'true' ELSE 'false' END AS is_partial_index
FROM pg_index AS i
JOIN pg_class AS trel ON trel.oid = i.indrelid
JOIN pg_namespace AS tnsp ON trel.relnamespace = tnsp.oid
JOIN pg_class AS irel ON irel.oid = i.indexrelid
JOIN pg_am AS am ON irel.relam = am.oid
CROSS JOIN LATERAL unnest (i.indkey)
WITH ORDINALITY AS c (colnum, ordinality) LEFT JOIN LATERAL unnest (i.indoption)
WITH ORDINALITY AS o (option, ordinality)
ON c.ordinality = o.ordinality JOIN pg_attribute AS a ON trel.oid = a.attrelid AND a.attnum = c.colnum
WHERE tnsp.nspname NOT LIKE 'pg_%'
GROUP BY tnsp.nspname, trel.relname, irel.relname, am.amname, i.indisunique, i.indexrelid, irel.reltuples, a.attname, Array_position(i.indkey, a.attnum), o.OPTION, i.indpred
),
cols AS (
SELECT array_to_string(array_agg(CONCAT('{\"schema\":\"', cols.table_schema,
'\",\"table\":\"', cols.table_name,
'\",\"name\":\"', cols.column_name,
'\",\"ordinal_position\":\"', cols.ordinal_position,
'\",\"type\":\"', LOWER(replace(cols.data_type, '\"', '')),
'\",\"character_maximum_length\":\"', COALESCE(cols.character_maximum_length::text, 'null'),
'\",\"precision\":',
CASE
WHEN cols.data_type = 'numeric' OR cols.data_type = 'decimal'
THEN CONCAT('{\"precision\":', COALESCE(cols.numeric_precision::text, 'null'),
',\"scale\":', COALESCE(cols.numeric_scale::text, 'null'), '}')
ELSE 'null'
END,
',\"nullable\":', CASE WHEN (cols.IS_NULLABLE = 'YES') THEN 'true' ELSE 'false' END,
',\"default\":\"', COALESCE(replace(replace(cols.column_default, '\"', '\\\"'), '\\x', '\\\\x'), ''),
'\",\"collation\":\"', COALESCE(cols.COLLATION_NAME, ''),
'\",\"comment\":\"', COALESCE(replace(replace(dsc.description, '\"', '\\\"'), '\\x', '\\\\x'), ''),
'\"}')), ',') AS cols_metadata
FROM information_schema.columns cols
LEFT JOIN pg_catalog.pg_class c
ON c.relname = cols.table_name
JOIN pg_catalog.pg_namespace n
ON n.oid = c.relnamespace AND n.nspname = cols.table_schema
LEFT JOIN pg_catalog.pg_description dsc ON dsc.objoid = c.oid
AND dsc.objsubid = cols.ordinal_position
WHERE cols.table_schema NOT IN ('information_schema', 'pg_catalog')
AND cols.table_schema !~ '^(timescaledb_|_timescaledb_)'
AND cols.table_name !~ '^(pg_stat_)'
), indexes_metadata AS (
SELECT array_to_string(array_agg(CONCAT('{\"schema\":\"', schema_name,
'\",\"table\":\"', table_name,
'\",\"name\":\"', index_name,
'\",\"column\":\"', replace(col_name :: TEXT, '\"', E'\"'),
'\",\"index_type\":\"', index_type,
'\",\"cardinality\":', cardinality,
',\"size\":', index_size,
',\"unique\":', is_unique,
',\"is_partial_index\":', is_partial_index,
',\"column_position\":', column_position,
',\"direction\":\"', LOWER(direction),
'\"}')), ',') AS indexes_metadata
FROM indexes_cols x
WHERE schema_name !~ '^(timescaledb_|_timescaledb_)'
), tbls AS (
SELECT array_to_string(array_agg(CONCAT('{',
'\"schema\":\"', tbls.TABLE_SCHEMA, '\",',
'\"table\":\"', tbls.TABLE_NAME, '\",',
'\"rows\":', COALESCE((SELECT s.n_live_tup
FROM pg_stat_user_tables s
WHERE tbls.TABLE_SCHEMA = s.schemaname AND tbls.TABLE_NAME = s.relname),
0), ', \"type\":\"', tbls.TABLE_TYPE, '\",', '\"engine\":\"\",', '\"collation\":\"\",',
'\"comment\":\"', COALESCE(replace(replace(dsc.description, '\"', '\\\"'), '\\x', '\\\\x'), ''),
'\"}'
)),
',') AS tbls_metadata
FROM information_schema.tables tbls
LEFT JOIN pg_catalog.pg_class c ON c.relname = tbls.TABLE_NAME
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
AND n.nspname = tbls.TABLE_SCHEMA
LEFT JOIN pg_catalog.pg_description dsc ON dsc.objoid = c.oid
AND dsc.objsubid = 0
WHERE tbls.TABLE_SCHEMA NOT IN ('information_schema', 'pg_catalog')
AND tbls.table_schema !~ '^(timescaledb_|_timescaledb_)'
AND tbls.table_name !~ '^(pg_stat_)'
), config AS (
SELECT array_to_string(
array_agg(CONCAT('{\"name\":\"', conf.name, '\",\"value\":\"', replace(conf.setting, '\"', E'\"'), '\"}')),
',') AS config_metadata
FROM pg_settings conf
), views AS (
SELECT array_to_string(array_agg(CONCAT('{\"schema\":\"', views.schemaname,
'\",\"view_name\":\"', viewname,
'\",\"view_definition\":\"', encode(convert_to(REPLACE(definition, '\"', '\\\"'), 'UTF8'), 'base64'),
'\"}')),
',') AS views_metadata
FROM pg_views views
WHERE views.schemaname NOT IN ('information_schema', 'pg_catalog')
AND views.schemaname !~ '^(timescaledb_|_timescaledb_)'
)
SELECT CONCAT('{ \"fk_info\": [', COALESCE(fk_metadata, ''),
'], \"pk_info\": [', COALESCE(pk_metadata, ''),
'], \"columns\": [', COALESCE(cols_metadata, ''),
'], \"indexes\": [', COALESCE(indexes_metadata, ''),
'], \"tables\":[', COALESCE(tbls_metadata, ''),
'], \"views\":[', COALESCE(views_metadata, ''),
'], \"database_name\": \"', CURRENT_DATABASE(), '', '\", \"version\": \"', '',
'\"}') AS metadata_json_to_import
FROM fk_info_timescale, pk_info, cols, indexes_metadata, tbls, config, views;
" -t -A | pbcopy; LG='\033[0;32m'; NC='\033[0m'; echo "You got the resultset ($(pbpaste | wc -c | xargs) characters) in Copy/Paste. ${LG}Go back & paste in ChartDB :)${NC}";
```
## Troubleshooting
Find solutions for frequently encountered import problems and their resolutions
# Import SQL Server
Source: https://docs.chartdb.io/docs/import/sql-server
Import your SQL Server database into ChartDB
ChartDB never stores or accesses your data - the import query only retrieves schema metadata.
## Quick Start
1. Click **File** > **New** in the ChartDB editor
2. Select **Sql Server** from the database options
Select your Sql Server edition:
* **Regular** - Standard Sql Server installations
* **V2016 and below** - Sql Server 2016 and below installations
Execute the provided query using either:
* Your database client (e.g., Sql Server Workbench, DBeaver)
* Direct Sql Server connection
1. Copy the JSON output from the query
2. Paste into ChartDB's import field
3. Click **Import** to generate your diagram
## Import Methods
```sql Sql Server theme={null}
-- Regular Sql Server Database Import Query
WITH fk_info AS (
SELECT
JSON_QUERY(
'[' + STRING_AGG(
CONVERT(nvarchar(max),
JSON_QUERY(N'{"schema": "' + COALESCE(REPLACE(tp_schema.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "table": "' + COALESCE(REPLACE(tp.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "column": "' + COALESCE(REPLACE(cp.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "foreign_key_name": "' + COALESCE(REPLACE(fk.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "reference_schema": "' + COALESCE(REPLACE(tr_schema.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "reference_table": "' + COALESCE(REPLACE(tr.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "reference_column": "' + COALESCE(REPLACE(cr.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "fk_def": "FOREIGN KEY (' + COALESCE(REPLACE(cp.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
') REFERENCES ' + COALESCE(REPLACE(tr.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'(' + COALESCE(REPLACE(cr.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
') ON DELETE ' + fk.delete_referential_action_desc COLLATE SQL_Latin1_General_CP1_CI_AS +
' ON UPDATE ' + fk.update_referential_action_desc COLLATE SQL_Latin1_General_CP1_CI_AS + '"}')
), ','
) + N']'
) AS all_fks_json
FROM sys.foreign_keys AS fk
JOIN sys.foreign_key_columns AS fkc ON fk.object_id = fkc.constraint_object_id
JOIN sys.tables AS tp ON fkc.parent_object_id = tp.object_id
JOIN sys.schemas AS tp_schema ON tp.schema_id = tp_schema.schema_id
JOIN sys.columns AS cp ON fkc.parent_object_id = cp.object_id AND fkc.parent_column_id = cp.column_id
JOIN sys.tables AS tr ON fkc.referenced_object_id = tr.object_id
JOIN sys.schemas AS tr_schema ON tr.schema_id = tr_schema.schema_id
JOIN sys.columns AS cr ON fkc.referenced_object_id = cr.object_id AND fkc.referenced_column_id = cr.column_id
), pk_info AS (
SELECT
JSON_QUERY(
'[' + STRING_AGG(
CONVERT(nvarchar(max),
JSON_QUERY(N'{"schema": "' + COALESCE(REPLACE(pk.TABLE_SCHEMA, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "table": "' + COALESCE(REPLACE(pk.TABLE_NAME, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "column": "' + COALESCE(REPLACE(pk.COLUMN_NAME, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "pk_def": "PRIMARY KEY (' + pk.COLUMN_NAME COLLATE SQL_Latin1_General_CP1_CI_AS + ')"}')
), ','
) + N']'
) AS all_pks_json
FROM
(
SELECT
kcu.TABLE_SCHEMA,
kcu.TABLE_NAME,
kcu.COLUMN_NAME
FROM
INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
JOIN
INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
ON kcu.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
AND kcu.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
WHERE
tc.CONSTRAINT_TYPE = 'PRIMARY KEY'
) pk
),
cols AS (
SELECT
JSON_QUERY(
'[' + STRING_AGG(
CONVERT(nvarchar(max),
JSON_QUERY('{"schema": "' + COALESCE(REPLACE(cols.TABLE_SCHEMA, '"', ''), '') +
'", "table": "' + COALESCE(REPLACE(cols.TABLE_NAME, '"', ''), '') +
'", "name": "' + COALESCE(REPLACE(cols.COLUMN_NAME, '"', ''), '') +
'", "ordinal_position": "' + CAST(cols.ORDINAL_POSITION AS NVARCHAR(MAX)) +
'", "type": "' + LOWER(cols.DATA_TYPE) +
'", "character_maximum_length": "' +
COALESCE(CAST(cols.CHARACTER_MAXIMUM_LENGTH AS NVARCHAR(MAX)), 'null') +
'", "precision": ' +
CASE
WHEN cols.DATA_TYPE IN ('numeric', 'decimal') THEN
CONCAT('{"precision":', COALESCE(CAST(cols.NUMERIC_PRECISION AS NVARCHAR(MAX)), 'null'),
',"scale":', COALESCE(CAST(cols.NUMERIC_SCALE AS NVARCHAR(MAX)), 'null'), '}')
ELSE
'null'
END +
', "nullable": ' +
CASE WHEN cols.IS_NULLABLE = 'YES' THEN 'true' ELSE 'false' END +
', "default": "' +
COALESCE(REPLACE(CAST(cols.COLUMN_DEFAULT AS NVARCHAR(MAX)), '"', '\"'), '') +
'", "collation": "' +
COALESCE(cols.COLLATION_NAME, '') +
'"}')
), ','
) + ']'
) AS all_columns_json
FROM
INFORMATION_SCHEMA.COLUMNS cols
WHERE
cols.TABLE_CATALOG = DB_NAME()
),
indexes AS (
SELECT
'[' + STRING_AGG(
CONVERT(nvarchar(max),
JSON_QUERY(
N'{"schema": "' + COALESCE(REPLACE(s.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "table": "' + COALESCE(REPLACE(t.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "name": "' + COALESCE(REPLACE(i.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "column": "' + COALESCE(REPLACE(c.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "index_type": "' + LOWER(i.type_desc) COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "unique": ' + CASE WHEN i.is_unique = 1 THEN 'true' ELSE 'false' END +
', "direction": "' + CASE WHEN ic.is_descending_key = 1 THEN 'desc' ELSE 'asc' END COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "column_position": ' + CAST(ic.key_ordinal AS nvarchar(max)) + N'}'
)
), ','
) + N']' AS all_indexes_json
FROM
sys.indexes i
JOIN
sys.tables t ON i.object_id = t.object_id
JOIN
sys.schemas s ON t.schema_id = s.schema_id
JOIN
sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
JOIN
sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
WHERE
s.name LIKE '%'
AND i.name IS NOT NULL
),
tbls AS (
SELECT
'[' + STRING_AGG(
CONVERT(nvarchar(max),
JSON_QUERY(
N'{"schema": "' + COALESCE(REPLACE(aggregated.schema_name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "table": "' + COALESCE(REPLACE(aggregated.table_name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "row_count": "' + CAST(aggregated.row_count AS NVARCHAR(MAX)) +
'", "table_type": "' + aggregated.table_type COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "creation_date": "' + CONVERT(NVARCHAR(MAX), aggregated.creation_date, 120) + '"}'
)
), ','
) + N']' AS all_tables_json
FROM
(
-- Select from tables
SELECT
COALESCE(REPLACE(s.name, '"', ''), '') AS schema_name,
COALESCE(REPLACE(t.name, '"', ''), '') AS table_name,
SUM(p.rows) AS row_count,
t.type_desc AS table_type,
t.create_date AS creation_date
FROM
sys.tables t
JOIN
sys.schemas s ON t.schema_id = s.schema_id
JOIN
sys.partitions p ON t.object_id = p.object_id AND p.index_id IN (0, 1)
WHERE
s.name LIKE '%'
GROUP BY
s.name, t.name, t.type_desc, t.create_date
UNION ALL
-- Select from views
SELECT
COALESCE(REPLACE(s.name, '"', ''), '') AS table_name,
COALESCE(REPLACE(v.name, '"', ''), '') AS object_name,
0 AS row_count, -- Views don't have row counts
'VIEW' AS table_type,
v.create_date AS creation_date
FROM
sys.views v
JOIN
sys.schemas s ON v.schema_id = s.schema_id
WHERE
s.name LIKE '%'
) AS aggregated
),
views AS (
SELECT
'[' + STRING_AGG(
CONVERT(nvarchar(max),
JSON_QUERY(
N'{"schema": "' + STRING_ESCAPE(COALESCE(s.name, ''), 'json') +
'", "view_name": "' + STRING_ESCAPE(COALESCE(v.name, ''), 'json') +
'", "view_definition": "' +
STRING_ESCAPE(
CAST(
'' AS XML
).value(
'xs:base64Binary(sql:column("DefinitionBinary"))',
'VARCHAR(MAX)'
), 'json') +
'"}'
)
), ','
) + N']' AS all_views_json
FROM
sys.views v
JOIN
sys.schemas s ON v.schema_id = s.schema_id
JOIN
sys.sql_modules m ON v.object_id = m.object_id
CROSS APPLY
(SELECT CONVERT(VARBINARY(MAX), m.definition) AS DefinitionBinary) AS bin
WHERE
s.name LIKE '%'
)
SELECT JSON_QUERY(
N'{"fk_info": ' + ISNULL((SELECT cast(all_fks_json as nvarchar(max)) FROM fk_info), N'[]') +
', "pk_info": ' + ISNULL((SELECT cast(all_pks_json as nvarchar(max)) FROM pk_info), N'[]') +
', "columns": ' + ISNULL((SELECT cast(all_columns_json as nvarchar(max)) FROM cols), N'[]') +
', "indexes": ' + ISNULL((SELECT cast(all_indexes_json as nvarchar(max)) FROM indexes), N'[]') +
', "tables": ' + ISNULL((SELECT cast(all_tables_json as nvarchar(max)) FROM tbls), N'[]') +
', "views": ' + ISNULL((SELECT cast(all_views_json as nvarchar(max)) FROM views), N'[]') +
', "database_name": "' + DB_NAME() + '"' +
', "version": ""}'
) AS metadata_json_to_import;
```
```sql Sql Server V2016 and below theme={null}
-- Sql Server 2016 and below Database Import Query
WITH fk_info AS (
SELECT
JSON_QUERY(
'[' + ISNULL(
STUFF((
SELECT ',' +
CONVERT(nvarchar(max),
JSON_QUERY(N'{"schema": "' + COALESCE(REPLACE(tp_schema.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "table": "' + COALESCE(REPLACE(tp.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "column": "' + COALESCE(REPLACE(cp.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "foreign_key_name": "' + COALESCE(REPLACE(fk.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "reference_schema": "' + COALESCE(REPLACE(tr_schema.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "reference_table": "' + COALESCE(REPLACE(tr.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "reference_column": "' + COALESCE(REPLACE(cr.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "fk_def": "FOREIGN KEY (' + COALESCE(REPLACE(cp.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
') REFERENCES ' + COALESCE(REPLACE(tr.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'(' + COALESCE(REPLACE(cr.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
') ON DELETE ' + fk.delete_referential_action_desc COLLATE SQL_Latin1_General_CP1_CI_AS +
' ON UPDATE ' + fk.update_referential_action_desc COLLATE SQL_Latin1_General_CP1_CI_AS + '"}')
)
FROM
sys.foreign_keys AS fk
JOIN
sys.foreign_key_columns AS fkc ON fk.object_id = fkc.constraint_object_id
JOIN
sys.tables AS tp ON fkc.parent_object_id = tp.object_id
JOIN
sys.schemas AS tp_schema ON tp.schema_id = tp_schema.schema_id
JOIN
sys.columns AS cp ON fkc.parent_object_id = cp.object_id AND fkc.parent_column_id = cp.column_id
JOIN
sys.tables AS tr ON fkc.referenced_object_id = tr.object_id
JOIN
sys.schemas AS tr_schema ON tr.schema_id = tr_schema.schema_id
JOIN
sys.columns AS cr ON fkc.referenced_object_id = cr.object_id AND fkc.referenced_column_id = cr.column_id
FOR XML PATH('')
), 1, 1, ''), '')
+ N']'
) AS all_fks_json
),
pk_info AS (
SELECT
JSON_QUERY(
'[' + ISNULL(
STUFF((
SELECT ',' +
CONVERT(nvarchar(max),
JSON_QUERY(N'{"schema": "' + COALESCE(REPLACE(pk.TABLE_SCHEMA, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "table": "' + COALESCE(REPLACE(pk.TABLE_NAME, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "column": "' + COALESCE(REPLACE(pk.COLUMN_NAME, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "pk_def": "PRIMARY KEY (' + pk.COLUMN_NAME COLLATE SQL_Latin1_General_CP1_CI_AS + ')"}')
)
FROM
(
SELECT
kcu.TABLE_SCHEMA,
kcu.TABLE_NAME,
kcu.COLUMN_NAME
FROM
INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
JOIN
INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
ON kcu.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
AND kcu.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
WHERE
tc.CONSTRAINT_TYPE = 'PRIMARY KEY'
) pk
FOR XML PATH('')
), 1, 1, ''), '')
+ N']'
) AS all_pks_json
),
cols AS (
SELECT
JSON_QUERY(
'[' + ISNULL(
STUFF((
SELECT ',' +
CONVERT(nvarchar(max),
JSON_QUERY('{"schema": "' + COALESCE(REPLACE(cols.TABLE_SCHEMA, '"', ''), '') +
'", "table": "' + COALESCE(REPLACE(cols.TABLE_NAME, '"', ''), '') +
'", "name": "' + COALESCE(REPLACE(cols.COLUMN_NAME, '"', ''), '') +
'", "ordinal_position": "' + CAST(cols.ORDINAL_POSITION AS NVARCHAR(MAX)) +
'", "type": "' + LOWER(cols.DATA_TYPE) +
'", "character_maximum_length": "' +
COALESCE(CAST(cols.CHARACTER_MAXIMUM_LENGTH AS NVARCHAR(MAX)), 'null') +
'", "precision": ' +
CASE
WHEN cols.DATA_TYPE IN ('numeric', 'decimal') THEN
CONCAT('{"precision":', COALESCE(CAST(cols.NUMERIC_PRECISION AS NVARCHAR(MAX)), 'null'),
',"scale":', COALESCE(CAST(cols.NUMERIC_SCALE AS NVARCHAR(MAX)), 'null'), '}')
ELSE
'null'
END +
', "nullable": ' +
CASE WHEN cols.IS_NULLABLE = 'YES' THEN 'true' ELSE 'false' END +
', "default": "' +
COALESCE(REPLACE(CAST(cols.COLUMN_DEFAULT AS NVARCHAR(MAX)), '"', '"'), '') +
'", "collation": "' +
COALESCE(cols.COLLATION_NAME, '') +
'"}')
)
FROM
INFORMATION_SCHEMA.COLUMNS cols
WHERE
cols.TABLE_CATALOG = DB_NAME()
FOR XML PATH('')
), 1, 1, ''), '')
+ ']'
) AS all_columns_json
),
indexes AS (
SELECT
'[' + ISNULL(
STUFF((
SELECT ',' +
CONVERT(nvarchar(max),
JSON_QUERY(
N'{"schema": "' + COALESCE(REPLACE(s.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "table": "' + COALESCE(REPLACE(t.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "name": "' + COALESCE(REPLACE(i.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "column": "' + COALESCE(REPLACE(c.name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "index_type": "' + LOWER(i.type_desc) COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "unique": ' + CASE WHEN i.is_unique = 1 THEN 'true' ELSE 'false' END +
', "direction": "' + CASE WHEN ic.is_descending_key = 1 THEN 'desc' ELSE 'asc' END COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "column_position": ' + CAST(ic.key_ordinal AS nvarchar(max)) + N'}'
)
)
FROM
sys.indexes i
JOIN
sys.tables t ON i.object_id = t.object_id
JOIN
sys.schemas s ON t.schema_id = s.schema_id
JOIN
sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
JOIN
sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
WHERE
s.name LIKE '%'
AND i.name IS NOT NULL
FOR XML PATH('')
), 1, 1, ''), '')
+ N']' AS all_indexes_json
),
tbls AS (
SELECT
'[' + ISNULL(
STUFF((
SELECT ',' +
CONVERT(nvarchar(max),
JSON_QUERY(
N'{"schema": "' + COALESCE(REPLACE(aggregated.schema_name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "table": "' + COALESCE(REPLACE(aggregated.object_name, '"', ''), '') COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "row_count": "' + CAST(aggregated.row_count AS NVARCHAR(MAX)) +
'", "object_type": "' + aggregated.object_type COLLATE SQL_Latin1_General_CP1_CI_AS +
'", "creation_date": "' + CONVERT(NVARCHAR(MAX), aggregated.creation_date, 120) + '"}'
)
)
FROM
(
-- Select from tables
SELECT
COALESCE(REPLACE(s.name, '"', ''), '') AS schema_name,
COALESCE(REPLACE(t.name, '"', ''), '') AS object_name,
SUM(p.rows) AS row_count,
t.type_desc AS object_type,
t.create_date AS creation_date
FROM
sys.tables t
JOIN
sys.schemas s ON t.schema_id = s.schema_id
JOIN
sys.partitions p ON t.object_id = p.object_id AND p.index_id IN (0, 1)
WHERE
s.name LIKE '%'
GROUP BY
s.name, t.name, t.type_desc, t.create_date
UNION ALL
-- Select from views
SELECT
COALESCE(REPLACE(s.name, '"', ''), '') AS schema_name,
COALESCE(REPLACE(v.name, '"', ''), '') AS object_name,
0 AS row_count, -- Views don't have row counts
'VIEW' AS object_type,
v.create_date AS creation_date
FROM
sys.views v
JOIN
sys.schemas s ON v.schema_id = s.schema_id
WHERE
s.name LIKE '%'
) AS aggregated
FOR XML PATH('')
), 1, 1, ''), '')
+ N']' AS all_objects_json
),
views AS (
SELECT
'[' +
(
SELECT
STUFF((
SELECT ',' + CONVERT(nvarchar(max),
JSON_QUERY(
N'{"schema": "' + COALESCE(REPLACE(s.name, '"', ''), '') +
'", "view_name": "' + COALESCE(REPLACE(v.name, '"', ''), '') +
'", "view_definition": "' +
CAST(
(
SELECT CAST(OBJECT_DEFINITION(v.object_id) AS VARBINARY(MAX)) FOR XML PATH('')
) AS NVARCHAR(MAX)
) + '"}'
)
)
FROM
sys.views v
JOIN
sys.schemas s ON v.schema_id = s.schema_id
WHERE
s.name LIKE '%'
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
) + ']' AS all_views_json
)
SELECT JSON_QUERY(
N'{"fk_info": ' + ISNULL((SELECT cast(all_fks_json as nvarchar(max)) FROM fk_info), N'[]') +
', "pk_info": ' + ISNULL((SELECT cast(all_pks_json as nvarchar(max)) FROM pk_info), N'[]') +
', "columns": ' + ISNULL((SELECT cast(all_columns_json as nvarchar(max)) FROM cols), N'[]') +
', "indexes": ' + ISNULL((SELECT cast(all_indexes_json as nvarchar(max)) FROM indexes), N'[]') +
', "tables": ' + ISNULL((SELECT cast(all_objects_json as nvarchar(max)) FROM tbls), N'[]') +
', "views": ' + ISNULL((SELECT cast(all_views_json as nvarchar(max)) FROM views), N'[]') +
', "database_name": "' + DB_NAME() + '"' +
', "version": ""}'
) AS metadata_json_to_import;
```
## Troubleshooting
Find solutions for frequently encountered import problems and their resolutions
# Import SQLite
Source: https://docs.chartdb.io/docs/import/sqllite
Import SQLite database schema into ChartDB
ChartDB never stores or accesses your data - the import query only retrieves schema metadata.
## Quick Start
1. Click **File** > **New** in the ChartDB editor
2. Select **SQlite** from the database options
Execute the provided query using either:
* Your database client (e.g., DBeaver, CLI command)
* Direct SQlite connection
1. Copy the JSON output from the query
2. Paste into ChartDB's import field
3. Click **Import** to generate your diagram
## Import Methods
```sql SQlite theme={null}
-- Regular SQlite Database Import Query
WITH fk_info AS (
SELECT
json_group_array(
json_object(
'schema', '', -- SQLite does not have schemas
'table', m.name,
'column', fk."from",
'foreign_key_name',
'fk_' || m.name || '_' || fk."from" || '_' || fk."table" || '_' || fk."to", -- Generated foreign key name
'reference_schema', '', -- SQLite does not have schemas
'reference_table', fk."table",
'reference_column', fk."to",
'fk_def',
'FOREIGN KEY (' || fk."from" || ') REFERENCES ' || fk."table" || '(' || fk."to" || ')' ||
' ON UPDATE ' || fk.on_update || ' ON DELETE ' || fk.on_delete
)
) AS fk_metadata
FROM
sqlite_master m
JOIN
pragma_foreign_key_list(m.name) fk
ON
m.type = 'table'
), pk_info AS (
SELECT
json_group_array(
json_object(
'schema', '', -- SQLite does not have schemas
'table', pk.table_name,
'field_count', pk.field_count,
'column', pk.pk_column,
'pk_def', 'PRIMARY KEY (' || pk.pk_column || ')'
)
) AS pk_metadata
FROM
(
SELECT
m.name AS table_name,
COUNT(p.name) AS field_count, -- Count of primary key columns
GROUP_CONCAT(p.name) AS pk_column -- Concatenated list of primary key columns
FROM
sqlite_master m
JOIN
pragma_table_info(m.name) p
ON
m.type = 'table' AND p.pk > 0
GROUP BY
m.name
) pk
), indexes_metadata AS (
SELECT
json_group_array(
json_object(
'schema', '', -- SQLite does not have schemas
'table', m.name,
'name', idx.name,
'column', ic.name,
'index_type', 'B-TREE', -- SQLite uses B-Trees for indexing
'cardinality', '', -- SQLite does not provide cardinality
'size', '', -- SQLite does not provide index size
'unique', (CASE WHEN idx."unique" = 1 THEN 'true' ELSE 'false' END),
'direction', '', -- SQLite does not provide direction info
'column_position', ic.seqno + 1 -- Adding 1 to convert from zero-based to one-based index
)
) AS indexes_metadata
FROM
sqlite_master m
JOIN
pragma_index_list(m.name) idx
ON
m.type = 'table'
JOIN
pragma_index_info(idx.name) ic
), cols AS (
SELECT
json_group_array(
json_object(
'schema', '', -- SQLite does not have schemas
'table', m.name,
'name', p.name,
'type',
CASE
WHEN INSTR(LOWER(p.type), '(') > 0 THEN
SUBSTR(LOWER(p.type), 1, INSTR(LOWER(p.type), '(') - 1)
ELSE LOWER(p.type)
END,
'ordinal_position', p.cid,
'nullable', (CASE WHEN p."notnull" = 0 THEN 'true' ELSE 'false' END),
'collation', '',
'character_maximum_length',
CASE
WHEN LOWER(p.type) LIKE 'char%' OR LOWER(p.type) LIKE 'varchar%' THEN
CASE
WHEN INSTR(p.type, '(') > 0 THEN
REPLACE(SUBSTR(p.type, INSTR(p.type, '(') + 1, LENGTH(p.type) - INSTR(p.type, '(') - 1), ')', '')
ELSE 'null'
END
ELSE 'null'
END,
'precision',
CASE
WHEN LOWER(p.type) LIKE 'decimal%' OR LOWER(p.type) LIKE 'numeric%' THEN
CASE
WHEN instr(p.type, '(') > 0 THEN
json_object(
'precision', substr(p.type, instr(p.type, '(') + 1, instr(p.type, ',') - instr(p.type, '(') - 1),
'scale', substr(p.type, instr(p.type, ',') + 1, instr(p.type, ')') - instr(p.type, ',') - 1)
)
ELSE 'null'
END
ELSE 'null'
END,
'default', COALESCE(REPLACE(p.dflt_value, '"', '\"'), '')
)
) AS cols_metadata
FROM
sqlite_master m
JOIN
pragma_table_info(m.name) p
ON
m.type in ('table', 'view')
), tbls AS (
SELECT
json_group_array(
json_object(
'schema', '', -- SQLite does not have schemas
'table', m.name,
'rows', -1,
'type', 'table',
'engine', '', -- SQLite does not use storage engines
'collation', '' -- Collation information is not available
)
) AS tbls_metadata
FROM
sqlite_master m
WHERE
m.type in ('table', 'view')
), views AS (
SELECT
json_group_array(
json_object(
'schema', '',
'view_name', m.name
)
) AS views_metadata
FROM
sqlite_master m
WHERE
m.type = 'view'
)
SELECT
replace(replace(replace(
json_object(
'fk_info', (SELECT fk_metadata FROM fk_info),
'pk_info', (SELECT pk_metadata FROM pk_info),
'columns', (SELECT cols_metadata FROM cols),
'indexes', (SELECT indexes_metadata FROM indexes_metadata),
'tables', (SELECT tbls_metadata FROM tbls),
'views', (SELECT views_metadata FROM views),
'database_name', 'sqlite',
'version', sqlite_version()
),
'\"', '"'),'"[', '['), ']"', ']'
) AS metadata_json_to_import;
```
## Troubleshooting
Find solutions for frequently encountered import problems and their resolutions
# Import Supabase
Source: https://docs.chartdb.io/docs/import/supabase
Import your Supabase PostgreSQL database schema into ChartDB using SQL queries
**Want automatic sync?** Use the [Supabase Integration](/docs/integrations/supabase) for one-click OAuth authentication and automatic hourly schema sync.
[Supabase](https://supabase.com) is an open-source Firebase alternative that provides a PostgreSQL database with built-in authentication, storage, and real-time subscriptions. This guide covers the manual SQL import method for Supabase databases.
ChartDB never stores or accesses your data - the import query only retrieves schema metadata. The query automatically excludes Supabase system schemas (`auth`, `extensions`, `pgsodium`, `realtime`, `storage`, `vault`) to show only your application tables.
## Quick Start
1. Go to [ChartDB](https://app.chartdb.io/)
2. Click **File** > **New** in the editor
3. Select **PostgreSQL** from the database options
4. Choose **Supabase** as the edition
1. Log in to your [Supabase Dashboard](https://supabase.com/dashboard)
2. Select your project
3. Navigate to **SQL Editor** in the left sidebar
4. Click **New query**
Copy the import query from ChartDB and paste it into the Supabase SQL Editor. Click **Run** to execute.
1. After the query runs, you'll see a JSON result
2. Click on the result cell
3. Click **Copy cell content** or use the copy button
1. Go back to ChartDB
2. Paste the JSON into the import field
3. Click **Import** to generate your diagram
## Import Query
Run this query in your Supabase SQL Editor:
```sql theme={null}
-- Supabase PostgreSQL Database Import Query
WITH fk_info_supabase AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', replace(schema_name, '"', ''), '"',
',"table":"', replace(table_name::text, '"', ''), '"',
',"column":"', replace(fk_column::text, '"', ''), '"',
',"foreign_key_name":"', foreign_key_name, '"',
',"reference_schema":"', COALESCE(reference_schema, 'public'), '"',
',"reference_table":"', reference_table, '"',
',"reference_column":"', reference_column, '"',
',"fk_def":"', replace(fk_def, '"', ''),
'"}')), ',') as fk_metadata
FROM (
SELECT c.conname AS foreign_key_name,
n.nspname AS schema_name,
CASE
WHEN position('.' in conrelid::regclass::text) > 0
THEN split_part(conrelid::regclass::text, '.', 2)
ELSE conrelid::regclass::text
END AS table_name,
a.attname AS fk_column,
nr.nspname AS reference_schema,
CASE
WHEN position('.' in confrelid::regclass::text) > 0
THEN split_part(confrelid::regclass::text, '.', 2)
ELSE confrelid::regclass::text
END AS reference_table,
af.attname AS reference_column,
pg_get_constraintdef(c.oid) as fk_def
FROM
pg_constraint AS c
JOIN
pg_attribute AS a ON a.attnum = ANY(c.conkey) AND a.attrelid = c.conrelid
JOIN
pg_class AS cl ON cl.oid = c.conrelid
JOIN
pg_namespace AS n ON n.oid = cl.relnamespace
JOIN
pg_attribute AS af ON af.attnum = ANY(c.confkey) AND af.attrelid = c.confrelid
JOIN
pg_class AS clf ON clf.oid = c.confrelid
JOIN
pg_namespace AS nr ON nr.oid = clf.relnamespace
WHERE
c.contype = 'f'
AND connamespace::regnamespace::text NOT IN ('information_schema', 'pg_catalog')
AND connamespace::regnamespace::text NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')
) AS x
), pk_info AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', replace(schema_name, '"', ''), '"',
',"table":"', replace(pk_table, '"', ''), '"',
',"column":"', replace(pk_column, '"', ''), '"',
',"pk_def":"', replace(pk_def, '"', ''),
'"}')), ',') AS pk_metadata
FROM (
SELECT connamespace::regnamespace::text AS schema_name,
CASE
WHEN strpos(conrelid::regclass::text, '.') > 0
THEN split_part(conrelid::regclass::text, '.', 2)
ELSE conrelid::regclass::text
END AS pk_table,
unnest(string_to_array(substring(pg_get_constraintdef(oid) FROM '\((.*?)\)'), ',')) AS pk_column,
pg_get_constraintdef(oid) as pk_def
FROM
pg_constraint
WHERE
contype = 'p'
AND connamespace::regnamespace::text NOT IN ('information_schema', 'pg_catalog')
AND connamespace::regnamespace::text NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')
) AS y
),
indexes_cols AS (
SELECT tnsp.nspname AS schema_name,
trel.relname AS table_name,
pg_relation_size('"' || tnsp.nspname || '".' || '"' || irel.relname || '"') AS index_size,
irel.relname AS index_name,
am.amname AS index_type,
a.attname AS col_name,
(CASE WHEN i.indisunique = TRUE THEN 'true' ELSE 'false' END) AS is_unique,
irel.reltuples AS cardinality,
1 + Array_position(i.indkey, a.attnum) AS column_position,
CASE o.OPTION & 1 WHEN 1 THEN 'DESC' ELSE 'ASC' END AS direction,
CASE WHEN indpred IS NOT NULL THEN 'true' ELSE 'false' END AS is_partial_index
FROM pg_index AS i
JOIN pg_class AS trel ON trel.oid = i.indrelid
JOIN pg_namespace AS tnsp ON trel.relnamespace = tnsp.oid
JOIN pg_class AS irel ON irel.oid = i.indexrelid
JOIN pg_am AS am ON irel.relam = am.oid
CROSS JOIN LATERAL unnest (i.indkey)
WITH ORDINALITY AS c (colnum, ordinality) LEFT JOIN LATERAL unnest (i.indoption)
WITH ORDINALITY AS o (option, ordinality)
ON c.ordinality = o.ordinality JOIN pg_attribute AS a ON trel.oid = a.attrelid AND a.attnum = c.colnum
WHERE tnsp.nspname NOT LIKE 'pg_%'
GROUP BY tnsp.nspname, trel.relname, irel.relname, am.amname, i.indisunique, i.indexrelid, irel.reltuples, a.attname, Array_position(i.indkey, a.attnum), o.OPTION, i.indpred
),
cols AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', cols.table_schema,
'","table":"', cols.table_name,
'","name":"', cols.column_name,
'","ordinal_position":"', cols.ordinal_position,
'","type":"', LOWER(replace(cols.data_type, '"', '')),
'","character_maximum_length":"', COALESCE(cols.character_maximum_length::text, 'null'),
'","precision":',
CASE
WHEN cols.data_type = 'numeric' OR cols.data_type = 'decimal'
THEN CONCAT('{"precision":', COALESCE(cols.numeric_precision::text, 'null'),
',"scale":', COALESCE(cols.numeric_scale::text, 'null'), '}')
ELSE 'null'
END,
',"nullable":', CASE WHEN (cols.IS_NULLABLE = 'YES') THEN 'true' ELSE 'false' END,
',"default":"', COALESCE(replace(replace(cols.column_default, '"', '\"'), '\x', '\\x'), ''),
'","collation":"', COALESCE(cols.COLLATION_NAME, ''),
'","comment":"', COALESCE(replace(replace(dsc.description, '"', '\"'), '\x', '\\x'), ''),
'"}')), ',') AS cols_metadata
FROM information_schema.columns cols
LEFT JOIN pg_catalog.pg_class c
ON c.relname = cols.table_name
JOIN pg_catalog.pg_namespace n
ON n.oid = c.relnamespace AND n.nspname = cols.table_schema
LEFT JOIN pg_catalog.pg_description dsc ON dsc.objoid = c.oid
AND dsc.objsubid = cols.ordinal_position
WHERE cols.table_schema NOT IN ('information_schema', 'pg_catalog')
AND cols.table_schema NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')
), indexes_metadata AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', schema_name,
'","table":"', table_name,
'","name":"', index_name,
'","column":"', replace(col_name :: TEXT, '"', E'"'),
'","index_type":"', index_type,
'","cardinality":', cardinality,
',"size":', index_size,
',"unique":', is_unique,
',"is_partial_index":', is_partial_index,
',"column_position":', column_position,
',"direction":"', LOWER(direction),
'"}')), ',') AS indexes_metadata
FROM indexes_cols x
WHERE schema_name NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')
), tbls AS (
SELECT array_to_string(array_agg(CONCAT('{',
'"schema":"', tbls.TABLE_SCHEMA, '",',
'"table":"', tbls.TABLE_NAME, '",',
'"rows":', COALESCE((SELECT s.n_live_tup
FROM pg_stat_user_tables s
WHERE tbls.TABLE_SCHEMA = s.schemaname AND tbls.TABLE_NAME = s.relname),
0), ', "type":"', tbls.TABLE_TYPE, '",', '"engine":"",', '"collation":"",',
'"comment":"', COALESCE(replace(replace(dsc.description, '"', '\"'), '\x', '\\x'), ''),
'"}'
)),
',') AS tbls_metadata
FROM information_schema.tables tbls
LEFT JOIN pg_catalog.pg_class c ON c.relname = tbls.TABLE_NAME
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
AND n.nspname = tbls.TABLE_SCHEMA
LEFT JOIN pg_catalog.pg_description dsc ON dsc.objoid = c.oid
AND dsc.objsubid = 0
WHERE tbls.TABLE_SCHEMA NOT IN ('information_schema', 'pg_catalog')
AND tbls.table_schema NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')
), config AS (
SELECT array_to_string(
array_agg(CONCAT('{"name":"', conf.name, '","value":"', replace(conf.setting, '"', E'"'), '"}')),
',') AS config_metadata
FROM pg_settings conf
), views AS (
SELECT array_to_string(array_agg(CONCAT('{"schema":"', views.schemaname,
'","view_name":"', viewname,
'","view_definition":"', encode(convert_to(REPLACE(definition, '"', '\"'), 'UTF8'), 'base64'),
'"}')),
',') AS views_metadata
FROM pg_views views
WHERE views.schemaname NOT IN ('information_schema', 'pg_catalog')
AND views.schemaname NOT IN ('auth', 'extensions', 'pgsodium', 'realtime', 'storage', 'vault')
)
SELECT CONCAT('{ "fk_info": [', COALESCE(fk_metadata, ''),
'], "pk_info": [', COALESCE(pk_metadata, ''),
'], "columns": [', COALESCE(cols_metadata, ''),
'], "indexes": [', COALESCE(indexes_metadata, ''),
'], "tables":[', COALESCE(tbls_metadata, ''),
'], "views":[', COALESCE(views_metadata, ''),
'], "database_name": "', CURRENT_DATABASE(), '', '", "version": "', '',
'"}') AS metadata_json_to_import
FROM fk_info_supabase, pk_info, cols, indexes_metadata, tbls, config, views;
```
## Connection Details (Alternative Method)
If you prefer to connect using a database client like pgAdmin or DBeaver, you can find your Supabase connection credentials:
1. Go to your [Supabase Dashboard](https://supabase.com/dashboard)
2. Select your project
3. Click **Project Settings** (gear icon) in the sidebar
4. Navigate to **Database**
Under **Connection parameters**, you'll find:
* **Host**: `db.[project-ref].supabase.co`
* **Database name**: `postgres`
* **Port**: `5432`
* **User**: `postgres`
* **Password**: Your database password (set during project creation)
Make sure to use the **Direct connection** string, not the pooled connection. The direct connection is required for schema introspection queries.
You can also use the command line to import:
```bash theme={null}
psql "postgresql://postgres:[YOUR-PASSWORD]@db.[PROJECT-REF].supabase.co:5432/postgres" -c "
[PASTE IMPORT QUERY HERE]
" -t -A
```
## Excluded Schemas
The Supabase import query automatically excludes these internal schemas to keep your diagram focused on your application tables:
| Schema | Description |
| ------------ | ------------------------------ |
| `auth` | Supabase Authentication tables |
| `extensions` | PostgreSQL extensions |
| `pgsodium` | Encryption utilities |
| `realtime` | Real-time subscription system |
| `storage` | File storage system |
| `vault` | Secrets management |
If you need to visualize these system schemas, use the standard [PostgreSQL import](/docs/import/postgresql) query instead.
## Troubleshooting
Solutions for frequently encountered import problems
Full PostgreSQL documentation including alternative import methods
# Supabase Integration
Source: https://docs.chartdb.io/docs/integrations/supabase
Connect your Supabase database to ChartDB with OAuth authentication and automatic schema sync
ChartDB integrates directly with [Supabase](https://supabase.com) using OAuth authentication, allowing you to import and automatically sync your Supabase PostgreSQL database schema without manually entering connection strings.
ChartDB only reads your database schema metadata. It does not access your actual data.
## Key Benefits
Connect with your Supabase account using OAuth - no connection strings needed
Your schema automatically syncs every hour to stay up-to-date
No need to manually manage or store database credentials
Visual diagrams that stay in sync with your Supabase database
***
## Connect Your Supabase Database
From the [ChartDB homepage](https://app.chartdb.io/), click **New Diagram** to open the diagram creation dialog.
In the database selection screen, choose **Supabase** (the green lightning bolt icon), then click **Continue**.
On the import method screen, select **Cloud Platforms** which shows the OAuth option with BigQuery, Snowflake, and Supabase logos.
Click the **Connect with Supabase** button to initiate the OAuth flow.
A popup window will open directing you to the Supabase authentication page. Log in with your Supabase account credentials and authorize ChartDB to access your projects.
Make sure popups are enabled for ChartDB in your browser.
After successful authentication, you'll see a list of your Supabase projects. Select the project you want to visualize.
Each project card shows:
* Project name
* Region
* Database host
Then enter your Supabase database password in the password field.
**Where to find your password:** Click the link in the info box to go directly to your Supabase Dashboard: **Settings > Database > Database password**
Click **Connect & Import** to start the synchronization process. You'll see a loading screen while ChartDB imports your schema metadata.
Once complete, you'll see a success message. ChartDB will automatically redirect you to your new diagram showing your Supabase database schema.
***
## Re-syncing Your Database
Your Supabase database automatically syncs every hour. You can also manually trigger a re-sync at any time.
### Quick Re-sync from Toolbar
On your diagram page, click the **Connected** button (with a green cloud icon) in the top navigation bar.
In the popup, you'll see:
* **Database Connected** status with green checkmark
* **Last synced** timestamp
* **Re-sync Now** button
Click **Re-sync Now** to fetch the latest schema from Supabase.
The page will reload with your updated schema.
### Full Re-sync from Database Settings
1. Click **Sync** or the database settings option from the diagram menu
2. The Supabase sync dialog will appear showing you're already connected
3. Click **Re-Sync Now** to refresh your schema
***
## Troubleshooting
If you see an "Invalid password" error:
1. Go to your [Supabase Dashboard](https://supabase.com/dashboard)
2. Navigate to your project's **Settings > Database**
3. Copy your database password (or reset it if needed)
4. Re-enter the correct password in ChartDB
If OAuth connection fails:
1. Ensure you're logged into Supabase
2. Check that your Supabase project is active (not paused)
3. Allow popups for ChartDB in your browser
4. Try again by clicking "Connect with Supabase"
If your projects don't appear after authentication:
1. Click **Retry** to refresh the project list
2. Verify your Supabase account has at least one active project
3. Check that your Supabase organization permissions allow API access
***
## User Flow Summary
| Step | Action | Screen |
| ---- | ------------------------------- | ------------------ |
| 1 | Create New Diagram | Homepage |
| 2 | Select Supabase | Database Selection |
| 3 | Choose Cloud Platforms | Import Method |
| 4 | Connect with Supabase | OAuth Connection |
| 5 | Authenticate | Supabase Popup |
| 6 | Select Project & Enter Password | Project & Password |
| 7 | Connect & Import | Syncing |
| 8 | View Diagram | Diagram Editor |
***
## Alternative: Manual SQL Import
If you prefer not to use OAuth or need more control over the import process, you can use the manual SQL query method. See the [Supabase Import Guide](/docs/import/supabase) for instructions on running the import query directly in the Supabase SQL Editor.
# Sync Database to Diagram
Source: https://docs.chartdb.io/docs/sync-database-to-diagram
Automatically keep your diagrams in sync with your database schema changes
## Programmatic Diagram Updates
ChartDB offers powerful capabilities to automatically sync and update your diagrams based on changes made to your database schema without requiring database credentials. This feature ensures your database diagrams always reflect the current state of your database structure.
**Tables maintain their positions, colors, and areas during updates,
preserving your carefully designed diagram layout.**
### Key Benefits
* **Always Up-to-Date**: Keep diagrams in sync with your evolving database structure
* **No Credentials Required**: Sync without exposing sensitive database access details
* **Automation-Ready**: Integrate diagram updates into CI/CD pipelines
* **Time-Saving**: Eliminate manual diagram updates after schema changes
* **Documentation Accuracy**: Maintain reliable documentation for your team
### How It Works
The synchronization process uses our API to update diagrams whenever changes are detected in your database schema. You can trigger updates:
1. Automatically via webhooks when database migrations run
2. As part of CI/CD pipelines
3. Through scheduled jobs
4. Manually via API calls when needed
### Implementation Examples
see our [API Documentation](/docs/api/introduction).
### Supported Database Types
ChartDB supports schema synchronization for multiple database types:
* PostgreSQL
* MySQL
* MariaDB
* SQL Server
* SQLite
### Best Practices
* Include diagram updates in your database migration process
* Set up monitoring for sync failures
* Consider versioning your diagrams alongside schema changes
* Use environment-specific diagrams for development, staging, and production
For detailed implementation instructions, authentication methods, and complete API reference, see our [API Documentation](/docs/api/introduction).
# Common Issues
Source: https://docs.chartdb.io/docs/troubleshooting/common-issues
Find solutions to common technical problems encountered while using ChartDB.
This usually means ChartDB can't understand the format of the JSON data you're providing.
**Possible Causes:**
* **Incorrect JSON Syntax:** The JSON data might have syntax errors.
* **Database Client Formatting:** Your database client might be outputting JSON in an unrecognized format.
* **Truncated JSON:** For large schemas, your database client might cut off the JSON output.
* **Copy-Paste Issues:** Errors can be introduced when pasting the JSON into ChartDB.
**Troubleshooting Steps:**
Use an online JSON validator to check your exported JSON for syntax errors. Correct any errors found before importing into ChartDB.
This step is primarily for users using **SQL Server Management Studio (SSMS)** or **Azure Data Studio**.
Increase the character limit for query results in your database client:
1. Go to **Tools → Options**.
2. Navigate to **Query Results → SQL Server → Results to Text**.
3. Increase **Maximum number of characters displayed in each column**.
1. Go to **File -> Preferences -> Settings** (or **Code -> Settings** on macOS).
2. Search for `mssql.query.maxCharsToStore`.
3. Set **Maximum Characters To Store** to a large value.
After adjusting settings or correcting JSON syntax, re-run the ChartDB export query. Copy the **entire** JSON output again and paste it into ChartDB.
Test importing the JSON in a different web browser to rule out browser-specific issues.
If your database has multiple schemas, ChartDB might not display all your tables immediately after import.
**Possible Cause:**
* **Default Schema Display:** By default, ChartDB only shows tables from the `public` schema. Tables in other schemas are hidden initially.
**Solution:**
To display tables from other schemas, you need to select them in the schema management panel.
**Steps:**
Go to the tables and fields pages and navigate to the "Managing Schemas" section to learn how to select schemas and display tables from those schemas.
[Learn more about managing schemas](/docs/diagrams/tables-fields#managing-schemas)
Users importing PostgreSQL and SQL Server databases sometimes report that relationships between tables are not automatically detected and visualized.
**Possible Causes:**
* **Incomplete Foreign Key Definitions:** ChartDB relies on foreign key constraints. If these are missing or incorrect, relationships might not import.
* **Query Limitations (Rare):** The import query might not fully capture relationship data in complex schemas.
**Troubleshooting Steps:**
Ensure foreign key constraints are correctly defined in your database. Use a database client to inspect table definitions.
Try using the updated import query provided by the ChartDB team for your specific database.
Create relationships manually within ChartDB using drag and drop, right-click, or the side panel.
Unconventional naming of foreign key columns might hinder automatic detection.
Sometimes, exporting your diagram to image formats (PNG, JPG, SVG) might fail without a clear error message, or the process might hang.
**Possible Causes:**
* **Diagram Complexity:** Very large or complex diagrams can strain browser resources.
* **Browser Limitations:** Browser resource limits might prevent successful export.
* **Browser Compatibility:** Browser-specific issues might interfere with export.
**Troubleshooting Steps:**
Open your browser's developer console. Attempt export again. Check the console for JavaScript errors.
Try exporting the diagram using a different web browser.
Working with very large database schemas can sometimes lead to performance issues or browser crashes.
**Possible Causes:**
* **Browser Resource Limits:** Browsers have resource limitations when rendering large diagrams.
* **ChartDB Optimization:** Current ChartDB version is optimized for moderately sized databases.
**Recommendations for Handling Large Databases:**
If possible, import only the schemas or subsets you need to visualize.
Break down very large diagrams into smaller, domain-specific diagrams.
Observe browser resource usage (CPU, Memory) to identify limitations.
Users sometimes report that their diagrams disappear after closing their browser.
ChartDB cloud version automatically backs up your diagrams in the cloud, while the self-hosted version stores diagrams only in your browser's local storage and requires manual exports for backup.
See our [Cloud vs Self-Hosted](/docs/cloud-vs-self-hosted) documentation for more details.
**Possible Causes:**
* **Browser Data Clearing:** Clearing browser data (cache, cookies, site data) can delete locally stored diagrams.
* **Incognito/Private Browsing:** Private browsing modes usually clear local storage when the session ends.
* **Browser Settings or Extensions:** Some settings or extensions might clear IndexedDB or site data.
**Preventing Diagram Loss:**
Diagrams are stored **locally in your web browser**.
Be cautious when clearing browser data. **Do not clear "Cookies and other site data"** to preserve diagrams.
Avoid using incognito mode for important diagrams.
**Regularly export your diagrams as JSON files.** Treat these files as backups.
# Welcome to ChartDB
Source: https://docs.chartdb.io/docs/welcome
A database visualization tool with support for multiple databases, schema creation, and export options.
ChartDB is an open-source database visualization tool that helps you visualize your database schema and generate insightful diagrams.
Get from database to diagram as quick as 15 seconds.
Join our [Discord community](https://discord.gg/QeFwyWSKwC) for support and
discussions.
## Import
We support a wide range of database systems: PostgreSQL, MySQL, SQLite, SQL Server, MariaDB, ClickHouse, CockroachDB.
## Diagram
Create clear and insightful diagrams from your database schema.
Effortlessly add, manage, and visualize tables and fields in your database
diagrams.
Visualize and create relationships between tables using foreign keys, making
complex schemas understandable.
Understand the dependencies between Views and Tables, crucial for database
maintenance and refactoring.
Customize your diagrams with layouts, colors, and filters for enhanced
clarity and visual organization.
## Export
Share your database diagrams in various formats.