1. SQLite 中的数据类型

大多数 SQL 数据库引擎(据我们所知,除了 SQLite 之外的每个 SQL 数据库引擎)都使用静态的、严格的类型。使用静态类型时,值的数据类型由其容器(存储值的特定列)决定。

SQLite 使用更通用的动态类型系统。在 SQLite 中,值的数据类型与值本身相关联,而不是与其容器相关联。SQLite 的动态类型系统向后兼容其他数据库引擎的更常见的静态类型系统,因为在静态类型数据库上工作的 SQL 语句在 SQLite 中的工作方式相同。然而,SQLite 中的动态类型允许它做一些在传统严格类型的数据库中做不到的事情。 灵活的类型是SQLite 的一个特性,而不是一个错误。

更新:从版本 3.37.0 (2021-11-27) 开始,SQLite 为 喜欢这种东西的开发人员提供了执行严格类型强制的 STRICT 表。

2.存储类和数据类型

存储在 SQLite 数据库中(或由数据库引擎操作)的每个值都具有以下存储类之一:

  • NULL. The value is a NULL value.

  • INTEGER. The value is a signed integer, stored in 0, 1, 2, 3, 4, 6, or 8 bytes depending on the magnitude of the value.

  • REAL. The value is a floating point value, stored as an 8-byte IEEE floating point number.

  • TEXT. The value is a text string, stored using the database encoding (UTF-8, UTF-16BE or UTF-16LE).

  • BLOB. The value is a blob of data, stored exactly as it was input.

存储类比数据类型更通用。例如,INTEGER 存储类包括 7 种不同长度的整数数据类型。 这在磁盘上有所不同。 但是一旦 INTEGER 值从磁盘读取到内存中进行处理,它们就会被转换为最通用的数据类型(8 字节有符号整数)。因此在大多数情况下,“存储类”与“数据类型”没有区别,这两个术语可以互换使用。

SQLite 版本 3 数据库中的任何列(INTEGER PRIMARY KEY列除外)都可用于存储任何存储类的值。

SQL 语句中的所有值,无论它们是嵌入在 SQL 语句文本中的文字还是绑定到 预编译 SQL 语句的参数, 都具有隐式存储类。在下面描述的情况下,数据库引擎可能会在查询执行期间在数字存储类(INTEGER 和 REAL)和 TEXT 之间转换值。

2.1. 布尔数据类型

SQLite 没有单独的布尔存储类。相反,布尔值存储为整数 0 (false) 和 1 (true)。

从版本 3.23.0 (2018-04-02) 开始,SQLite 识别关键字“TRUE”和“FALSE”,但这些关键字实际上只是整数文字 1 和 0 的替代拼写。

2.2. 日期和时间数据类型

SQLite 没有为存储日期和/或时间预留的存储类。相反,SQLite 的内置日期和时间函数能够将日期和时间存储为 TEXT、REAL 或 INTEGER 值:

  • TEXT作为 ISO8601 字符串(“YYYY-MM-DD HH:MM:SS.SSS”)。
  • REAL作为 Julian 天数,根据 proleptic Gregorian calendar,从公元前 4714 年 11 月 24 日格林威治中午开始的天数。
  • INTEGER作为 Unix 时间,自 1970-01-01 00:00:00 UTC 以来的秒数。

应用程序可以选择以这些格式中的任何一种存储日期和时间,并使用内置的 日期和时间函数在格式之间自由转换。

3.类型亲和力

使用严格类型的 SQL 数据库引擎通常会尝试自动将值转换为适当的数据类型。考虑一下:

CREATE TABLE t1(a INT, b VARCHAR(10));
INSERT INTO t1(a,b) VALUES('123',456);

严格类型的数据库在执行插入之前会将字符串“123”转换为整数 123,将整数 456 转换为字符串“456”。

