> ## Documentation Index
> Fetch the complete documentation index at: https://ngquct-feat-sql-code-folding.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Data Grid

> Sort, filter, edit, and copy query results in a spreadsheet-style grid

Query results and table data open in a spreadsheet-style grid. Sort and filter columns, edit cells inline, and copy in several formats.

<Frame caption="Data grid with query results">
  <img className="block dark:hidden" src="https://mintcdn.com/ngquct-feat-sql-code-folding/zGZR177kZHvwb1PV/images/data-grid.png?fit=max&auto=format&n=zGZR177kZHvwb1PV&q=85&s=04da5597c3454d35f965e6a0f51c2475" alt="Data grid" width="1560" height="960" data-path="images/data-grid.png" />

  <img className="hidden dark:block" src="https://mintcdn.com/ngquct-feat-sql-code-folding/zGZR177kZHvwb1PV/images/data-grid-dark.png?fit=max&auto=format&n=zGZR177kZHvwb1PV&q=85&s=5aea366f12435554067923960557cf98" alt="Data grid" width="1560" height="960" data-path="images/data-grid-dark.png" />
</Frame>

## View Modes

Switch between **Data**, **Structure**, **JSON**, and **Chart** in the status bar. Query tabs show Data, JSON, and Chart only. The mode is remembered per tab.

JSON mode shows the rows as a JSON array, with **Text** and **Tree** views. Select rows in Data mode first to limit what JSON shows, then use **Copy JSON**.

### Chart mode

Chart mode is a Starter feature. Pick a bar, line, area, or scatter chart, then choose a numeric Y column. X can use row numbers, another numeric column, a date or timestamp column, or a categorical column; an optional text, boolean, enum, or set column splits the result into series. Date and timestamp columns plot on a real time axis, so points sit at their true spacing whatever order the rows arrive in. Hover the plot to inspect exact X, Y and series values. Controls stack vertically when the result pane is narrow.

Your chart type and axis choices belong to the tab and follow the column names, so they survive a page turn, a sort, a refresh and a re-run. A column that is missing from the next result is remembered rather than cleared, and comes back when the column does.

Charts draw the active result's loaded rows, up to 2,000 points, 20 series, and 50,000 inspected rows. Past a limit the chart still draws what fits and the toolbar says how much, for example "Showing the first 2,000 points of 8,431 loaded rows". Null, binary and unrepresentable axis values are skipped and counted. The status bar keeps the row count, the pagination controls and **Fetch All** in Chart mode, so you can load the rest without leaving the chart. Grid selection, Find, hidden columns and value filters do not narrow the chart.

## Columns

### Sort

Click a header to cycle through ascending, descending, and off. `Shift`-click another header to add it to the sort, so you can order by several columns at once. Sort re-runs the query with `ORDER BY` appended, replacing any existing one.

To sort on open, set **Settings > Data > Default row sort** to **Primary key** or **First column** (default is **No sorting**). A click on any header still overrides it.

<Note>
  If the sort column can't be ordered on the server (a `BLOB`, `JSON`, or spatial column), the query fails. Pick another column or set the default back to No sorting.
</Note>

### Filter

The header gives you two filters:

* **Filter Values…** (or the funnel icon on hover) filters the loaded rows by picking values, without re-querying. The popover lists each value with its count; search to narrow, check the values to keep, and click **Apply**. NULL and empty values appear as their own entries. `Return` applies, `Escape` cancels. Filter several columns at once and rows must match every filter. Clear from the header menu (**Clear Value Filter**, **Clear All Value Filters**).
* **Filter with column** adds a filter row for that column to the [Filter Panel](/features/filtering) and opens it. Use this to filter the whole table on the server.

<Frame caption="Filter a column by its values">
  <img className="block dark:hidden" src="https://mintcdn.com/ngquct-feat-sql-code-folding/VLUDsoMaXL0TA-5F/images/value-filter-popover.png?fit=max&auto=format&n=VLUDsoMaXL0TA-5F&q=85&s=672522f549bbae0a1b2d71fb0fd25ce6" alt="Value filter popover" width="1560" height="960" data-path="images/value-filter-popover.png" />

  <img className="hidden dark:block" src="https://mintcdn.com/ngquct-feat-sql-code-folding/VLUDsoMaXL0TA-5F/images/value-filter-popover-dark.png?fit=max&auto=format&n=VLUDsoMaXL0TA-5F&q=85&s=d99a18cd99c55e02d6b7446060f9bd30" alt="Value filter popover" width="1560" height="960" data-path="images/value-filter-popover-dark.png" />
