<title>Quirks, Caveats, and Gotchas In SQLite</title>
<center><font color="red" size=6><b>
This document is a work in progress.
The SQL language is a "standard".
Even so, no two SQL database engines work exactly alike.
Every SQL implementation has it own peculiarities and oddities,
and SQLite is no exception to this rule.
This document strives to highlight the principal differences
between SQLite and other SQL implementations, as an aid to developers
that are porting to or from SQLite or who are trying to build a
system that works across multiple database engines.
If you are an SQLite user whose has stumbled over some quirk of
SQLite that is not mentioned here, please send us an email so that
we can document the problem.
<h1>SQLite Is Embedded, Not Client-Server</h1>
When ever comparing SQLite to other SQL database engines like
SQL Server, PostgreSQL, MySQL, or Oracle, it is important first of all
to realize the SQLite is not intended as a replacement or competitor to
any of those systems. SQLite is [serverless]. There is no separate
server process that manages the database. An application interact
with the database engine using function calls, not be sending messages
to a separate process or thread.
The fact that SQLite is embedded and [serverless] instead of being
client/server is a feature, not a bug.
Client/server databases like MySQL, PostgreSQL, SQL Server, Oracle, and
others are an important component of modern systems.
These systems solve an important problem.
But SQLite solves a different problem.
Both SQLite and client/server databases have their role.
Developers who are comparing SQLite against other SQL database engines need
to clearly understand this distinction.
See the [Appropriate Uses For SQLite] document for additional information.
SQLite is very flexible with regard to datatypes.
Some commentators say that SQLite is "weakly typed" and that other
SQL databases are "strongly typed". We consider these terms to be
inaccurate and purgative. We prefer to say that SQLite is "flexibly typed"
and that other SQL databases are "rigidly typed".
See the [datatype|Datatypes in SQLite Version 3] document for a detailed
discussion of the type system in SQLite.
The key point is that SQLite is very forgiving of the type of data that
you put into the database. For example, if a column has a datatype of
"INTEGER" and the application inserts a text string into that column,
SQLite will first try to convert the text string into an integer, just like
every other SQL database engine. Thus, if one inserts <b>'1234'</b> into
an INTEGER column, that value is converted into an integer 1234 and stored.
But, if you insert a non-numeric string like <b>'wxyz'</b> into an INTEGER
column, unlike other SQL databases, SQLite does not throw an error. Instead,
SQLite stores the actual string value in the column.
Similarly, SQLite allows you to store a 2000-character string into a
column of type VARCHAR(50). Other SQL implementions would either throw
an error or truncate the string. SQLite stores the entire 2000-character
string with no loss of information and without complaint.
Where this ends up causing problems is when developers do some initial
coding work using SQLite and get their application working, but then try
to convert to another database like PostgreSQL or SQL Server for deployment.
If the application is initially taking advantage of SQLite's flexible typing,
then it will fail when moved to another database that uses a more rigid
and unforgiving type enforcement policy.
Flexible typing is considered a feature of SQLite, not a bug.
Nevertheless, we recognize that this feature does sometimes cause
confusion and pain for developers who are acustomed to working with
other databases that are more judgemental with regard to data types.
In retrospect, perhaps it would have been better if SQLite had merely
implemented an ANY datatype so that developers could explicitly state
when they wanted to use flexible typing, rather than making flexible
typing the default.
But that is not something that can be changed now without breaking
the millions of applications and trillions of database files that
already use SQLite's flexible typing feature.
<h2>No Separate BOOLEAN Datatype</h2>
Unlike most other SQL implementations,
SQLite does not have a separate BOOLEAN data type.
Instead, TRUE and FALSE are (normally) represented as integers 1 and 0,
This does not seem to cause many problems, as we seldom get complaints
about it. But it is important to recognize.
Beginning with SQLite [version 3.23.0] ([dateof:3.23.0]), SQLite also
recognizes TRUE and FALSE keywords as aliases for integer values 1 and 0,
This provides better compatibility with other SQL implementations.
But to retain backwards compatibility, if there are columns named TRUE or
FALSE, then the keywords are treated as identifiers referencing those
columns, rather than BOOLEAN literals.
<h2>No Separate DATETIME Datatype</h2>
SQLite as no DATETIME datatype.
Instead, dates and times can be stored in any of these ways:
<li> As a TEXT string in the ISO-8610 format. Example: '2018-04-02 12:13:46'.
<li> As an INTEGER number of seconds since 1970 (also known as "unix time").
<li> As a REAL value that is the fractional
[https://en.wikipedia.org/wiki/Julian_day|Julian day number].
The built-in [date and time functions] of SQLite understand date/times in
all of the formats above, and can freely change between them.
Which format you use, is entirely up to your application.
<h1>Foreign Key Enforcement Is Off By Default</h1>
<p>SQLite has parsed foreign key constraints for time out of mind,
but added the ability to actually enforce those constraints much later,
with [version 3.6.19] ([dateof:3.6.19]). By the time foreign key constraint
enforcement was added, there where already countless millions of databases
in circulation that contained foreign key constraints, some of which
were not correct. To avoid breaking those legacy databases, foreign key
constraint enforcement is turned off by default in SQLite.
<p>Applications can activate foreign key enforcement at run-time using
the [PRAGMA foreign_keys] statement. Or, foreign key enforcement can
be activated at compile-time using the
[-DSQLITE_DEFAULT_FOREIGN_KEYS=1] compile-time option.
<h1>PRIMARY KEYs Can Sometimes Contain NULLs</h1>
Usually (the exceptions are [INTEGER PRIMARY KEY] tables
[WITHOUT ROWID] tables) a PRIMARY KEY in an SQLite table is really
the same as a UNIQUE constraint. Due to an historical oversight,
the column values of such a PRIMARY KEY are allowed to be NULL.
This is a bug, but by the time the problem was discovered there
where so many databases in circulation that depended on the bug that
the decision was made to support the bugging behavior moving forward.
The value of an [INTEGER PRIMARY KEY] column must always be a
non-NULL integer. The PRIMARY KEY columns of a [WITHOUT ROWID]
table are also required to be non-NULL.
<h1>Aggregate Queries Can Contain Non-Aggregate Result Columns
That Are Not In The GROUP BY Clause</h1>
In most SQL implementations, output columns of an aggregate query
may only reference aggregate functions or columns named in the
GROUP BY clause. It does not make good sense to reference an ordinary
column in an aggregate query because each output row might be composed
from two more more rows in the input table(s).
SQLite does not impose this restriction.
The output columns from an aggregate query can be arbitrary expressions
that include columns not found in GROUP BY clause.
This feature has two uses:
With SQLite (but not any other SQL implementation that we know of) if
an aggregate query contains a single min() or max() function, then the
values of columns used in the output are taken from the row where
the min() or max() value was achieved. If two or more rows have the
same min() or max() value, then the columns values will be chosen arbitrarily
from one of those rows.
For example to find the highest paid employee:
SELECT max(salary), first_name, last_name FROM employee;
In the query above, the values for the first_name and last_name columns
will correspond to the row that satisfied the max(salary) condition.
If a query contains no aggregate functions at all, then a GROUP BY
clause can be added as a substitute of DISTINCT ON clause. In other words,
output rows are filtered so that only one row is shows for each distinct
set of values in the GROUP BY clause. If two or more output rows would
have otherwise had the same set of values for the GROUP BY columns, then
the one of the rows is chosen arbitrarily.
<h1>Does Not Do Full Unicode Case Folding By Default</h1>
SQLite does not know about the upper-case/lower-case distinction
for all unicode characters. SQL functions like
upper() and lower() only work on ASCII characters. There are two
reasons for this:
<li> Though stable now, when SQLite was first designed, the rules for
unicode case folding where still in flux. That means that the
behavior might have changed with each new unicode release, disrupting
applications and corrupting indexes in the process.
<li> The tables necessary to do full and proper unicode case folding are
larger than the whole SQLite library.
Full unicode case folding is supported in SQLite if it is compiled
with the [-DSQLITE_ENABLE_ICU] option and linked against the
[http://site.icu-project.org/|International Components for Unicode]
<h1>Double-quoted String Literals Are Accepted</h1>
The SQL standard says that one should use double-quotes around identifiers
and single-quotes around string literals.
<li> <tt>"this is a legal SQL column name"</tt>
<li> <tt>'this is an SQL string literal'</tt>
SQLite accepts both of the above. But, in an effort to be compatible
with MySQL 3.x (which was widely popular when SQLite was first being
designed) will also use content contained in double-quotes as a string
literal if the content does not match any valid identifier.
An unfortunate side-effect of this is that a misspelled double-quoted
identifier will be interpreted as a string literal, rather than generating
Another problem is that this behavior allows developers who are new to
the SQL language to continue using double-quoted string literals when they
really need to learn to use the correct single-quoted string literal form.
In hindsight, we should not have tried to make SQLite accept MySQL 3.x
syntax, and should have never allowed double-quoted string literals.
However, we continue to support that capability to avoid breaking legacy
<h1>Keywords Can Often Be Used As Identifiers</h1>
The SQL language is rich in keywords.
Most SQL implementations do not allow keywords to be used as identifiers
(the names of table or columns) unless they are enclosed in double-quotes.
But SQLite is more flexible. Many keywords can be used as identifiers without
needing to be quoted, as long as those keywords are used in a context where
it is clear that they are intended to be an identifier.
For example, the following statement is valid in SQLite:
CREATE TABLE union(true INT, with BOOLEAN);
The same SQL statement will fail on every other SQL implementation that
we know of due to the use of keywords "union", "true", and "with" as
<h1>Dubious SQL Is Allowed Without Any Error Or Warning</h1>
The original implementation of SQLite sought to follow
[https://en.wikipedia.org/wiki/Robustness_principle|Postel's Law] which
states in part "Be liberal in what you accept".
This used to be considered good design - that a system would accept
dodgy inputs and try to do the best it could without complaining too much.
But lately, people have come to realize that it is sometimes better to
be strict in what you accept, so as to more easily find errors in the
<h1>AUTOINCREMENT Does Not Work The Same As MySQL</h1>