为了最大限度地提高 SQLite 和其他数据库引擎之间的兼容性,并使上面的示例能够像在其他 SQL 数据库引擎上一样在 SQLite 上运行,SQLite 支持列上的“类型亲和性”概念。列的类型关联是存储在该列中的数据的推荐类型。这里的重要思想是推荐类型,而不是必需的。任何列仍然可以存储任何类型的数据。只是有些列,如果有选择,会更喜欢使用一种存储类而不是另一种。列的首选存储类称为其“亲和性”。

SQLite 3 数据库中的每一列都分配了以下类型关联之一:

  • TEXT
  • NUMERIC
  • INTEGER
  • REAL
  • BLOB

(历史记录:“BLOB”类型亲和力过去称为“NONE”。但该术语很容易与“无亲和力”混淆,因此被重命名。)

具有 TEXT 亲和性的列使用 NULL、TEXT 或 BLOB 存储类存储所有数据。如果将数字数据插入具有 TEXT 亲和性的列中,则在存储之前将其转换为文本形式。

具有 NUMERIC 亲和力的列可能包含使用所有五个存储类的值。将文本数据插入 NUMERIC 列时,如果文本是格式正确的整数或实数,则文本的存储类别将分别转换为 INTEGER 或 REAL(按优先顺序)。如果 TEXT 值是格式良好的整数文字,但太大而无法放入 64 位带符号整数中,则会将其转换为 REAL。对于 TEXT 和 REAL 存储类之间的转换,仅保留数字的前 15 个有效十进制数字。如果 TEXT 值不是格式正确的整数或真实文字,则该值将存储为 TEXT。出于本段的目的,十六进制整数文字不被视为格式正确且存储为 TEXT。(这样做是为了与之前的 SQLite 版本保持历史兼容性version 3.8.6 2014-08-15 where hexadecimal integer literals were first introduced into SQLite.) 如果将可以精确表示为整数的浮点值插入到具有 NUMERIC 亲和力的列中,则该值将转换为整数。不会尝试转换 NULL 或 BLOB 值。

字符串可能看起来像带有小数点和/或指数符号的浮点文字,但只要该值可以表示为整数,NUMERIC affinity 就会将其转换为整数。因此,字符串“3.0e+5”存储在具有 NUMERIC 亲和力的列中,作为整数 300000,而不是浮点值 300000.0。

使用 INTEGER 亲和力的列与具有 NUMERIC 亲和力的列的行为相同。INTEGER 和 NUMERIC affinity 之间的区别仅在CAST 表达式中很明显:表达式“CAST(4.0 AS INT)”返回整数 4,而“CAST(4.0 AS NUMERIC)”将值保留为浮点 4.0。

具有 REAL 亲和力的列的行为类似于具有 NUMERIC 亲和力的列,只是它强制将整数值转换为浮点表示形式。(作为一项内部优化,没有小数部分且存储在具有 REAL 亲和力的列中的小浮点值将作为整数写入磁盘以占用更少的空间,并在读取值时自动转换回浮点数。这优化在 SQL 级别是完全不可见的,只能通过检查数据库文件的原始位来检测。)

具有亲和力 BLOB 的列不会优先于一个存储类而不是另一个存储类,并且不会尝试将数据从一个存储类强制转换为另一个存储类。

3.1. 柱亲和力的测定

对于未声明为STRICT的表,列的亲和性由列的声明类型根据以下规则按所示顺序确定:

  1. 如果声明的类型包含字符串“INT”,则为它分配 INTEGER 亲和力。

  2. 如果列的声明类型包含任何字符串“CHAR”、“CLOB”或“TEXT”,则该列具有 TEXT 亲和性。请注意,VARCHAR 类型包含字符串“CHAR”,因此被分配了 TEXT 亲和性。

  3. 如果列的声明类型包含字符串“BLOB”,或者如果未指定类型,则该列具有亲和力 BLOB。

  4. 如果列的声明类型包含任何字符串“REAL”、“FLOA”或“DOUB”,则该列具有 REAL 亲和力。

  5. 否则,亲和力为 NUMERIC。

