Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>SQLite sidekickNew to Visual Studio Code? Get it now.
SQLite sidekick

SQLite sidekick

PrasertKana

|
40 installs
| (0) | Free
Read-only SQLite database viewer with a spreadsheet-like grid, explorer, SQL history, and Jupyter export
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

SQLite sidekick

A read-only SQLite 3 database viewer for Visual Studio Code.

Open a .sqlite, .sqlite3, .db3, or .db file normally. The viewer checks SQLite's binary header before starting a worker, so an unrelated file using the same .db suffix is refused rather than parsed. For any other extension, or none, run PK SQLite: Open File as SQLite Database.

No native dependency, and nothing bundled. The engine is node:sqlite, built into the Node that VS Code already ships, so one platform-neutral package of about 110 KB serves every platform.

Remote files work too. Databases reached over Remote - SSH, WSL, Dev Containers, or Codespaces open the same way, as do those in a virtual workspace such as GitHub Remote Repositories — see Remote and virtual workspaces, which covers where the extension is best installed and the one case where a database can be missing its most recent rows.

Requirements

VS Code built on Node 24 or newer, which is where node:sqlite became available without a command-line flag. On an older build the extension reports this and registers nothing, rather than failing later.

Features

  • Every table and view appears as a workbook-style bottom tab.
  • Explorer panel listing the database, its tables, views, and their columns.
  • SQL suggestions for keywords, types, SQLite's 172 built-in functions, tables, views, and columns, aware of the clause and of FROM aliases.
  • Virtualized rows and columns for large databases.
  • Column names and declared types remain visible while scrolling.
  • Repeating 16-color, color-blind-friendly column palette.
  • Optional thousands separators for numeric columns, without changing copied or exported values.
  • REAL columns show one consistent number of decimals so the decimal points line up, without the noise digits binary floating point prints.
  • Integers are exact at any size, including beyond 2^53.
  • BLOB columns are shown as a size and never decoded as text.
  • On-demand, wrapping data profiles with key statistics, missing and exact unique counts, distributions, and complete PNG download.
  • Three-state sorting, Excel-style cell/range/row/column selection and keyboard navigation, Tail 10, resizing, auto-fit, and freeze panes.
  • Stacked row filters, run inside SQLite so they narrow the whole table rather than the page on screen, and writable into the SQL editor as the query they stand for.
  • Selection-aware TSV copy and selected-column CSV, TSV, PSV, or JSON export.
  • Focus cell crosshair with matching row and column-header highlights.
  • Session SQL editor with Ctrl/Cmd+Enter execution and cancellation, and syntax highlighting for keywords, types, strings, quoted identifiers, numbers, function calls, and comments.
  • One-click Prettify reformats the query into a consistent clause-per-line layout with indented subqueries.
  • A four-layer read-only guard, described below.
  • SQL history remains in memory for the current open-document session.
  • Selected history exports to a Python/JupySQL .ipynb notebook that needs no database driver installed.
  • Active tables and the active SQL query result can be exported as CSV. A query result export reruns the query so the file holds every row, not only the rows shown under the display limit.
  • Open databases reload after external changes, including changes that reach only the write-ahead log.
  • Works on databases held remotely — Remote - SSH, WSL, Dev Containers, Codespaces — and in virtual workspaces, which are read through VS Code rather than off the local disk.

The database, its -wal, and its -journal are never modified.

Read-only, and how

Viewing must never change a database, so the guarantee is enforced four times over rather than once:

  1. The connection is opened read-only. INSERT, CREATE, and DROP fail inside SQLite itself, below any code of ours.
  2. An authorizer allows only reads. This is a security control, not just a correctness one: a read-only SQLite connection can still ATTACH another database and read it, so without this the SQL box would be an arbitrary-file reader. Everything outside a read allowlist is denied, so a future SQLite release cannot introduce a write-capable action that quietly passes.
  3. Defensive mode is on and extension loading is never enabled.
  4. Exactly one statement, and it must return rows. SQLite's prepare() silently ignores anything after the first statement, so a second one is reported rather than dropped.

SELECT, WITH, VALUES, EXPLAIN, and read-only PRAGMA calls are accepted. INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, ATTACH, DETACH, VACUUM, REINDEX, ANALYZE, transaction control, and pragma assignments are all refused.

Row counts

SQLite has no stored row count — count(*) reads every row — so the grid never waits for one. The first page appears immediately, and the toolbar's count carries a leading … — the tab reads … rows — until the count arrives. Above pkSqlite.exactRowCountLimit (five million by default) the count is reported as ≈ N from max(rowid), which is an upper bound that can overcount after deletions; its tooltip says so.

