---
title: "Move a SQLite database to another version"
description: "Convert a SQLite .db file or dump into a script that loads on SQLite 3.31 and later, with the same tables, keys and data, and a report of every change."
canonical: https://intosql.com/sqlite-to-sqlite
last_updated: 2026-10-15
---

# Move a SQLite database to another version

Drop a SQLite database file (.db, .sqlite, .sqlite3) or the SQL that sqlite3's .dump writes, and download a script that loads on SQLite 3.31 and later. Each column keeps its declared type and collation, STRICT is left out so older versions accept the tables, and every change is listed in the report.

[SQLite to SQLite migration guide](https://intosql.com/docs/migrations/sqlite-to-sqlite)

[SQLite to SQLite data types](https://intosql.com/docs/types/sqlite-to-sqlite)

## What changes between the file and the script

Each row is a rule the converter applies, with the report line it writes when the rule changes something in your data.

| In the file | In the script | Report line |
| --- | --- | --- |
| declared types (any text: VARCHAR(255), DATETIME, BOOLEAN, JSON or none) | the type every value in the column fits |  |
| BOOLEAN, BOOL | INTEGER 0 or 1 with a CHECK |  |
| TEXT, VARCHAR(n), CHAR(n), CLOB | the declared type, kept as it is |  |
| DECIMAL(p,s), NUMERIC | the exact type when every value fits it, else a double |  |
| BLOB, and columns holding bytes | BLOB |  |
| DATE, DATETIME, TIMESTAMP holding ISO 8601 text | DATE, DATETIME as ISO 8601 text |  |
| JSON holding JSON text | TEXT |  |
| Infinity in REAL columns | 9e999, SQLite's own infinity |  |
| INTEGER PRIMARY KEY, AUTOINCREMENT | INTEGER PRIMARY KEY, AUTOINCREMENT kept |  |
| COLLATE NOCASE | COLLATE NOCASE |  |
| generated columns | generated columns, STORED or VIRTUAL as in the source |  |
| generated columns with functions IntoSQL doesn't write, and the || operator | a plain column | `generated_not_translated` |
| defaults with functions IntoSQL doesn't write (strftime, randomblob) | no default | `default_not_translated` |
| CHECK constraints | translated |  |
| CHECK constraints with functions IntoSQL doesn't write | not created | `check_skipped` |
| partial and expression indexes | not created | `index_skipped` |
| foreign keys (unenforced unless PRAGMA foreign_keys=ON) | the same foreign keys, listed by PRAGMA foreign_key_check |  |
| foreign keys to tables the dump doesn't create | not created | `foreign_key_skipped` |
| primary keys holding NULL, and NOT NULL columns with values that become NULL | NOT NULL dropped | `not_null_relaxed` |
| views, triggers and virtual tables (FTS, R*Tree) | listed in the report | `not_converted_objects` |
| dumps without CREATE TABLE (--data-only) | types read from the values | `no_schema` |
| expressions in INSERT values other than the text functions dumps write | NULL | `expression_value` |
| NUL characters in text | kept |  |
| text that isn't valid UTF-8 | the bad bytes as the replacement character | `invalid_utf8_replaced` |
| text decoded twice (Ã±) | copied as it is | `mojibake_suspected` |

## Load the script into SQLite

The script creates the tables, copies the rows, then adds keys, indexes and foreign keys, so it loads fast and stops at the first error.

```shell
sqlite3 -bail mydb.sqlite < sqlite.sqlite.sql
```

Foreign keys are declared in each table and the rows load with foreign key checks off. The script then lists any row that points to a row that doesn't exist, instead of failing half way.

For a large dump, load into a new file on a local disk. The script loads everything in one transaction with syncing off, so it runs at disk speed, and a load that stops half way leaves no partial tables.

The command line loads the file straight into a SQLite database, without writing a script. It's free.

## Dump SQLite for the best result

The converter reads what the dump holds. These options keep more of your data in it:

- The database file itself converts as it is, read page by page in your browser. Close the app that writes to it first, so no change is left behind in its -wal file.
- sqlite3 app.db .dump > app.sql writes the same database as SQL, which converts too, as .sql or .sql.gz.
- DB Browser for SQLite writes that SQL with File, Export, Database to SQL file, and so does Python's iterdump().

## What isn't converted

Views, triggers, procedures, functions and other objects written in SQLite's own language are listed in the report by name, never dropped silently, so you can check each one against the SQLite version you load into before recreating it.

## Questions about moving SQLite between versions

### Is my dump uploaded?

No. The converter runs in your browser and reads the dump from your disk in pieces, so it never leaves your computer. The desktop app and the command line convert on your computer too.

### How large can the dump be?

The converter never holds the whole dump in memory: it reads the file twice, by ranges, and writes the script as it goes. Multi-gigabyte dumps convert in the browser, in the desktop app and with the command line.

### Can I convert the .db file itself?

Yes. Drop the .db, .sqlite or .sqlite3 file: it is read in your browser and never uploaded. Text with NUL characters and every digit of a double arrive as stored, which a .dump can cut short.

### Which SQLite versions can the dump come from?

Database files from SQLite 3.0 on, in UTF-8 or UTF-16, and dumps from the sqlite3 shell's .dump or Python's iterdump(). Every test file and dump comes from sqlite3 3.31 to 3.54, STRICT and WITHOUT ROWID tables included, and is compared with its source cell by cell.

### How are column types decided when SQLite doesn't enforce them?

Each column keeps its declared type, and so do COLLATE NOCASE and RTRIM. STRICT is left out, so the script loads on every version from 3.31.

### Are INTEGER PRIMARY KEY and AUTOINCREMENT kept?

Yes. INTEGER PRIMARY KEY and AUTOINCREMENT stay as they are, and sqlite_sequence continues where the dump left it.

### What about views and triggers?

They are listed in the report by name and not converted, so you can recreate them once the tables are in.

### Which SQLite versions does the script load on?

SQLite 3.31 and later. Every test file and dump loads on 3.31 and 3.54, stopping at the first error if anything goes wrong.

## From SQLite

- [SQLite to PostgreSQL](https://intosql.com/sqlite-to-postgresql)
- [SQLite to MySQL](https://intosql.com/sqlite-to-mysql)
- [SQLite to MariaDB](https://intosql.com/sqlite-to-mariadb)
- [SQLite to SQL Server](https://intosql.com/sqlite-to-sql-server)
- [SQLite to Oracle](https://intosql.com/sqlite-to-oracle)

## Into SQLite

- [MySQL to SQLite](https://intosql.com/mysql-to-sqlite)
- [MariaDB to SQLite](https://intosql.com/mariadb-to-sqlite)
- [PostgreSQL to SQLite](https://intosql.com/postgresql-to-sqlite)
- [SQL Server to SQLite](https://intosql.com/sql-server-to-sqlite)
- [Oracle to SQLite](https://intosql.com/oracle-to-sqlite)

## Guide and every pair

- [SQLite to SQLite migration guide](https://intosql.com/docs/migrations/sqlite-to-sqlite)
- [SQLite to SQLite data types](https://intosql.com/docs/types/sqlite-to-sqlite)
- [Every database pair](https://intosql.com/database-migration)