请注意,确定列亲和性的规则顺序很重要。声明类型为“CHARINT”的列将同时匹配规则 1 和 2,但第一个规则优先,因此列关联将为 INTEGER。

3.1.1. 亲缘关系名称示例

下表显示了有多少来自更传统的 SQL 实现的常见数据类型名称被上一节的五个规则转换为亲缘关系。此表仅显示 SQLite 将接受的数据类型名称的一小部分。请注意,SQLite 会忽略类型名称(例如:“ VARCHAR (255)”)后面括号中的数字参数 - SQLite 不会对字符串、BLOB 或数值。

Example Typenames From The
CREATE TABLE Statement
or CAST Expression
Resulting Affinity Rule Used To Determine Affinity
INT
INTEGER
TINYINT
SMALLINT
MEDIUMINT
BIGINT
UNSIGNED BIG INT
INT2
INT8
INTEGER 1
CHARACTER(20)
VARCHAR(255)
VARYING CHARACTER(255)
NCHAR(55)
NATIVE CHARACTER(70)
NVARCHAR(100)
TEXT
CLOB
TEXT 2
BLOB
no datatype specified
BLOB 3
REAL
DOUBLE
DOUBLE PRECISION
FLOAT
REAL 4
NUMERIC
DECIMAL(10,5)
BOOLEAN
DATE
DATETIME
NUMERIC 5

请注意,由于“POINT”末尾的“INT”,声明的“FLOATING POINT”类型将给出 INTEGER 亲和力,而不是 REAL 亲和力。并且“STRING”的声明类型具有 NUMERIC 的亲和力,而不是 TEXT。

3.2. 表达式的亲和性

每个表列都有类型关联(BLOB、TEXT、INTEGER、REAL 或 NUMERIC 之一),但表达式不一定具有关联。

表达式亲和力由以下规则确定:

  • 如果操作数是列表,则 IN 或 NOT IN 运算符的右侧操作数没有亲和力,如果操作数是 SELECT,则与结果集表达式的亲和力相同。

  • 当表达式是对真实表(不是VIEW或子查询)列的简单引用时,表达式与表列具有相同的亲和力。

    • 列名两边的括号将被忽略。因此,如果 X 和 YZ 是列名,则 (X) 和 (YZ) 也被视为列名并且具有对应列的亲和性。

    • 任何应用于列名的运算符,包括无操作一元“+”运算符,都会将列名转换为始终没有亲和力的表达式。因此,即使 X 和 YZ 是列名,表达式 +X 和 +YZ 也不是列名并且没有亲和力。

  • “CAST( expr AS type )”形式的表达式具有与声明类型为“ type ”的列相同的亲和力

  • COLLATE 运算符与其左侧操作数具有相同的亲和力。

  • 否则,表达式没有亲和力。

3.3. 视图和子查询的列亲和力

VIEW或 FROM 子查询的“列”实际上是实现 VIEW 或子查询的SELECT语句的结果集中的表达式。因此,VIEW 或子查询的列的亲和力由上面的表达式亲和力规则确定。考虑一个例子:

CREATE TABLE t1(a INT, b TEXT, c REAL);
CREATE VIEW v1(x,y,z) AS SELECT b, a+c, 42 FROM t1 WHERE b!=11;

v1.x 列的关联将与 t1.b (TEXT) 的关联相同,因为 v1.x 直接映射到 t1.b。但是列 v1.y 和 v1.z 都没有亲和力,因为这些列映射到表达式 a+c 和 42,并且表达式始终没有亲和力。

3.3.1. 复合视图的列亲和力

实现VIEW或 FROM 子查询的SELECT语句是复合 SELECT时那么 VIEW 或子查询的每一列的亲和力将是构成复合的单个 SELECT 语句之一的相应结果列的亲和力。但是,不确定将使用哪个 SELECT 语句来确定关联。不同的构成 SELECT 语句可能用于在查询评估期间的不同时间确定亲和力。选择可能因 SQLite 的不同版本而异。在同一版本的 SQLite 中,选择可能会在一个查询和下一个查询之间发生变化。在同一查询中的不同时间,选择可能不同。因此,您永远无法确定在组成子查询中具有不同亲缘关系的复合 SELECT 列将使用何种亲缘关系。