</Frame>

<Note>
  Value filters run on the rows already loaded, so they cover the current page only.
</Note>

### Resize and Hide

Drag a column border to resize, or double-click it to fit the content. The header menu has **Size to Fit** for one column and **Size All Columns to Fit** for all of them. Fitted columns stop at half the visible grid width, so one long-text column can't push the rest off screen.

Hide columns from the columns button in the status bar or the header menu. A hidden column isn't fetched, so hiding a large column makes the table load faster. The primary key is always fetched, so editing still works, and so is the column you are sorting by, so hiding it leaves the order alone.

On a schemaless store such as MongoDB, the popover lists every field the grid can draw, including ones that only turn up in later documents, so **Hide All** covers all of them.

Column widths, order, and which columns are hidden are remembered per table, scoped to the connection, database, and schema. Click **Reset Columns** in the columns popover to put everything back to defaults.

### Display Format

UUIDs and Unix timestamps render in a readable form when the column type and name match (for example a `BINARY(16)` column named `uuid`). Right-click a header and choose **Display As** to set the format per column: Raw Value, UUID, Unix Timestamp (seconds or milliseconds), JSON, or PHP Serialized. See [Cell and Row Viewers](/features/json-viewer) for what each format does, and for how MongoDB binary columns differ. Toggle the automatic detection in **Settings > Data > Smart value detection**.

## Editing

<Warning>
  Editing works when the results come from one table, with or without an alias, as in `SELECT * FROM orders o WHERE o.id = 1`. Comments, `DISTINCT`, `ORDER BY`, `LIMIT` and `FOR UPDATE` are fine. Joins, comma joins, subqueries in `FROM`, CTEs, `UNION`, `EXCEPT`, `INTERSECT` and `FROM ONLY` are read-only, and so is any connection set to the Read-Only [safe mode](/features/safe-mode) level.

  You can write the schema in front of the table, as in `SELECT * FROM public.orders o`, as long as it is the schema you are browsing. Naming a different schema keeps the results read-only: the saved edit does not repeat the schema, so it would go to the browsed one instead of the table you queried. Switch to that schema first if you want to edit through it.

  Two shapes look editable but refuse to save. A column renamed with `AS` cannot be written back, though the other columns in the same row still can. A query that renames or omits the primary key blocks the whole save, because there is nothing to match the row on.
</Warning>

Double-click a cell to edit it. Press `Enter` to confirm or `Escape` to cancel. Some types open a dedicated editor:

| Column type                         | Editor                                                                    |
| ----------------------------------- | ------------------------------------------------------------------------- |
| Date, time, datetime, timestamp     | Calendar and time picker, or edit as text                                 |
| Foreign key                         | Searchable list of referenced values                                      |
| Boolean, `BIT`, `TINYINT(1)`        | Checkbox, with a third state for nullable columns                         |
| `ENUM`                              | Searchable value list                                                     |
| `SET` (MySQL/MariaDB)               | Multi-select checkboxes                                                   |
| Array of a simple type (PostgreSQL) | Ordered list, one row per element                                         |
| `JSON`, `JSONB`, `BLOB`, binary     | [Cell viewers](/features/json-viewer); hex editor is read-only over 10 KB |

<Frame caption="Type-specific cell editor">
  <img className="block dark:hidden" src="https://mintcdn.com/ngquct-feat-sql-code-folding/zGZR177kZHvwb1PV/images/date-picker-editor.png?fit=max&auto=format&n=zGZR177kZHvwb1PV&q=85&s=d9abbd19da151caa9afd70fcccc80f39" alt="Cell editor" width="1560" height="960" data-path="images/date-picker-editor.png" />

  <img className="hidden dark:block" src="https://mintcdn.com/ngquct-feat-sql-code-folding/zGZR177kZHvwb1PV/images/date-picker-editor-dark.png?fit=max&auto=format&n=zGZR177kZHvwb1PV&q=85&s=ec7d21dbd83b3a0265ce9e6aec083e35" alt="Cell editor" width="1560" height="960" data-path="images/date-picker-editor-dark.png" />
</Frame>

