Migrate

Migrate SQL Server to SQLite, Types and Traps

Migrate SQL Server to SQLite, Types and Traps

Moving a SQL Server database to SQLite is an export and load, not a server to server migration, because SQLite has no server to connect to. You pull each table to CSV and a schema script, then load them into a single SQLite file with the sqlite3 tool. Watch three things SQLite handles differently, its flexible typing, the lack of an exact money type and the absence of a native date type. When the SQL Server is gone and all you have is the MDF, Univik reads the file offline and writes the CSV for you, which the MDF converter handles.

SQLite is not a smaller SQL Server. It is a different shape of database, one file on disk with no service, no instance and no network port. That shape is why people move to it, to ship a database inside a desktop or mobile app, to hand a client a single portable file or to cache a slice of a big system close to the code. And it is why the migration works differently from a move to PostgreSQL or MySQL. There is no server to point a tool at, so the whole job is export the data, map the types and load the file.

Why SQLite Is a Different Kind of Move

A migration to PostgreSQL or MySQL is a conversation between two running servers. A tool logs into SQL Server, reads the schema, connects to the target and streams the rows across. SQLite has no server in that sentence. It is a library that reads and writes one file, so there is nothing to log into and nothing to stream to.

Two very different shapes of database

SQL Server
A running service with its own process, network port and logins. Data sits in MDF, NDF and LDF files the engine owns, reached only through the server.

SQLite
One file and a small library. No service, no port, no login. Any program that opens the file reads the whole database directly.

Because there is no server to connect to, the migration is an export and a load, never a live sync.

That single difference decides everything that follows. It rules out the connection based migration tools, it puts all the weight on the export format and it means a dead or offline SQL Server is no obstacle, since you were going to read the data out to a file anyway.

The Routes from SQL Server to SQLite

Three practical routes get the data across, and the right one turns on a single question, is the SQL Server still running.

Three ways to get the data out

Live export
The server is up. Export each table to CSV from SSMS or with bcp, and script the schema. Fine for a one time move you can babysit.

Scripted or ETL
A script or an ETL tool reads the tables and writes SQLite through a driver. Worth the setup only for a repeatable, scheduled load.

File first
Read the MDF straight off the disk to CSV, no instance running. The only route that still works when the server is retired, detached or corrupt.

All three end at the same place, a folder of CSV files and a schema, ready for SQLite to load.

Notice that every route produces the same thing, CSV plus a schema. The target is a file either way, so the smart move is to pick the export that matches your situation and not overthink it. A live server suits a plain SSMS export. A gone server leaves only the file first read, covered in the last section.

The scripted route has real tools behind it, some of them free. ErikEJ’s SQLite and SQL Server Compact Toolbox, an add on for SSMS and Visual Studio, runs a one step migration from a live SQL Server connection to a SQLite file, and the open source MSSQLToSQLite reads the schema and copies the rows for you. Commercial toolkits like DBConvert, ESF Database Migration Toolkit and Withdata DBCopier automate more, mapping indexes and relationships for a licence fee. Every one of them needs a SQL Server it can reach, which is the line the file first route crosses.

Load the Data into a SQLite File

Once the tables are CSV and the schema is a script, loading is quick with the sqlite3 command line tool. Build the empty tables first, then pull each CSV into its table.

  1. Export each SQL Server table to a CSV file and write the CREATE TABLE statements into a schema script.
  2. Start the sqlite3 tool on a new database file, which creates the file if it does not exist.
  3. Run the schema script to build the empty tables with their SQLite types.
  4. Import each CSV into its table, skipping the header row so column names are not read as data.
sqlite3 sales.db
.read schema.sql
.import --csv --skip 1 customers.csv customers
.import --csv --skip 1 orders.csv orders