如果您关心结果的数据类型,最佳做法是避免在复合 SELECT 中混合亲和力。在复合 SELECT 中混合亲和力会导致令人惊讶和不直观的结果。例如,参见论坛帖子 02d7be94d7

3.4. 列亲和行为示例

以下 SQL 演示了 SQLite 如何在将值插入表时使用列亲和力进行类型转换。

CREATE TABLE t1(
    t  TEXT,     -- text affinity by rule 2
    nu NUMERIC,  -- numeric affinity by rule 5
    i  INTEGER,  -- integer affinity by rule 1
    r  REAL,     -- real affinity by rule 4
    no BLOB      -- no affinity by rule 3
);

-- Values stored as TEXT, INTEGER, INTEGER, REAL, TEXT.
INSERT INTO t1 VALUES('500.0', '500.0', '500.0', '500.0', '500.0');
SELECT typeof(t), typeof(nu), typeof(i), typeof(r), typeof(no) FROM t1;
text|integer|integer|real|text

-- Values stored as TEXT, INTEGER, INTEGER, REAL, REAL.
DELETE FROM t1;
INSERT INTO t1 VALUES(500.0, 500.0, 500.0, 500.0, 500.0);
SELECT typeof(t), typeof(nu), typeof(i), typeof(r), typeof(no) FROM t1;
text|integer|integer|real|real

-- Values stored as TEXT, INTEGER, INTEGER, REAL, INTEGER.
DELETE FROM t1;
INSERT INTO t1 VALUES(500, 500, 500, 500, 500);
SELECT typeof(t), typeof(nu), typeof(i), typeof(r), typeof(no) FROM t1;
text|integer|integer|real|integer

-- BLOBs are always stored as BLOBs regardless of column affinity.
DELETE FROM t1;
INSERT INTO t1 VALUES(x'0500', x'0500', x'0500', x'0500', x'0500');
SELECT typeof(t), typeof(nu), typeof(i), typeof(r), typeof(no) FROM t1;
blob|blob|blob|blob|blob

-- NULLs are also unaffected by affinity
DELETE FROM t1;
INSERT INTO t1 VALUES(NULL,NULL,NULL,NULL,NULL);
SELECT typeof(t), typeof(nu), typeof(i), typeof(r), typeof(no) FROM t1;
null|null|null|null|null

4.比较表达式

SQLite 版本 3 具有常用的 SQL 比较运算符集,包括“=”、“==”、“<”、“<=”、“>”、“>=”、“!=”、“”、“IN” ,“不在”,“介于”,“是”和“不是”,。

4.1. 排序

根据以下规则,比较的结果取决于操作数的存储类别:

  • 存储类为 NULL 的值被认为小于任何其他值(包括另一个存储类为 NULL 的值)。

  • INTEGER 或 REAL 值小于任何 TEXT 或 BLOB 值。当一个 INTEGER 或 REAL 与另一个 INTEGER 或 REAL 进行比较时,将执行数值比较。

  • TEXT 值小于 BLOB 值。当比较两个 TEXT 值时,使用适当的整理顺序来确定结果。

  • 比较两个 BLOB 值时,使用 memcmp() 确定结果。

4.2. 比较前的类型转换

在执行比较之前,SQLite 可能会尝试在存储类 INTEGER、REAL 和/或 TEXT 之间转换值。在比较发生之前是否尝试任何转换取决于操作数的类型亲和性。

“应用亲和力”意味着当且仅当转换不会丢失基本信息时,才将操作数转换为特定的存储类。数值总是可以转换成文本。如果文本内容是格式正确的整数或真实文字,但不是十六进制整数文字,则 TEXT 值可以转换为数值。通过简单地将二进制 BLOB 内容解释为当前数据库编码中的文本字符串,将 BLOB 值转换为 TEXT 值。

