Convert SQLite to SQL Server
Drop a SQLite database file (.db, .sqlite, .sqlite3) or the SQL that sqlite3's .dump writes, and download a script for SQL Server with the same tables, keys, indexes and data. Each column gets the type its values fit, and every value SQL Server can't hold as it is is listed in the report.
18 rows converted, no fixes, none rejected
Tables in shop_2024_sqlite.sqlSQLite dump · written by sqlite3 · utf-8
customers8 rows
7 columns id bigint not null name nvarchar(max) not null email nvarchar(450) not null country nvarchar(max) not null is_active bit not null signed_up date created_at datetime2(0) not null orders10 rows
9 columns id bigint not null customer_id bigint not null status nvarchar(450) not null total decimal(10,2) not null items int not null ordered_at datetime2(0) not null shipped_at datetime2(0) note nvarchar(max) receipt varbinary(max)
Not converted
- View order_totals
- Trigger orders_shipped
Each database writes these in its own language. Rewrite them for SQL Server.
-- Written by IntoSQL from a SQLite dump. Load it with: sqlcmd -b -f 65001 -d <database> -i <file>
SET NOCOUNT ON;
SET ANSI_NULLS ON;
SET QUOTED_IDENTIFIER ON;
SET ANSI_PADDING ON;
SET ANSI_WARNINGS ON;
SET ARITHABORT ON;
SET CONCAT_NULL_YIELDS_NULL ON;
SET NUMERIC_ROUNDABORT OFF;
GO
CREATE TABLE [dbo].[customers] (
[id] bigint IDENTITY(9, 1) NOT NULL,
[name] nvarchar(max) COLLATE Latin1_General_100_BIN2 NOT NULL,
[email] nvarchar(450) COLLATE Latin1_General_100_CI_AS_SC NOT NULL,
[country] nvarchar(max) COLLATE Latin1_General_100_BIN2 DEFAULT N'US' NOT NULL,
[is_active] bit DEFAULT 1 NOT NULL,
[signed_up] date NULL,
[created_at] datetime2(0) DEFAULT SYSDATETIME() NOT NULL,
CONSTRAINT [PK_customers] PRIMARY KEY ([id])
);
GO
CREATE TABLE [dbo].[orders] (
[id] bigint IDENTITY(11, 1) NOT NULL,
[customer_id] bigint NOT NULL,
[status] nvarchar(450) COLLATE Latin1_General_100_BIN2 DEFAULT N'pending' NOT NULL,
[total] decimal(10,2) NOT NULL,
[items] int DEFAULT 1 NOT NULL,
[ordered_at] datetime2(0) NOT NULL,
[shipped_at] datetime2(0) NULL,
[note] nvarchar(max) COLLATE Latin1_General_100_BIN2 NULL,
[receipt] varbinary(max) NULL,
CONSTRAINT [check1] CHECK ([status] IN (N'pending', N'paid', N'shipped', N'cancelled')),
CONSTRAINT [PK_orders] PRIMARY KEY ([id])
);
GO
SET IDENTITY_INSERT [dbo].[customers] ON;
INSERT INTO [dbo].[customers] ([id], [name], [email], [country], [is_active], [signed_up], [created_at]) VALUES
(1,N'Ana Martínez',N'ana@example.com',N'MX',1,'2023-01-15','2023-01-15 10:24:00');
SET IDENTITY_INSERT [dbo].[customers] OFF;
GO
SET IDENTITY_INSERT [dbo].[customers] ON;
INSERT INTO [dbo].[customers] ([id], [name], [email], [country], [is_active], [signed_up], [created_at]) VALUES
(2,N'Zoë Müller',N'zoe@example.de',N'DE',1,'2023-02-03','2023-02-03 08:11:42');
SET IDENTITY_INSERT [dbo].[customers] OFF;
GO
SET IDENTITY_INSERT [dbo].[customers] ON;
INSERT INTO [dbo].[customers] ([id], [name], [email], [country], [is_active], [signed_up], [created_at]) VALUES
(3,N'José Peña',N'jose@example.es',N'ES',0,'2023-03-21','2023-03-21 19:05:17');
SET IDENTITY_INSERT [dbo].[customers] OFF;
GO
SET IDENTITY_INSERT [dbo].[customers] ON;
INSERT INTO [dbo].[customers] ([id], [name], [email], [country], [is_active], [signed_up], [created_at]) VALUES
(4,N'李雷',N'lilei@example.cn',N'CN',1,'2023-05-09','2023-05-09 02:40:00');
SET IDENTITY_INSERT [dbo].[customers] OFF;
GO
SET IDENTITY_INSERT [dbo].[customers] ON;
INSERT INTO [dbo].[customers] ([id], [name], [email], [country], [is_active], [signed_up], [created_at]) VALUES
(5,N'Noah O''Brien',N'noah@example.ie',N'IE',1,NULL,'2023-06-30 14:00:00');
SET IDENTITY_INSERT [dbo].[customers] OFF;
GO
SET IDENTITY_INSERT [dbo].[customers] ON;
INSERT INTO [dbo].[customers] ([id], [name], [email], [country], [is_active], [signed_up], [created_at]) VALUES
(6,N'Amélie Laurent',N'amelie@example.fr',N'FR',1,'2024-01-02','2024-01-02 09:30:00');
SET IDENTITY_INSERT [dbo].[customers] OFF;
GO
SET IDENTITY_INSERT [dbo].[customers] ON;
INSERT INTO [dbo].[customers] ([id], [name], [email], [country], [is_active], [signed_up], [created_at]) VALUES
(7,N'Ravi Patel',N'ravi@example.in',N'IN',0,'2024-02-29','2024-02-29 23:59:59');
SET IDENTITY_INSERT [dbo].[customers] OFF;
GO
SET IDENTITY_INSERT [dbo].[customers] ON;
INSERT INTO [dbo].[customers] ([id], [name], [email], [country], [is_active], [signed_up], [created_at]) VALUES
(8,N'Sofia Rossi 🌻',N'sofia@example.it',N'IT',1,'2024-04-12','2024-04-12 12:12:12');
SET IDENTITY_INSERT [dbo].[customers] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [status], [total], [items], [ordered_at], [shipped_at], [note], [receipt]) VALUES
(1,1,N'shipped',129.9,3,'2024-03-01 10:00:00','2024-03-02 16:30:00',NULL,NULL);
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [status], [total], [items], [ordered_at], [shipped_at], [note], [receipt]) VALUES
(2,2,N'paid',54,1,'2024-03-02 11:15:00',NULL,N'Gift wrap 🎁',NULL);
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [status], [total], [items], [ordered_at], [shipped_at], [note], [receipt]) VALUES
(3,1,N'cancelled',12.5,1,'2024-03-03 09:45:00',NULL,N'Customer changed their mind',NULL);
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [status], [total], [items], [ordered_at], [shipped_at], [note], [receipt]) VALUES
(4,4,N'shipped',980,12,'2024-03-05 02:10:00','2024-03-07 08:00:00',N'请小心轻放',0x25504446);
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [status], [total], [items], [ordered_at], [shipped_at], [note], [receipt]) VALUES
(5,5,N'pending',45.75,2,'2024-03-08 17:20:00',NULL,CAST(N'' AS nvarchar(max))+N'Leave at the door;'+NCHAR(10)+N'ring twice',NULL);
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [status], [total], [items], [ordered_at], [shipped_at], [note], [receipt]) VALUES
(6,6,N'shipped',310.4,5,'2024-03-10 13:00:00','2024-03-11 09:00:00',N'Fragile: "glass"',NULL);
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [status], [total], [items], [ordered_at], [shipped_at], [note], [receipt]) VALUES
(7,8,N'paid',76.2,2,'2024-03-11 18:45:00',NULL,NULL,NULL);
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [status], [total], [items], [ordered_at], [shipped_at], [note], [receipt]) VALUES
(8,2,N'paid',19.99,1,'2024-03-12 08:00:00',NULL,N'Paid at the counter',NULL);
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [status], [total], [items], [ordered_at], [shipped_at], [note], [receipt]) VALUES
(9,3,N'shipped',64,2,'2024-03-14 15:30:00',NULL,N'Shipping date never recorded',NULL);
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [status], [total], [items], [ordered_at], [shipped_at], [note], [receipt]) VALUES
(10,7,N'paid',1499,1,'2024-03-15 21:05:00',NULL,N'C:\Users\ravi\invoice.pdf',NULL);
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
-- Indexes, foreign keys, counters and triggers, after the rows.
CREATE UNIQUE INDEX [email] ON [dbo].[customers] ([email]);
CREATE INDEX [idx_orders_customer] ON [dbo].[orders] ([customer_id]);
CREATE INDEX [idx_orders_status_date] ON [dbo].[orders] ([status], [ordered_at]);
ALTER TABLE [dbo].[orders] WITH NOCHECK ADD CONSTRAINT [FK_orders] FOREIGN KEY ([customer_id]) REFERENCES [dbo].[customers] ([id]) ON DELETE CASCADE;
BEGIN TRY ALTER TABLE [dbo].[orders] WITH CHECK CHECK CONSTRAINT [FK_orders]; END TRY BEGIN CATCH PRINT N'Foreign key ' + N'[FK_orders]' + N' has rows without a parent: it stays unchecked, and new rows are checked.'; END CATCH;
DECLARE @next decimal(38,0) = (SELECT MAX([id]) FROM [dbo].[customers]); IF @next IS NOT NULL BEGIN IF @next < 8 SET @next = 8; DBCC CHECKIDENT (N'[dbo].[customers]', RESEED, @next) WITH NO_INFOMSGS; END;
GO
DECLARE @next decimal(38,0) = (SELECT MAX([id]) FROM [dbo].[orders]); IF @next IS NOT NULL BEGIN IF @next < 10 SET @next = 10; DBCC CHECKIDENT (N'[dbo].[orders]', RESEED, @next) WITH NO_INFOMSGS; END;
GO
GO
Fixes
Nothing needed fixing.
Needs a look
- 2 views, triggers, routines or events aren't converted; the report lists each one. Recreate them by hand in SQL Server.
- The check check1 on customers can't be written for SQL Server (function char_length), so it isn't created. Write the check by hand if it matters.
Your file is converted on this computer and never uploaded.
Other samples:
What changes from SQLite to SQL Server
Each row is a rule the converter applies, with the report line it writes when the rule changes something in your data.
declared types (any text: VARCHAR(255), DATETIME, BOOLEAN, JSON or none)
the type every value in the column fits
SQLite keeps any value in any column, so the declared type is a hint: each column gets the type all of its values fit, and a column holding mixed values keeps their text.
BOOLEAN, BOOL
bit
When every value is 0 or 1; a column that also holds other values keeps them as numbers or text.
TEXT, VARCHAR(n), CHAR(n), CLOB
nvarchar(n) or nvarchar(max)
SQLite never enforces a length; a declared length holds only when every value fits it.
DECIMAL(p,s), NUMERIC
the exact type when every value fits it, else a double
SQLite stores these as integers or doubles; a declared precision holds only when the values fit it.
BLOB, and columns holding bytes
varbinary(max)
Bytes stay bytes; text and numbers in the same column are kept as their bytes.
DATE, DATETIME, TIMESTAMP holding ISO 8601 text
date, datetime2(p)
When every value is a date or a date and time; epoch numbers and other text keep their values as they are.
JSON holding JSON text
nvarchar(max)
When every value is a JSON object or array.
Infinity in REAL columns
NULL
SQL Server's doubles have no infinity, so those values become NULL and the report counts them.
Report line: invalid_value_null
INTEGER PRIMARY KEY, AUTOINCREMENT
IDENTITY, continuing after the highest id or sqlite_sequence's
Only a column declared exactly INTEGER PRIMARY KEY is the rowid; every row keeps its id, and the counter starts after the highest id ever handed out.
COLLATE NOCASE
a case-insensitive collation
SQL Server's usual collation ignores case for every column, as NOCASE did for ASCII letters.
generated columns
computed columns, PERSISTED when STORED
SQL Server computes them the same way.
generated columns with functions IntoSQL doesn't write, and the || operator
a plain column
A formula that can't be written leaves a plain column and a report line; the dump has no values for generated columns.
Report line: generated_not_translated
defaults with functions IntoSQL doesn't write (strftime, randomblob)
no default
A default that can't be written is left out and reported; CURRENT_TIMESTAMP and datetime('now') are written.
Report line: default_not_translated
CHECK constraints
translated
The same checks, tested as each row loads.
CHECK constraints with functions IntoSQL doesn't write
not created
A check that can't be written is left out and reported.
Report line: check_skipped
partial and expression indexes
not created
IntoSQL writes indexes on whole columns; create the others again after loading.
Report line: index_skipped
foreign keys (unenforced unless PRAGMA foreign_keys=ON)
after the data WITH NOCHECK, then checked
SQLite checks foreign keys only when told to, so rows without a parent are common: they load, and the key is left unchecked with a warning instead of stopping the load.
ON DELETE or ON UPDATE that cascades in a cycle or along a second path
NO ACTION
SQL Server refuses cascades that could reach a table twice.
Report line: foreign_key_action_dropped
VARCHAR(n) holding characters past U+FFFF (emoji)
nvarchar(2n), or nvarchar(max) past 4000
nvarchar counts UTF-16 units and an emoji takes two, so the column gets room for every value.
Report line: text_length_widened
foreign keys to tables the dump doesn't create
not created
A key can't name a table that isn't there.
Report line: foreign_key_skipped
primary keys holding NULL, and NOT NULL columns with values that become NULL
NOT NULL dropped
SQLite lets a primary key other than the rowid hold NULL; the column can't be NOT NULL in the target, or the load would stop.
Report line: not_null_relaxed
views, triggers and virtual tables (FTS, R*Tree)
listed in the report
Their code is written in SQLite's own language, and virtual tables need SQLite's modules; they are listed for you to recreate. Their internal tables aren't copied.
Report line: not_converted_objects
dumps without CREATE TABLE (--data-only)
types read from the values
A data-only dump has only rows, so the types come from the values and the columns are numbered.
Report line: no_schema
expressions in INSERT values other than the text functions dumps write
NULL
unistr(), replace(), char() and || are evaluated as the shell writes them; other expressions can't be without a database.
Report line: expression_value
NUL characters in text
kept
The sqlite3 shell and Python cut text at a NUL character when they dump it, so only what precedes it is in the dump.
text that isn't valid UTF-8
the bad bytes as the replacement character
Dumps write such text as raw bytes; the script is UTF-8.
Report line: invalid_utf8_replaced
text decoded twice (ñ)
copied as it is
The dump holds exactly these characters, so they are copied; the report points them out.
Report line: mojibake_suspected
| In SQLite | In SQL Server | Report line |
|---|---|---|
| declared types (any text: VARCHAR(255), DATETIME, BOOLEAN, JSON or none) | the type every value in the column fitsSQLite keeps any value in any column, so the declared type is a hint: each column gets the type all of its values fit, and a column holding mixed values keeps their text. | |
| BOOLEAN, BOOL | bitWhen every value is 0 or 1; a column that also holds other values keeps them as numbers or text. | |
| TEXT, VARCHAR(n), CHAR(n), CLOB | nvarchar(n) or nvarchar(max)SQLite never enforces a length; a declared length holds only when every value fits it. | |
| DECIMAL(p,s), NUMERIC | the exact type when every value fits it, else a doubleSQLite stores these as integers or doubles; a declared precision holds only when the values fit it. | |
| BLOB, and columns holding bytes | varbinary(max)Bytes stay bytes; text and numbers in the same column are kept as their bytes. | |
| DATE, DATETIME, TIMESTAMP holding ISO 8601 text | date, datetime2(p)When every value is a date or a date and time; epoch numbers and other text keep their values as they are. | |
| JSON holding JSON text | nvarchar(max)When every value is a JSON object or array. | |
| Infinity in REAL columns | NULLSQL Server's doubles have no infinity, so those values become NULL and the report counts them. | invalid_value_null |
| INTEGER PRIMARY KEY, AUTOINCREMENT | IDENTITY, continuing after the highest id or sqlite_sequence'sOnly a column declared exactly INTEGER PRIMARY KEY is the rowid; every row keeps its id, and the counter starts after the highest id ever handed out. | |
| COLLATE NOCASE | a case-insensitive collationSQL Server's usual collation ignores case for every column, as NOCASE did for ASCII letters. | |
| generated columns | computed columns, PERSISTED when STOREDSQL Server computes them the same way. | |
| generated columns with functions IntoSQL doesn't write, and the || operator | a plain columnA formula that can't be written leaves a plain column and a report line; the dump has no values for generated columns. | generated_not_translated |
| defaults with functions IntoSQL doesn't write (strftime, randomblob) | no defaultA default that can't be written is left out and reported; CURRENT_TIMESTAMP and datetime('now') are written. | default_not_translated |
| CHECK constraints | translatedThe same checks, tested as each row loads. | |
| CHECK constraints with functions IntoSQL doesn't write | not createdA check that can't be written is left out and reported. | check_skipped |
| partial and expression indexes | not createdIntoSQL writes indexes on whole columns; create the others again after loading. | index_skipped |
| foreign keys (unenforced unless PRAGMA foreign_keys=ON) | after the data WITH NOCHECK, then checkedSQLite checks foreign keys only when told to, so rows without a parent are common: they load, and the key is left unchecked with a warning instead of stopping the load. | |
| ON DELETE or ON UPDATE that cascades in a cycle or along a second path | NO ACTIONSQL Server refuses cascades that could reach a table twice. | foreign_key_action_dropped |
| VARCHAR(n) holding characters past U+FFFF (emoji) | nvarchar(2n), or nvarchar(max) past 4000nvarchar counts UTF-16 units and an emoji takes two, so the column gets room for every value. | text_length_widened |
| foreign keys to tables the dump doesn't create | not createdA key can't name a table that isn't there. | foreign_key_skipped |
| primary keys holding NULL, and NOT NULL columns with values that become NULL | NOT NULL droppedSQLite lets a primary key other than the rowid hold NULL; the column can't be NOT NULL in the target, or the load would stop. | not_null_relaxed |
| views, triggers and virtual tables (FTS, R*Tree) | listed in the reportTheir code is written in SQLite's own language, and virtual tables need SQLite's modules; they are listed for you to recreate. Their internal tables aren't copied. | not_converted_objects |
| dumps without CREATE TABLE (--data-only) | types read from the valuesA data-only dump has only rows, so the types come from the values and the columns are numbered. | no_schema |
| expressions in INSERT values other than the text functions dumps write | NULLunistr(), replace(), char() and || are evaluated as the shell writes them; other expressions can't be without a database. | expression_value |
| NUL characters in text | keptThe sqlite3 shell and Python cut text at a NUL character when they dump it, so only what precedes it is in the dump. | |
| text that isn't valid UTF-8 | the bad bytes as the replacement characterDumps write such text as raw bytes; the script is UTF-8. | invalid_utf8_replaced |
| text decoded twice (ñ) | copied as it isThe dump holds exactly these characters, so they are copied; the report points them out. | mojibake_suspected |
Load the script into SQL Server
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.
sqlcmd -b -f 65001 -S localhost -d mydb -i sqlite.sql_server.sqlForeign keys are added WITH NOCHECK and then checked. If a row points to a row that doesn't exist, the script says which constraint and leaves it unchecked, instead of failing half way.
For a large dump, run sqlcmd on the same machine as the server. Each batch of up to 1000 rows commits on its own, so the transaction log stays small.
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 and triggers are written in SQLite's own language. They are listed in the report by name, never dropped silently, so you can rewrite them for SQL Server. Virtual tables, such as FTS5 and R*Tree, are listed too, and their shadow tables are left out.
Questions about SQLite to SQL Server
Not answered here? Write to us from the contact page and a person replies. Contact page
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?
The declared type is a hint and the values decide: a column becomes bigint, decimal, float, date, datetime2, bit, varbinary or nvarchar when every value fits it. Text compares exactly, as in SQLite, and COLLATE NOCASE ignoring case.
Are INTEGER PRIMARY KEY and AUTOINCREMENT kept?
Yes. An INTEGER PRIMARY KEY becomes an IDENTITY column, every row keeps its id, and the next id continues after the highest one, or after sqlite_sequence's value with AUTOINCREMENT.
What about views and triggers?
They are listed in the report by name and not converted, because each database writes them in its own language.
Which SQL Server versions does the script load on?
SQL Server 2017 to 2025. The same script loads on each of them, stopping at the first error if anything goes wrong.