Right-click an editable cell and open **Set Value** for common values without typing: **Empty**, **NULL** (nullable columns), **Default** (columns with a default), and date functions like `NOW()` or `CURRENT_DATE` on date columns.

### Foreign Keys

Foreign key cells show an arrow on the right edge. Click it to open the referenced table filtered to the matching row. If the current tab holds a query or unsaved edits, the reference opens in its own tab instead of replacing it, and clicking the same reference again returns to that tab. `Cmd`-click always opens a new tab. Right-click for **Preview Referenced Row**, which shows the row in a popover.

<Frame caption="Foreign key lookup">
  <img className="block dark:hidden" src="https://mintcdn.com/ngquct-feat-sql-code-folding/zGZR177kZHvwb1PV/images/fk-lookup-popover.png?fit=max&auto=format&n=zGZR177kZHvwb1PV&q=85&s=9c6f36f9aa21f583d09d2ebcf9a4c38c" alt="Foreign key lookup" width="1560" height="960" data-path="images/fk-lookup-popover.png" />

  <img className="hidden dark:block" src="https://mintcdn.com/ngquct-feat-sql-code-folding/zGZR177kZHvwb1PV/images/fk-lookup-popover-dark.png?fit=max&auto=format&n=zGZR177kZHvwb1PV&q=85&s=53610ea3d876b2881a791f7d9361ae81" alt="Foreign key lookup" width="1560" height="960" data-path="images/fk-lookup-popover-dark.png" />
</Frame>

### Row Operations

* **Add** (`Cmd+Shift+N`): click **+** in the status bar or use the shortcut.
* **Duplicate** (`Cmd+Shift+D`): right-click a row and choose **Duplicate**. The primary key resets to `DEFAULT` so the database assigns a new one.
* **Delete**: select rows by their row number (`Shift`-click for a range, `Cmd`-click for separate rows) and press `Delete`.
* **Paste**: copy rows, then right-click and choose **Paste** to insert them as new rows.
* **Fill Column**: right-click a header to set one value across all loaded rows. It skips primary key columns.

### Saving Changes

Edits are queued, not applied right away. The toolbar shows the pending count, modified cells are highlighted, and new and deleted rows are marked.

* **Preview SQL** (`Cmd+Shift+P`): review the statements before applying.
* **Save Changes** (`Cmd+S`): apply all pending changes.
* **Undo / Redo** (`Cmd+Z` / `Cmd+Shift+Z`): step through edits before saving.

There is no discard button. Undo your edits, or let the confirmation prompt drop them when you refresh, re-sort, change filters, or close the tab. See [Change Tracking](/features/change-tracking) for how the queue works.

## Inspector

Toggle the inspector with `Cmd+Option+I`, or with the button at the right end of the toolbar. With a row selected, it lists every column value with full editors for long text and JSON; see [Cell and Row Viewers](/features/json-viewer) for details. Turn on **Settings > Data > Auto-show inspector on row select** to open it automatically.

With no row selected, it shows table statistics: data, index, and total size, row count, average row size, engine, collation, and created and updated dates (fields vary by database).

In the Structure tab, the inspector follows the structure grid instead: select a column, index, or foreign key and it lists that row's own fields. The fields are editable where the grid is, with the same type list and YES/NO menus, and an edit joins the pending changes you apply with Save. A row you have marked for deletion is read-only.

<Frame caption="Row inspector">
  <img className="block dark:hidden" src="https://mintcdn.com/ngquct-feat-sql-code-folding/zGZR177kZHvwb1PV/images/cell-inspector.png?fit=max&auto=format&n=zGZR177kZHvwb1PV&q=85&s=dc99f9f69ca6170a267a7b8911ccb965" alt="Row inspector" width="1560" height="960" data-path="images/cell-inspector.png" />

  <img className="hidden dark:block" src="https://mintcdn.com/ngquct-feat-sql-code-folding/zGZR177kZHvwb1PV/images/cell-inspector-dark.png?fit=max&auto=format&n=zGZR177kZHvwb1PV&q=85&s=58eab0649e58a464e58743f91dd116dd" alt="Row inspector" width="1560" height="960" data-path="images/cell-inspector-dark.png" />
</Frame>

## Select and Copy

Click a cell to select it, drag or `Shift`-click for a range, and click row numbers for whole rows. Copy, **Copy as**, and Delete act on the whole current selection, whether that is a cell range, a column (`Cmd`-click its header), or rows picked by their row numbers.

* `Cmd+C`: the focused cell, or the selected rows as TSV.
* `Cmd+Shift+C`: the selected rows as TSV.
* `Cmd+Option+J`: the selected rows as JSON.

Right-click a row and choose **Copy as** for more formats:

| Format                                    | Output                                     |
| ----------------------------------------- | ------------------------------------------ |
| With Headers                              | TSV with a header row                      |
| JSON                                      | Array of row objects                       |
| CSV / CSV with Headers                    | RFC 4180 CSV                               |
| Markdown                                  | Markdown table                             |
| IN Clause                                 | `('a', 'b', 'c')` for `WHERE col IN (...)` |
| INSERT Statement(s) / UPDATE Statement(s) | SQL statements per row (SQL databases)     |

Copies follow the grid as shown: hidden columns are left out and columns keep their current order. When you select specific cells, **Copy as** (JSON, CSV, Markdown, INSERT, and UPDATE) includes only the columns you selected; select whole rows to include every column. The UPDATE format still keys its WHERE clause on the primary key, even when the primary key cell is not part of the selection. Right-click a header and choose **Copy Column Values** to copy a whole column, one value per line.

## Find in Results

`Cmd+F` on a table tab opens a find bar above the grid. Type a term and the matching cell is highlighted and scrolled into view. `Return` and `Cmd+G` step forward, `Cmd+Shift+G` steps back, and `Escape` clears the term, then closes the bar.

Matching is case and accent insensitive, and it runs over the text you see, so a value reads the same in the find bar as it does in the cell. Binary and spatial columns are skipped, since they render as hex rather than as text.

**The find bar searches the rows already loaded, not the whole table.** Results are paginated at the database, so the counter always says which it means:

| What you see           | What it means                                      |
| ---------------------- | -------------------------------------------------- |
| `3 of 12`              | Every row is loaded, so this is the whole table    |
| `3 of 12 on this page` | More rows exist that were never fetched            |
| `No matches`           | Every row is loaded and none match                 |
| `Not on this page`     | None of the loaded rows match, and more rows exist |

When nothing matches on the page and more rows exist, **Search All Rows** turns the term into a filter that runs on the server, so the answer covers the whole table. That button replaces the filters on the tab, so it only appears when you have none applied. With filters already set, add your search to the filter panel instead.

## Pagination and Limits

**Table tabs** page through data. The status bar has a rows-per-page menu (5 to 1,000, a custom size, or **All rows**) and First / Previous / Next / Last controls. Click the page indicator (for example `3 / 12`) to type a page number and jump to it. Set the default page size in **Settings > Data**.

Large tables show an estimated total instead of running a slow `COUNT(*)`. **Settings > Data > Count rows if estimate less than** sets the threshold below which TablePro counts exactly. An estimated total is prefixed with `~`. **Count exactly** next to the total replaces an estimate, or fills in a total shown as `?`, with a real count. That count can take a while on a large table, so it runs in the background and `Cmd+.` cancels it.

An estimate of zero from a table the server has never analyzed is treated as unknown rather than as an empty table, so you get no total instead of a wrong one.

**Query tabs** cap results at 10,000 rows by default. Your query is sent exactly as you wrote it; TablePro stops reading once it reaches the cap. A query with its own `LIMIT`, `FETCH FIRST`, or `TOP` is not capped. When the cap trims a result, the status bar shows **Fetch All** to load the rest (with a confirmation, since large results use a lot of memory). Run one query without the cap via **Execute Without Limit** (`Option+Cmd+Enter`). Adjust the cap in **Settings > Data** (**Truncate query results**, **Row cap**).

Press `Cmd+.` to cancel a running query or a Fetch All.

## Display Settings

NULL shows as styled `NULL` text. Set the NULL display, date format, row height, row numbers, and alternate row backgrounds in **Settings > Data**.

## MongoDB Collections

MongoDB has no fixed schema, so TablePro infers columns by sampling up to 50 documents. Missing fields show as NULL, and the `_id` column is read-only (delete and re-insert to change it). BSON values display as `ObjectId("...")`, ISO 8601 dates, `BinData(...)`, decimal strings, and `DBRef(...)`.

Changes preview as shell commands: `insertMany`, `updateOne` with `$set`, and `deleteMany`.