亲和性在比较之前根据以下规则按所示顺序应用于比较运算符的操作数:

  • 如果一个操作数具有 INTEGER、REAL 或 NUMERIC 亲和力,而另一个操作数具有 TEXT 或 BLOB 或没有亲和力,则将 NUMERIC 亲和力应用于其他操作数。

  • 如果一个操作数具有 TEXT 亲和力而另一个没有亲和力,则将 TEXT 亲和力应用于另一个操作数。

  • 否则,不应用亲和力,并且两个操作数按原样进行比较。

表达式“a BETWEEN b AND c”被视为两个单独的二进制比较“a >= b AND a <= c”,即使这意味着在每个比较中将不同的亲和力应用于“a”。“x IN (SELECT y ...)”形式的比较中的数据类型转换被处理为就好像比较真的是“x=y”一样。表达式“a IN (x, y, z, ...)”等同于“a = +x OR a = +y OR a = +z OR ...”。换句话说,IN 运算符右侧的值(本例中的“x”、“y”和“z”值)被认为没有亲和力,即使它们恰好是列值或 CAST 表达式.

4.3. 比较例

CREATE TABLE t1(
    a TEXT,      -- text affinity
    b NUMERIC,   -- numeric affinity
    c BLOB,      -- no affinity
    d            -- no affinity
);

-- Values will be stored as TEXT, INTEGER, TEXT, and INTEGER respectively
INSERT INTO t1 VALUES('500', '500', '500', 500);
SELECT typeof(a), typeof(b), typeof(c), typeof(d) FROM t1;
text|integer|text|integer

-- Because column "a" has text affinity, numeric values on the
-- right-hand side of the comparisons are converted to text before
-- the comparison occurs.
SELECT a < 40,   a < 60,   a < 600 FROM t1;
0|1|1

-- Text affinity is applied to the right-hand operands but since
-- they are already TEXT this is a no-op; no conversions occur.
SELECT a < '40', a < '60', a < '600' FROM t1;
0|1|1

-- Column "b" has numeric affinity and so numeric affinity is applied
-- to the operands on the right.  Since the operands are already numeric,
-- the application of affinity is a no-op; no conversions occur.  All
-- values are compared numerically.
SELECT b < 40,   b < 60,   b < 600 FROM t1;
0|0|1

-- Numeric affinity is applied to operands on the right, converting them
-- from text to integers.  Then a numeric comparison occurs.
SELECT b < '40', b < '60', b < '600' FROM t1;
0|0|1

-- No affinity conversions occur.  Right-hand side values all have
-- storage class INTEGER which are always less than the TEXT values
-- on the left.
SELECT c < 40,   c < 60,   c < 600 FROM t1;
0|0|0

-- No affinity conversions occur.  Values are compared as TEXT.
SELECT c < '40', c < '60', c < '600' FROM t1;
0|1|1

-- No affinity conversions occur.  Right-hand side values all have
-- storage class INTEGER which compare numerically with the INTEGER
-- values on the left.
SELECT d < 40,   d < 60,   d < 600 FROM t1;
0|0|1

-- No affinity conversions occur.  INTEGER values on the left are
-- always less than TEXT values on the right.
SELECT d < '40', d < '60', d < '600' FROM t1;
1|1|1

如果交换比较,则示例中的所有结果都是相同的 - 如果将“a<40”形式的表达式重写为“40>a”。

5. Operators