Deep scrolling in a very large table slows down linearly, because SQLite still visits the rows an OFFSET skips.

Grid controls

Click a column header to select and sort it. Repeated clicks cycle through ascending, descending, and the original order; an arrow beside the name shows the active direction. Tables are sorted inside SQLite before pages are loaded, with rowid as a stable tie-breaker where one exists. The Query Result tab sorts the rows currently displayed. Each tab remembers its own sort while the viewer remains open.

Drag the right edge of a column header to resize that column. Double-click the edge to auto-fit just that column to its cached values, or click the Auto-fit all columns button to fit every column to the values currently loaded. Auto-fit is deliberately cache-based, so it does not scan a large table merely to measure text.

Click and drag cells to select a rectangle. Shift+click extends from the active cell. Shift+click a row or column header for a contiguous range, or Ctrl/Cmd+ click to toggle individual headers. The top-left corner or Ctrl/Cmd+A selects the displayed grid; Escape or an empty-grid click clears the selection.

With the grid focused, arrows, Home, End, Page Up, Page Down, Tab, and Enter move the active cell. Shift extends a cell or whole-row/column selection, and Ctrl/Cmd jumps to an edge. Ctrl/Cmd+Space selects the active column, Shift+Space the active row, and both modifiers select all cells. Tail 10 shows the final ten rows in the active sort; Show all restores the grid.

Ctrl/Cmd+C copies the selected rectangle or rows/columns as escaped TSV. Whole-column table copies include headers and every row, not only cached pages; query-result copies use all displayed rows. Ctrl/Cmd+Shift+C adds headers to a row selection. Nulls remain empty and exact raw values are used.

With column headers selected, Selected in the export group streams those columns as CSV, TSV, PSV, or a typed JSON array in the active sort order. Delimited files include a header and UTF-8 BOM. Query-result exports rerun the successful query so they are not capped by the display limit.

Focus cell highlights the active cell's row and column, including the matching row and column headers and any frozen copies. Column colors are temporarily turned off and their button disabled while focus cell mode is active; leaving the mode restores the previous color setting.

The comma icon toggles thousands separators for numeric cells. This and the column palette are display controls only: tooltips, copy, SQL, and CSV continue to use exact raw values.

Filter rows

Click the funnel, press Ctrl/Cmd+Shift+F, or run PK SQLite: Filter Rows to open the filter bar. A condition is a column — or Any column, which any field of the row may satisfy — an operator, and a value:

Operator Matches
contains, starts with, ends with text anywhere, at the start, or at the end
equals the whole value, ignoring case
is greater than, is less than numbers when the value is a number, text otherwise
is empty / is not empty NULL or an empty string, and its opposite
is NULL / is not NULL only a true NULL

+ Condition stacks another one; a row must satisfy every condition in the stack. Apply or Enter runs it, Clear empties it, and the funnel carries the number of active conditions. % and _ are matched literally rather than as wildcards. Right-click a cell for Filter by "value", which stacks that value onto whatever the bar is holding.

The filter becomes a WHERE clause inside SQLite, so it narrows the whole table and not just the rows already loaded — paging, the row count, the data profile, copy, and every export all read through it, and the row count says "filtered" beside the source name. Each tab keeps its own filter while the viewer is open.

A SQL query result is filtered by its own SQL: add a WHERE clause and run it again. Its rows are capped by the display limit, so a filter there would search only the part that fitted rather than the result it appears to.

Turning a filter into SQL

SQL in the filter bar — or PK SQLite: Write Row Filter as SQL — writes the filter into the SQL editor as the query it stands for, and opens the editor on it:

SELECT *
FROM buyers
WHERE name LIKE 'A%' ESCAPE '\'
  AND spend > 100
ORDER BY spend DESC;

It is the same conditions the grid is reading, one per line, plus the active sort, so running it returns the table on screen. From there it is an ordinary query: join to another table, group it, project fewer columns, keep it in the session history, or export it to a notebook. The bar can only express a WHERE clause, so this is the way out of the bar rather than a second way to run it.

While the bar is open it writes what the rows currently say, applied or not; with the bar closed it writes the filter the grid is showing. The editor's previous contents are replaced, as they are when a history entry is clicked.

Freeze panes

Select a cell and click Freeze panes. Everything above and to the left of it stays put while the rest of the grid scrolls, exactly as in Excel — so selecting the cell in row 3, column C pins the first two rows and the first two columns.

The button then reads Unfreeze; clicking it again removes the split. It is also available from the command palette as PK SQLite: Freeze or Unfreeze Panes at Active Cell, which makes it a single keybinding if you want one.