Build the schema before the import so each column has the SQLite type you chose, rather than letting the importer guess. Turn on foreign keys with PRAGMA foreign_keys = ON if the schema relies on them, since SQLite leaves them off by default. Reading the MDF out to CSV is its own step, and the convert MDF to CSV guide walks that part, while convert MDF to a SQL script covers the schema side.

How SQL Server Types Map to SQLite Affinities

SQL Server has dozens of precise types. SQLite has five, and it calls them affinities rather than types. The mapping is mostly common sense, with one column that needs care.

SQL Server types to SQLite affinities
INTEGER
INT, BIGINT, SMALLINT and TINYINT. Also BIT, which lands as 0 or 1.
TEXT
CHAR, VARCHAR, NVARCHAR and the date types, since dates become ISO8601 strings.
REAL, with care
DECIMAL, NUMERIC, MONEY and FLOAT. REAL is floating point, so exact money needs a workaround.
BLOB
VARBINARY and IMAGE, stored as raw bytes with no conversion.
INTEGER PRIMARY KEY
An IDENTITY column, which becomes SQLite’s own auto growing row id.
TEXT again
UNIQUEIDENTIFIER, kept as its string form since SQLite has no GUID type.
Lengths in parentheses, like VARCHAR(255), are dropped. SQLite does not cap string length.

Flexible Typing Changes the Rules

Coming from SQL Server, the biggest surprise is that the type you write on a SQLite column is a suggestion, not a rule. SQLite calls this flexible typing. A column declared INTEGER will happily hold the text “hello” if you insert it, because the declared type sets only a preferred affinity, and SQLite stores whatever you give it when the value will not convert.

One column, whatever you put in it
What you declare
quantity INTEGER
The type you write in the CREATE TABLE statement.

What SQLite stores
42 kept as an integer
3.14 kept as a real
backorder kept as text

The declared type is a preference, not a rule. A STRICT table brings the enforcement back if you want it.

This is by design and it rarely bites read only data that came from a strict SQL Server source, since that data is already clean. It matters for what happens next. If your application will keep writing to the SQLite file, the database will not guard your types the way SQL Server did. The fix, if you want the old strictness back, is a STRICT table, added in SQLite 3.37, which enforces declared types on write. Declare the tables STRICT in your schema script and the guardrails return.

The Money and Date Traps

Two conversions cause almost all the real damage, and both are quiet. Money is the sharper one. SQLite has no exact decimal type, so a DECIMAL or MONEY column lands in REAL, which is IEEE binary floating point. Most cents values have no exact form in that encoding.

Why money drifts, and how to keep it exact

The trap
A price of 47.49 has no exact binary form. Only .00, .25, .50 and .75 store cleanly, so every other amount becomes a hair off, and sums drift by a cent across many rows.

The fix
Store money as an INTEGER count of cents and divide in the application, so 47.49 becomes 4749. For heavy decimal math, load through the SQLite decimal extension, which keeps exact decimal text.

Decide the money strategy before the load. Fixing it after the numbers have drifted means reconciling every total.

Dates are the quieter trap. SQLite has no date or datetime type at all. A SQL Server datetime2 becomes TEXT, held as an ISO8601 string like the value 2026-03-14 09:41:07. That works well, since SQLite’s date functions read ISO8601 directly, as long as you export in that format and not a regional one. Pick the ISO layout on the way out and the date columns behave. Export them as March 14 or 14/03 and every date comparison in SQLite breaks.

What Does Not Come Across

A SQLite file holds tables, indexes and data. Everything that lived in the SQL Server engine rather than the data stays behind, and it helps to know the list before you promise a clean cutover.

Things a SQLite file will not hold
Stored procedures
SQLite has no procedural language. Business logic in T-SQL moves into the application code.
Server concurrency
One writer at a time. WAL mode lets readers work during a write, but SQLite is not built for many writers at once.
Full ALTER TABLE
SQLite can add, rename or drop a column, but bigger schema changes mean rebuilding the table.
Logins and roles
There is no user model. File permissions on the SQLite file are the only access control.
None of this makes SQLite worse, it makes it a different tool. The trick is choosing it for work that fits.

The honest test is what the database is for. As an embedded store inside an application, a portable file you hand to a client, a fast local cache or a test fixture seeded with real data, SQLite is superb. As a shared, high traffic database with many writers and server side logic, it is the wrong tool, and PostgreSQL, covered in the migrate to PostgreSQL guide, or MySQL in the migrate to MySQL guide, fits that job better.

When the SQL Server Is Gone

A live export needs a SQL Server that answers. Many SQLite projects inherit the opposite, a database whose server was switched off months ago and left one MDF on an old drive, or a file that has gone corrupt and refuses every attach. There is no session to export from.

The answer is to treat the MDF as the source. That is also the tidiest path to SQLite on a healthy server, since a file is where the data was headed anyway. Univik parses the MDF page by page with nothing installed, turning each table into a CSV the SQLite import expects, alongside a script that recreates the tables. Reading the raw file lets it recover tables from a database marked suspect that a connected export would give up on. That page level read is the idea behind opening an MDF without SQL Server. From there, the MDF converter produces the CSV and the schema script, the SQL Server migration hub lays out the broader set of destinations and the generic SQL Server converter shows which formats it writes. A file that is too far gone should go through SQL Database Recovery before any export.

No server left to export from?

Univik reads the SQL Server MDF straight off the disk and writes each table to CSV that SQLite loads directly, with no instance running and damage tolerated.



Export the MDF to CSV

Frequently Asked Questions

How do I Convert a SQL Server Database to SQLite?

Export each SQL Server table to a CSV file and script the CREATE TABLE statements, then load them into a new SQLite file with the sqlite3 tool, running the schema first and importing the CSV files after. There is no server to server tool because SQLite has no server, so every method comes down to an export and a load. When the SQL Server is offline, read the MDF directly to CSV instead.

Does SQLite Have a Decimal or Money Type?

No. SQLite has no exact decimal type, so a DECIMAL or MONEY column lands in REAL, which is binary floating point and cannot store most cents values exactly. Store money as an INTEGER count of cents and divide in the application, or use the SQLite decimal extension for exact decimal math. Leaving money in a plain REAL lets totals drift by a cent over many rows.

How Are Dates Stored When Moving to SQLite?

SQLite has no native date or datetime type. Dates are stored as text in an ISO8601 string like 2026-03-14 09:41:07, or sometimes as a number. Export your SQL Server dates in ISO8601 format so SQLite’s built in date functions read them directly. A regional date format will break comparisons and sorting once the data is in SQLite.

Do Stored Procedures Move from SQL Server to SQLite?

No. SQLite has no stored procedures and no procedural language, so any logic written in T-SQL does not come across. That logic moves into your application code instead. SQLite does support triggers, but they are limited compared with SQL Server, so plan to rebuild business logic outside the database rather than expecting it to port.

Can I Migrate to SQLite if the SQL Server Is Gone?

Yes. Because the target is a file, you do not need a running server, only the data. A file first tool reads the SQL Server MDF on disk and writes each table to CSV for a SQLite import, with no server needed. Univik takes this route and can lift tables from a detached or damaged database that a normal export could not reach.

Is SQLite a Good Replacement for a SQL Server Database?

It depends on the job. SQLite is excellent as an embedded database inside an application, a portable single file or a local cache, where its simplicity is the point. It is a poor fit for a shared database with many concurrent writers or server side logic, since it allows one writer at a time and has no stored procedures. Match the choice to how the database will be used.

About the Author

Written and maintained by Leena Taylor Paul and the Univik team, developers of Windows data conversion and recovery software since 2013. The SQLite moves that reach us are the ones where the SQL Server is already gone, an MDF pulled off a retired box that has to land in one portable file. Reading that file offline is the part we do all day. Last verified July 2026. Moving a database off a server that will not start? Contact our support team.