数学运算符(+、-、*、/、%、<<、>>、& 和 |)将两个操作数解释为数字。STRING 或 BLOB 操作数自动转换为 REAL 或 INTEGER 值。如果 STRING 或 BLOB 看起来像实数(如果它有小数点或指数)或者如果值超出可以表示为 64 位带符号整数的范围,则它会转换为 REAL。否则操作数转换为 INTEGER。数学操作数的隐含类型转换与CAST 到 NUMERIC略有不同,因为看起来像实数但没有小数部分的字符串和 BLOB 值保留为 REAL,而不是像CAST 到 NUMERIC那样转换为 INTEGER. 从 STRING 或 BLOB 到 REAL 或 INTEGER 的转换会执行,即使它是有损且不可逆的。一些数学运算符(%、<<、>>、& 和 |)需要 INTEGER 操作数。对于这些运算符,REAL 操作数转换为 INTEGER 的方式与CAST转换为 INTEGER 的方式相同。<<、>>、& 和 | 运算符始终返回 INTEGER(或 NULL)结果,但 % 运算符根据其操作数的类型返回 INTEGER 或 REAL(或 NULL)。数学运算符上的 NULL 操作数产生 NULL 结果。数学运算符上的操作数在任何方面都不是数字且不为 NULL,将转换为 0 或 0.0。除以零给出 NULL 的结果。

6.排序、分组和复合 SELECT

当查询结果按 ORDER BY 子句排序时,存储类为 NULL 的值排在最前面,然后是按数字顺序散布的 INTEGER 和 REAL 值,然后是按整理顺序排列的 TEXT 值,最后是按 memcmp() 顺序排列的 BLOB 值。排序之前不会发生存储类转换。

当使用 GROUP BY 子句对值进行分组时,具有不同存储类的值被认为是不同的,但 INTEGER 和 REAL 值除外,如果它们在数值上相等则被视为相等。作为 GROUP by 子句的结果,没有关联性应用于任何值。

复合 SELECT 运算符 UNION、INTERSECT 和 EXCEPT 执行值之间的隐式比较。对于与 UNION、INTERSECT 或 EXCEPT 关联的隐式比较,没有亲和力应用于比较操作数 - 值按原样进行比较。

7.整理序列

当 SQLite 比较两个字符串时,它使用整理序列或整理函数(同一事物的两个术语)来确定哪个字符串更大或两个字符串是否相等。SQLite 具有三个内置的整理函数:BINARY、NOCASE 和 RTRIM。

  • BINARY - 使用 memcmp() 比较字符串数据,不管文本编码如何。
  • NOCASE - 类似于二进制,除了它使用 sqlite3_strnicmp()进行比较。因此,在执行比较之前,ASCII 的 26 个大写字符被折叠成对应的小写字符。请注意,只有 ASCII 字符是大小写折叠的。由于所需表的大小,SQLite 不会尝试进行完整的 UTF 大小写折叠。另请注意,出于比较目的,字符串中的任何 U+0000 字符都被视为字符串终止符。
  • RTRIM - 与二进制相同,只是忽略尾随空格字符。

应用程序可以使用sqlite3_create_collat​​ion()接口注册额外的整理函数。

整理函数仅在比较字符串值时才重要。数值总是按数字进行比较,而 BLOB 总是使用 memcmp() 逐字节比较。

7.1. 从 SQL 分配整理顺序

每个表的每一列都有一个关联的整理功能。如果没有显式定义整理函数,则整理函数默认为 BINARY。列定义的 COLLATE 子句用于定义列的替代整理函数。

确定将哪个整理函数用于二元比较运算符(=、<、>、<=、>=、!=、IS 和 IS NOT)的规则如下:

  1. 如果任一操作数具有使用后缀COLLATE operator的显式整理函数赋值,则使用显式整理函数进行比较,并优先于左侧操作数的整理函数。

  2. 如果任一操作数是一列,则该列的整理函数优先于左操作数使用。出于上一句的目的,前面带有一个或多个一元“+”运算符和/或 CAST 运算符的列名仍被视为列名。

  3. 否则,使用 BINARY 整理函数进行比较。

如果操作数的任何子表达式使用后缀COLLATE operator ,则比较的操作数被认为具有显式整理函数分配(上面的规则 1) 。因此,如果在比较表达式中的任何地方使用COLLATE 运算符,则该运算符定义的整理函数将用于字符串比较,而不管哪些表列可能是该表达式的一部分。如果两个或多个 COLLATE 运算符子表达式出现在比较中的任何位置,则使用最左边的显式整理函数,而不管 COLLATE 运算符在表达式中嵌套的深度如何,也不管表达式如何括起来。

表达式“x BETWEEN y and z”在逻辑上等同于两个比较“x >= y AND x <= z”,并且在整理函数方面的作用就好像它是两个单独的比较一样。为了确定整理顺序,表达式“x IN (SELECT y ...)”的处理方式与表达式“x = y”的处理方式相同。用于“x IN (y, z, ...)”形式表达式的整理序列是 x 的整理序列。如果 IN 运算符需要显式整理序列,则应将其应用于左操作数,如下所示:“x COLLATE nocase IN (y,z, ...)”。

可以使用COLLATE operator为作为SELECT语句 一部分的 ORDER BY 子句的条款 分配一个整理序列 ,在这种情况下,指定的整理函数用于排序。否则,如果由 ORDER BY 子句排序的表达式是一列,则使用该列的整理顺序来确定排序顺序。如果表达式不是列并且没有 COLLATE 子句,则使用 BINARY 整理序列。

7.2. 归类序列示例

下面的示例标识了将用于确定可能由各种 SQL 语句执行的文本比较结果的整理序列。请注意,在数字、blob 或 NULL 值的情况下,可能不需要文本比较,也不使用整理顺序。

CREATE TABLE t1(
    x INTEGER PRIMARY KEY,
    a,                 /* collating sequence BINARY */
    b COLLATE BINARY,  /* collating sequence BINARY */
    c COLLATE RTRIM,   /* collating sequence RTRIM  */
    d COLLATE NOCASE   /* collating sequence NOCASE */
);
                   /* x   a     b     c       d */
INSERT INTO t1 VALUES(1,'abc','abc', 'abc  ','abc');
INSERT INTO t1 VALUES(2,'abc','abc', 'abc',  'ABC');
INSERT INTO t1 VALUES(3,'abc','abc', 'abc ', 'Abc');
INSERT INTO t1 VALUES(4,'abc','abc ','ABC',  'abc');
 
/* Text comparison a=b is performed using the BINARY collating sequence. */
SELECT x FROM t1 WHERE a = b ORDER BY x;
--result 1 2 3

/* Text comparison a=b is performed using the RTRIM collating sequence. */
SELECT x FROM t1 WHERE a = b COLLATE RTRIM ORDER BY x;
--result 1 2 3 4

/* Text comparison d=a is performed using the NOCASE collating sequence. */
SELECT x FROM t1 WHERE d = a ORDER BY x;
--result 1 2 3 4

/* Text comparison a=d is performed using the BINARY collating sequence. */
SELECT x FROM t1 WHERE a = d ORDER BY x;
--result 1 4

/* Text comparison 'abc'=c is performed using the RTRIM collating sequence. */
SELECT x FROM t1 WHERE 'abc' = c ORDER BY x;
--result 1 2 3

/* Text comparison c='abc' is performed using the RTRIM collating sequence. */
SELECT x FROM t1 WHERE c = 'abc' ORDER BY x;
--result 1 2 3

/* Grouping is performed using the NOCASE collating sequence (Values
** 'abc', 'ABC', and 'Abc' are placed in the same group). */
SELECT count(*) FROM t1 GROUP BY d ORDER BY 1;
--result 4

/* Grouping is performed using the BINARY collating sequence.  'abc' and
** 'ABC' and 'Abc' form different groups */
SELECT count(*) FROM t1 GROUP BY (d || '') ORDER BY 1;
--result 1 1 2

/* Sorting or column c is performed using the RTRIM collating sequence. */
SELECT x FROM t1 ORDER BY c, x;
--result 4 1 2 3

/* Sorting of (c||'') is performed using the BINARY collating sequence. */
SELECT x FROM t1 ORDER BY (c||''), x;
--result 4 2 3 1

/* Sorting of column c is performed using the NOCASE collating sequence. */
SELECT x FROM t1 ORDER BY c COLLATE NOCASE, x;
--result 2 4 3 1