To move a split, unfreeze first and then freeze at the new cell. Selecting the very first cell freezes nothing, since there is nothing above or to its left, and a split always leaves at least one row and one column free, so the grid can never be pinned solid.

Each table remembers its own split, so switching tabs and coming back keeps it.

One difference from Excel: columns you scroll past slide underneath the frozen ones rather than stopping beside them. Freezing the leftmost columns to keep an identifier in view works as expected; freezing many columns and then scrolling far right will hide some behind the frozen block.

Number display

REAL columns are formatted for reading. Every row in a column gets the same number of decimals, so the decimal points line up:

Stored value Shown
1499.95, 20, 3.5 1499.95, 20.00, 3.50
0.30000000000000004 0.3
0.0000012345678 0.000001235

Trailing digits like those in 0.1 + 0.2 are an artifact of binary floating point rather than your data, and are always dropped. Genuine precision beyond that is capped by pkSqlite.maxDecimalDigits, which defaults to 4. A column whose values are too small for the cap widens instead of rounding to zero, and scientific notation is never used.

Integers are never converted to JavaScript numbers or rounded, at any size. A value beyond 2^53 keeps every digit in the grid, tooltip, copy, and CSV. When the comma toggle is on, separators are inserted directly into the display text, so 9007199254740993 is shown as 9,007,199,254,740,993 without losing precision.

Formatting is display only. Ctrl/Cmd+C copies the exact stored value, hovering a cell shows it, and CSV export is unaffected.

Types, and values that disagree with them

SQLite is dynamically typed: a declared column type is an affinity, and any row may store any type. Two consequences are visible in the grid.

A column with no declared type, and every computed expression, reports no type at all. The viewer infers one from the first values it loads and marks it with a trailing ? in a dimmed style — INTEGER? is a guess, INTEGER is a declaration.

A column whose values disagree with its declared affinity is legal and is not flagged. Text in a REAL column is simply shown as text.

BLOB columns

A BLOB cell shows ‹BLOB 1,024 bytes› and is never decoded as text.

Blobs of 1 KB or less travel to the viewer whole, so hovering shows a complete x'…' literal and Ctrl/Cmd+C copies one that pastes straight back into a query. Above that, only the first pkSqlite.blobPreviewBytes (32 by default) are sent, the tooltip says they are leading bytes, and copying yields the size label instead of a partial value that would look complete.

CSV export always writes every byte, whatever the size, because it re-reads the value rather than using the copy the grid holds.

Remote and virtual workspaces

The viewer supports databases opened through VS Code Remote - SSH, WSL, Dev Containers, Codespaces, and desktop virtual workspaces such as GitHub Remote Repositories. Install the extension in the location offered by VS Code, then open the database normally or use Open With.

What decides the behavior is whether the extension is running on the same machine as the database:

  • Installed on the remote — with Install in SSH: …, or in the container or Codespace — the database is an ordinary local file and is opened directly. Nothing is copied.
  • Installed locally against a remote workspace, or in a virtual workspace where no file exists anywhere, the extension reads the resource through VS Code and opens a disposable local snapshot instead. The snapshot is refreshed when the source changes and removed when the viewer closes.

Either way the viewer works. Read-only repositories still support viewing, SQL, profiles, and CSV export to a separate writable location. VS Code offers the install location when you open the database; taking the remote one avoids the copy, and with it the caveat below.

A snapshot copies the database and its sidecars, but a write-ahead log is not always readable that way. So a snapshotted database is the one case that can show stale rows: if it has an un-checkpointed -wal, commits made since the last checkpoint may be missing, and the viewer says so once on opening. Where the file is opened directly there is no such limitation — a database another process is actively writing opens fine and shows that writer's committed rows.

Notebook export is the one feature that needs a directly-opened file rather than a snapshot, because the exported notebook has to reopen the database by path, outside VS Code. It is therefore available on a remote host when the extension is installed there, and unavailable in a virtual workspace.

Explorer

Click Explorer in the viewer toolbar to show or hide the tree of the database, its tables, views, and columns. Selecting a table or view opens it in the grid and highlights its bottom tab, so the tree and the tabs stay in step.

The tree is read from SQLite's schema, so expanding a table never scans it, however large it is. Tables and views are grouped separately; relations whose names begin with sqlite_ are internal and hidden.

Click Data profile in the viewer toolbar — or run PK SQLite: Toggle Data Profile — to open the profile pane beside the grid. Drag its left edge to resize it, and close it from the × in its header. Profiles are calculated lazily and cached until the database changes. The Query Result tab is also profileable: its successful SELECT is rerun to calculate statistics over the full result rather than only the displayed row limit.

The pane has three tabs, all following the active cell:

  • Record reads the selected row down the page instead of across it, one labelled field per column — the view that makes a forty-column table legible. Step rows with the arrows beside the position, and click a field to jump the grid to that column.
  • Column profiles just the active cell's column, at full pane height.
  • All columns is the card grid: every column at once. Click a card to select that column in the grid.

Every card shows the column's missing, unique, minimum, and maximum values; numeric columns add the mean, the median, and a twelve-bin histogram, while other columns show their eight most frequent values as bars, each with its share of the values present. PNG saves the complete theme-aware card grid, including off-screen cards; it is offered on the All columns tab only.

SQLite has no approximate-distinct function, so unique counts are exact — and exact means a full scan with a sort or hash per column. They are computed every time regardless, so on a very large table expect the profile to take a while.

SQL and notebook export

Click SQL in the viewer toolbar, enter one read-only query, and select Run or press Ctrl/Cmd+Enter. The editor shows line numbers, and the panel is resized by dragging its bottom edge.

Because SQLite offers no way to interrupt a running statement, Cancel and the pkSqlite.queryTimeoutSeconds timeout stop a query by destroying the thread running it and starting a fresh one. This is safe precisely because nothing is being written, and it cannot disturb the grid, the tabs, or the explorer, which are served by a separate worker.

Suggestions appear as you type and can be requested with Ctrl+Space. What is offered first depends on the clause: relations after FROM and JOIN, columns after SELECT, WHERE, ON, GROUP BY, and ORDER BY, and keywords at the start of a statement. Typing alias. narrows to that relation's columns, and main. lists the database's relations. Use ↑ and ↓ to move, Enter or Tab to accept, and Escape to dismiss; Ctrl/Cmd+Enter still runs the query. Names that need quoting are inserted quoted. Set pkSqlite.sqlSuggestions to false to turn the popup off.

Prettify

Prettify in the SQL panel, Shift+Alt+F in the editor, right-click in the editor, or PK SQLite: Prettify SQL reformats the query in place:

SELECT prefs.purchase_purpose,
  prefs.preference,
  prefs.rating
FROM (
  SELECT b.purchase_purpose,
    p.preference,
    p.rating,
    RANK() OVER (PARTITION BY b.purchase_purpose ORDER BY p.rating DESC) AS rank
  FROM buyers AS b
  JOIN prefs AS p
  ON b.buyer_id = p.buyer_id
) AS prefs
WHERE prefs.rank <= 5
ORDER BY prefs.purchase_purpose,
  prefs.rating DESC;

Each clause starts a line. The first item stays on the clause's line and later items — separated by a comma, or by AND or OR — wrap to one extra level of indentation. A subquery's parentheses open an indent level and its closing parenthesis returns to the clause indent, so ) AS prefs reads as one line. Parentheses holding an expression never break, which keeps a window specification such as OVER (PARTITION BY x ORDER BY y) intact.

Keywords and type names are uppercased. Identifiers, quoted identifiers, string literals, function names, and comments keep the spelling you gave them, and the caret stays with the token it was on.

Right-clicking the SQL editor opens a menu with Prettify SQL and Run SQL alongside Cut, Copy, Paste, and Select All. Each entry shows its keyboard shortcut, which remains available if your platform refuses clipboard access to the viewer.

The History panel records each command, status, duration, and displayed row count until the document closes. Notebook creates a JupySQL notebook that reopens the database read-only through a SQLite URI. Python's sqlite3 is in the standard library, so the notebook runs without installing a database driver. It is disabled until the session has at least one query to export.

Commands

  • PK SQLite: Toggle Explorer
  • PK SQLite: Toggle SQL Editor
  • PK SQLite: Run SQL
  • PK SQLite: Cancel SQL
  • PK SQLite: Prettify SQL
  • PK SQLite: Show SQL History
  • PK SQLite: Export SQL History as Jupyter Notebook
  • PK SQLite: Clear SQL History
  • PK SQLite: Export Active Table or Query Result as CSV
  • PK SQLite: Auto-fit All Columns
  • PK SQLite: Freeze or Unfreeze Panes at Active Cell
  • PK SQLite: Toggle Data Profile
  • PK SQLite: Filter Rows
  • PK SQLite: Write Row Filter as SQL
  • PK SQLite: Open File as SQLite Database

Not supported

Editing anything; charting; encrypted databases (SQLCipher, SEE); opening a database over HTTP or an object store; and FTS5 or R*Tree shadow tables presented as first-class relations. Parquet, CSV, and Excel are a different product — see DuckDB sidekick.

Contributing

Building the extension from source, running tests, and packaging a release are covered in DEV.md.

License

MIT

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft