、概述

选择stmt:

WITH RECURSIVE common-table-expression , SELECT DISTINCT result-column , ALL FROM table-or-subquery join-clause , WHERE expr GROUP BY expr HAVING expr , WINDOW window-name AS window-defn , VALUES ( expr ) , , compound-operator select-core ORDER BY LIMIT expr ordering-term , OFFSET expr , expr

公用表表达式:

复合运算符:

表达式:

加入子句:

订购条款:

结果列:

表或子查询:

窗口定义:

SELECT 语句用于查询数据库。SELECT 的结果是零行或多行数据,其中每行都有固定数量的列。SELECT 语句不会对数据库进行任何更改。

上面的“ select-stmt ”语法图试图在一个图中显示尽可能多的 SELECT 语句语法,因为一些读者觉得这很有帮助。下面的“ factored-select-stmt ”是另一种语法图,它表达了相同的语法,但试图将语法分解成更小的块。

因式选择 stmt:

请注意,语法图中有些路径在实践中是不允许的。一些例子:

文本中描述了这些和其他类似的语法限制。

SELECT 语句是 SQL 语言中最复杂的命令。为了使描述更容易理解,下面的一些段落描述了 SELECT 语句返回的数据被确定为一系列步骤的方式。请务必记住,这纯粹是说明性的——实际上,SQLite 或任何其他 SQL 引擎都不需要遵循此过程或任何其他特定过程。

2.简单的选择处理

SELECT 语句的核心是下面的 select-coresimple-select-stmt语法图所示的“简单 SELECT”。实际上,大多数 SELECT 语句都是简单的 SELECT 语句。

简单选择 stmt:

WITH RECURSIVE common-table-expression , select-core ORDER BY LIMIT expr ordering-term , OFFSET expr , expr

公用表表达式:

表达式:

订购条款:

选择核心:

SELECT DISTINCT result-column , ALL FROM table-or-subquery join-clause , WHERE expr GROUP BY expr HAVING expr , WINDOW window-name AS window-defn , VALUES ( expr ) , ,

加入子句:

结果列:

表或子查询:

窗口定义:

生成简单 SELECT 语句的结果在以下描述中分为四个步骤:

  1. FROM 子句处理:确定简单 SELECT 的输入数据。输入数据要么隐式地为具有 0 列的单行(如果没有 FROM 子句),要么由 FROM 子句确定。

  2. WHERE 子句处理:使用 WHERE 子句表达式过滤输入数据。

  3. GROUP BY、HAVING 和结果列表达式处理:结果行的集合是通过根据任何 GROUP BY 子句聚合数据并计算过滤输入数据集的行的结果集表达式来计算的。

  4. DISTINCT/ALL 关键字处理:如果查询是“SELECT DISTINCT”查询,则从结果行集合中删除重复行。

有两种类型的简单 SELECT 语句 - 聚合查询和非聚合查询。如果一个简单的 SELECT 语句在结果集中包含一个 GROUP BY 子句或一个或多个聚合函数,那么它就是一个聚合查询。否则,如果简单的 SELECT 不包含聚合函数或 GROUP BY 子句,则它是非聚合查询。

2.1. 输入数据的确定(FROM子句处理)

一个简单的 SELECT 查询使用的输入数据是一组N行,每行M列宽。

如果从简单的 SELECT 语句中省略 FROM 子句,则输入数据隐含为单行零列宽(即N =1 和 M =0)。

如果指定了 FROM 子句,则简单 SELECT 查询操作的数据来自 FROM 关键字后指定的一个或多个表或子查询(括号中的 SELECT 语句)。在简单 SELECT 语句中的 FROM 子句之后的表或子查询中指定的子查询被处理,就好像它是一个包含通过执行子查询语句返回的数据的表。子查询的每一列都有 子查询语句中对应表达式 的排序顺序亲和关系。

如果 FROM 子句中只有一个表或子查询,则 SELECT 语句使用的输入数据是指定表的内容。如果 FROM 子句中有多个表或子查询,则所有表和/或子查询的内容都将连接到一个数据集中,以便简单的 SELECT 语句对其进行操作。数据的具体组合方式取决于用于将表或子查询连接在一起 的特定连接运算符连接约束。

SQLite 中的所有连接都基于左右数据集的笛卡尔积。笛卡尔积数据集的列按顺序是左侧数据集的所有列,然后是右侧数据集的所有列。笛卡尔乘积数据集中有一行是通过组合左侧和右侧数据集中一行的每个唯一组合形成的。换句话说,如果左侧数据集由 NM组成,右侧数据集由 N右 M 列组成 笛卡尔积是 N× N 行数据,每个包含 M+M列。

如果连接运算符是“CROSS JOIN”、“INNER JOIN”、“JOIN”或逗号 (",") 并且没有 ON 或 USING 子句,则连接的结果只是左边的笛卡尔积和右边的数据集。如果连接运算符确实有 ON 或 USING 子句,则根据以下要点处理这些子句:

  • 如果有 ON 子句,则 ON 表达式将作为 布尔表达式对笛卡尔积的每一行求值。数据集中仅包含表达式计算结果为 true 的行。

  • 如果有 USING 子句,则指定的每个列名都必须存在于连接运算符左侧和右侧的数据集中。对于每对命名列,表达式“lhs.X = rhs.X”作为布尔表达式对笛卡尔积的每一行进行评估. 结果集中仅包含所有此类表达式的计算结果为 true 的行。当比较值作为 USING 子句的结果时,在比较中处理关联性、归类序列和 NULL 值的一般规则适用。出于整理顺序和关联优先级的目的,连接运算符左侧的数据集中的列被视为位于比较运算符 (=) 的左侧。

    对于由 USING 子句标识的每对列,右侧数据集中的列从连接的数据集中省略。这是 USING 子句与其等效的 ON 约束之间的唯一区别。

  • 如果 NATURAL 关键字在连接运算符中,则将隐式 USING 子句添加到连接约束。隐式 USING 子句包含出现在左侧和右侧输入数据集中的每个列名。如果左右输入数据集没有共同的列名,则 NATURAL 关键字对连接结果没有影响。不得将 USING 或 ON 子句添加到指定 NATURAL 关键字的连接中。

  • 如果连接运算符是“LEFT JOIN”或“LEFT OUTER JOIN”,则在应用 ON 或 USING 过滤子句后,将为原始左侧输入数据集中的每一行的输出添加一个额外的行不匹配右侧数据集中的任何行。添加的行在通常包含从右侧输入数据集复制的值的列中包含 NULL 值。

  • 如果连接运算符是“RIGHT JOIN”或“RIGHT OUTER JOIN”,则在应用 ON 或 USING 过滤子句后,将为原始右侧输入数据集中的每一行的输出添加一个额外的行不匹配左侧数据集中的任何行。添加的行在通常包含从左侧输入数据集复制的值的列中包含 NULL 值。

  • “FULL JOIN”或“FULL OUTER JOIN”是“LEFT JOIN”和“RIGHT JOIN”的组合。为左侧数据集中不匹配右侧任何行的每一行以及右侧数据集中不匹配左侧任何行的每一行添加额外的输出行。不匹配的列用 NULL 填充。

当两个以上的表作为 FROM 子句的一部分连接在一起时,连接操作按从左到右的顺序进行处理。换句话说,FROM 子句 (A join-op-1 B join-op-2 C) 计算为 ((A join-op-1 B) join-op-2 C)。

2.2. CROSS JOIN 的特殊处理。

“INNER JOIN”、“JOIN”和“,”连接运算符之间没有区别。它们在 SQLite 中是完全可以互换的。“CROSS JOIN”连接运算符产生与“INNER JOIN”、“JOIN”和“,”运算符相同的结果,但 查询优化器的处理方式不同, 因为它阻止查询优化器对连接中的表重新排序。应用程序程序员可以使用 CROSS JOIN 运算符直接影响为实现 SELECT 语句而选择的算法。避免使用 CROSS JOIN,除非在需要手动控制查询优化器的特定情况下。避免在应用程序开发的早期使用 CROSS JOIN,因为这样做是过早的优化. CROSS JOIN 的特殊处理是 SQLite 特定的功能,不是标准 SQL 的一部分。

2.3. WHERE 子句过滤。

如果指定了 WHERE 子句,则 WHERE 表达式将作为布尔表达式对输入数据中的每一行求值。在继续之前,只有 WHERE 子句表达式的计算结果为 true 的行才会包含在数据集中。如果 WHERE 子句的计算结果为 false 或 NULL,则行将从结果中排除。

对于 JOIN 或 INNER JOIN 或 CROSS JOIN,WHERE 子句中的约束表达式与 ON 子句中的约束表达式没有区别。但是,对于 LEFT JOIN 或 LEFT OUTER JOIN,区别非常重要。在 LEFT JOIN 中,右侧表的额外 NULL 行在 ON 子句处理之后但在 WHERE 子句处理之前添加。因此,ON 子句中形式为“left.x=right.y”的约束将允许右表中添加的全空行。但是如果相同的约束在 WHERE 子句中,“right.y”中的 NULL 将阻止表达式“left.x=right.y”为真,从而从输出中排除该行。

2.4. 结果行集的生成

一旦来自 FROM 子句的输入数据被 WHERE 子句表达式(如果有)过滤后,就会计算简单 SELECT 的结果行集。具体如何完成取决于简单 SELECT 是聚合查询还是非聚合查询,以及是否指定了 GROUP BY 子句。

SELECT 和 FROM 关键字之间的表达式列表称为结果表达式列表。如果结果表达式是特殊表达式“*”,则输入数据中的所有列都将替换为该表达式。如果表达式是 FROM 子句中表或子查询的别名,后跟“.*”,则命名表或子查询中的所有列都将替换为单个表达式。在结果表达式列表以外的任何上下文中使用“*”或“alias.*”表达式都是错误的。在没有 FROM 子句的简单 SELECT 查询中使用“*”或“alias.*”表达式也是错误的。

简单 SELECT 语句返回的行中的列数等于替换 * 和 alias.* 表达式后结果表达式列表中的表达式数。每个结果行都是通过计算结果表达式列表中关于单行输入数据的表达式,或者对于聚合查询,关于一组行计算的。

  • 如果 SELECT 语句是非聚合查询,则结果表达式列表中的每个表达式都会针对 WHERE 子句过滤的数据集中的每一行进行计算。

  • 如果 SELECT 语句是没有 GROUP BY子句的聚合查询,那么结果集中的每个聚合表达式都会在整个数据集中计算一次。结果集中的每个非聚合表达式都会针对数据集中任意选择的行进行一次评估。同一任意选择的行用于每个非聚合表达式。或者,如果数据集包含零行,则针对完全由 NULL 值组成的行评估每个非聚合表达式。

    通过评估结果集中的聚合和非聚合表达式创建的单行结果集数据构成了没有 GROUP BY 子句的聚合查询的结果。没有 GROUP BY 子句的聚合查询总是只返回一行数据,即使有零行输入数据。

  • 如果 SELECT 语句是带有 GROUP BY子句的聚合查询,则根据下面针对 ORDER BY 表达式所述的处理规则,为数据集的每一行评估指定为 GROUP BY 子句一部分的每个表达式。然后根据结果将每一行分配给一个“组”;GROUP BY 表达式求值结果相同的行被分配到同一组。为了对行进行分组,NULL 值被认为是相等的。当评估 GROUP BY 子句中的表达式时,选择用于比较文本值的归类序列的常用规则适用。GROUP BY 子句中的表达式必须是出现在结果中的表达式。GROUP BY 子句中的表达式不能是聚合表达式。

    如果指定了 HAVING 子句,它将作为布尔表达式对每组行求值一次如果评估 HAVING 子句的结果为假,则丢弃该组。如果 HAVING 子句是一个聚合表达式,它会在组中的所有行中计算。如果 HAVING 子句是非聚合表达式,则根据从组中任意选择的行对其进行评估。HAVING 表达式可能引用不在结果中的值,甚至聚合函数。

    结果集中的每个表达式然后针对每组行计算一次。如果表达式是一个聚合表达式,它会在组中的所有行中计算。否则,将根据组内任意选择的单个行对其进行评估。如果结果集中有多个非聚合表达式,则对同一行计算所有此类表达式。

    每组输入数据集行为结果行集贡献一行。根据与 DISTINCT 关键字关联的过滤,使用 GROUP BY 子句的聚合查询返回的行数与将 GROUP BY 和 HAVING 子句应用于过滤的输入数据集所产生的行组数相同。

2.5. 聚合查询中的裸列

通常情况下,聚合查询中的所有列名要么是聚合函数的参数,要么出现在 GROUP BY 子句中。包含不在聚合函数内且未出现在 GROUP BY 子句(如果存在)中的列名的结果列称为“裸”列。例子:

SELECT a, b, sum(c) FROM tab1 GROUP BY a;

在上面的查询中,“a”列是 GROUP BY 子句的一部分,因此输出的每一行都包含“a”的不同值之一。“c”列包含在sum()聚合函数中,因此输出列是具有相同“a”值的行中所有“c”值的总和。但是裸列“b”的结果是什么?答案是“b”结果将是构成聚合的输入行之一中“b”的值。问题是您通常不知道哪个输入行用于计算“b”,因此在许多情况下“b”的值是未定义的。

当聚合函数为min()max()时,会发生特殊处理。例子:

SELECT a, b, max(c) FROM tab1 GROUP BY a;

当在聚合查询中使用min()max()聚合函数时,结果集中的所有裸列都从输入行中获取值,该输入行也包含最小值或最大值。所以在上面的查询中,输出中“b”列的值将是输入行中具有最大“c”值的“b”列的值。如果两个或多个输入行具有相同的最小值或最大值,或者查询包含多个 min() 和/或 max() 聚合函数,则仍然存在歧义。只有内置的min()max()函数以这种方式工作。

如果裸列出现在缺少 GROUP BY 子句的聚合查询中,并且输入行数为零,则裸列的值是任意的。例如,在此查询中:

SELECT count(*), b FROM tab1;

如果 tab1 表不包含任何行(如果 count(*) 的计算结果为 0),则裸列“b”将具有任意且无意义的值。

大多数其他 SQL 数据库引擎不允许裸列。如果您在查询中包含裸列,其他数据库引擎通常会引发错误。在查询中包含裸列的能力是 SQLite 特定的扩展。

2.6. 去除重复行(DISTINCT 处理)

在简单的 SELECT 语句中,ALL 或 DISTINCT 关键字之一可以跟在 SELECT 关键字之后。如果简单 SELECT 是 SELECT ALL,则 SELECT 返回整个结果行集。如果 ALL 或 DISTINCT 都不存在,则行为就像指定了 ALL 一样。如果简单 SELECT 是 SELECT DISTINCT,则重复行在返回之前从结果行集中删除。为了检测重复行,两个 NULL 值被认为是相等的。通常的 规则适用于选择排序规则序列来比较文本值。

3.复合选择语句

两个或多个简单的 SELECT语句可以使用 UNION、UNION ALL、INTERSECT 或 EXCEPT 运算符连接在一起形成复合 SELECT,如下图所示:

复合选择 stmt:

WITH RECURSIVE common-table-expression , select-core ORDER BY LIMIT expr UNION UNION ALL select-core INTERSECT EXCEPT ordering-term , OFFSET expr , expr

公用表表达式:

表达式:

订购条款:

选择核心:

在复合 SELECT 中,所有构成的 SELECT 必须返回相同数量的结果列。由于复合 SELECT 的组件必须是简单的 SELECT 语句,因此它们可能不包含ORDER BYLIMIT子句。 ORDER BYLIMIT子句只能出现在整个复合 SELECT 的末尾,并且仅当复合的最后一个元素不是VALUES子句时。

使用 UNION ALL 运算符创建的复合 SELECT 返回从 SELECT 到 UNION ALL 运算符左侧的所有行,以及从 SELECT 到它右侧的所有行。UNION 运算符的工作方式与 UNION ALL 相同,只是会从最终结果集中删除重复的行。INTERSECT 运算符返回左右 SELECT 的结果的交集。EXCEPT 运算符返回左侧 SELECT 返回的行的子集,这些行也未由右侧 SELECT 返回。在返回结果集之前,从 INTERSECT 和 EXCEPT 运算符的结果中删除重复的行。

为了确定复合 SELECT 运算符结果的重复行,NULL 值被视为等于其他 NULL 值并且不同于所有非 NULL 值。用于比较两个文本值的归类序列被确定为就好像左侧和右侧 SELECT 语句的列是等号 (=) 运算符的左侧和右侧操作数,只是没有为归类分配更高的优先级使用后缀 COLLATE 运算符指定的序列。在将行作为复合 SELECT 的一部分进行比较时,不会对任何值应用亲和力转换。

当三个或更多的简单 SELECT 连接成一个复合 SELECT 时,它们从左到右分组。换句话说,如果“A”、“B”和“C”都是简单的SELECT语句,则(A op B op C)被处理为((A op B) op C)。

4. ORDER BY 子句

如果返回多行的 SELECT 语句没有 ORDER BY 子句,则返回行的顺序是未定义的。或者,如果 SELECT 语句确实有一个 ORDER BY 子句,那么附加到 ORDER BY 的表达式列表决定了将行返回给用户的顺序。

复合 SELECT语句中,只有最后一个或最右边的简单 SELECT 可以有 ORDER BY 子句。该 ORDER BY 子句将应用于复合的所有元素。如果复合 SELECT最右边的元素VALUES子句,则该语句中不允许有 ORDER BY 子句。

首先根据 ORDER BY 列表中最左边的表达式的计算结果对行进行排序,然后通过计算最左边的第二个表达式来打破平局,依此类推。未定义返回所有 ORDER BY 表达式求得相等值的两行的顺序。每个 ORDER BY 表达式都可以选择性地后跟关键字 ASC(首先返回较小的值)或 DESC(首先返回较大的值)之一。如果既未指定 ASC 也未指定 DESC,则默认情况下按升序(较小的值在前)对行进行排序。

SQLite 认为 NULL 值比排序目的的任何其他值都小。因此,NULL 自然会出现在 ASC order-by 的开头和 DESC order-by 的结尾。这可以使用“ASC NULLS LAST”或“DESC NULLS FIRST”语法更改。

每个 ORDER BY 表达式的处理如下:

  1. 如果 ORDER BY 表达式是常量整数 K,则该表达式被视为结果集第 K 列的别名(列从左到右从 1 开始编号)。

  2. 如果 ORDER BY 表达式是对应于输出列之一的别名的标识符,则该表达式被视为该列的别名。

  3. 否则,如果 ORDER BY 表达式是任何其他表达式,则对其求值并使用返回值对输出行进行排序。如果 SELECT 语句是一个简单的 SELECT,那么 ORDER BY 可以包含任意表达式。但是,如果 SELECT 是复合 SELECT,则不是输出列别名的 ORDER BY 表达式必须与用作输出列的表达式完全相同。

为了对行进行排序,值的比较方式与比较表达式相同。用于比较两个文本值的归类序列确定如下:

  1. 如果使用后缀COLLATE operator为 ORDER BY 表达式分配了归类序列,则使用指定的归类序列。

  2. 否则,如果 ORDER BY 表达式是已使用后缀 COLLATE operator分配归类序列的表达式的别名,则使用分配给别名表达式的归类序列。

  3. 否则,如果 ORDER BY 表达式是列或表达式的别名是列,则使用该列的默认排序规则序列。

  4. 否则,使用BINARY归类序列。

复合 SELECT语句中,所有 ORDER BY 表达式都作为复合结果列之一的别名处理。如果 ORDER BY 表达式不是整数别名,则 SQLite 在复合中搜索最左边的 SELECT 以查找与上面的第二个或第三个规则匹配的结果列。如果找到匹配项,搜索将停止,表达式将作为与其匹配的结果列的别名进行处理。否则,将尝试右边的下一个 SELECT,依此类推。如果在任何一个 SELECT 的结果列中都找不到匹配的表达式,则为错误。ORDER BY 子句的每个术语都单独处理,并且可以与复合中不同 SELECT 语句的结果列相匹配。

5. LIMIT 子句

LIMIT 子句用于设置整个 SELECT 语句返回的行数的上限。

复合 SELECT中,只有最后一个或最右边的简单 SELECT可以包含 LIMIT 子句。复合 SELECT中,LIMIT 子句适用于整个复合,而不仅仅是最终的 SELECT。如果最右边的简单 SELECTVALUES 子句,则不允许使用 LIMIT 子句。

任何标量表达式都可以用在 LIMIT 子句中,只要它的计算结果为整数或可以无损转换为整数的值。如果表达式的计算结果为 NULL 值或任何其他无法无损转换为整数的值,则会返回错误。如果 LIMIT 表达式的计算结果为负值,则返回的行数没有上限。否则,SELECT 仅返回其结果集的前 N ​​行,其中 N 是 LIMIT 表达式的计算结果。或者,如果 SELECT 语句在没有 LIMIT 子句的情况下返回少于 N 行,则返回整个结果集。

附加到可能跟在 LIMIT 子句之后的可选 OFFSET 子句的表达式的计算结果也必须为整数,或者可以无损地转换为整数的值。如果表达式具有 OFFSET 子句,则从 SELECT 语句返回的结果集中省略前 M 行,并返回接下来的 N 行,其中 M 和 N 分别是 OFFSET 和 LIMIT 子句的计算结果。或者,如果 SELECT 在没有 LIMIT 子句的情况下将返回少于 M+N 行,则跳过前 M 行并返回其余行(如果有)。如果 OFFSET 子句的计算结果为负值,则结果与计算结果为零的结果相同。

LIMIT 子句可以指定两个用逗号分隔的标量表达式,而不是单独的 OFFSET 子句。在这种情况下,第一个表达式用作 OFFSET 表达式,第二个用作 LIMIT 表达式。这是违反直觉的,因为当使用 OFFSET 子句时,两个表达式中的第二个是 OFFSET,第一个是 LIMIT。这种偏移量和限制的颠倒是有意的——它最大限度地提高了与其他 SQL 数据库系统的兼容性。但是,为避免混淆,强烈建议程序员使用使用“OFFSET”关键字的 LIMIT 子句形式,并避免使用带逗号分隔偏移量的 LIMIT 子句。

6. VALUES 子句

短语“VALUES( expr-list )”与“SELECT expr-list ”的意思相同短语“VALUES( expr-list-1 ),...,( expr-list-N )”与“SELECT expr-list-1 UNION ALL ... UNION ALL SELECT expr-list-N的含义相同. 这两种形式是相同的,除了复合中 SELECT 语句的数量受 SQLITE_LIMIT_COMPOUND_SELECT限制,而 VALUES 子句中的行数没有任意限制。

语法图中未显示的 VALUES 子句的使用有一些限制:

  • VALUES 子句后面不能跟ORDER BY

  • VALUES 子句后不能跟LIMIT

7. WITH 子句

SELECT 语句前面可以有一个可选的 WITH 子句,该子句定义了一个或多个公用表表达式 以供在 SELECT 语句中使用。

8. FROM 子句中的表值函数

包含隐藏列虚拟表可以像FROM 子句中的表值函数一样使用。表值函数的参数成为对虚拟表的 HIDDEN 列的约束。可以在 虚拟表文档中找到更多信息。

9.与标准 SQL 的偏差

SQLite 的 SELECT 语法语法与标准 SQL 略有不同。这些差异是由于以下几个原因造成的:

  • 在 2000 年代中期,人们非常重视使库占用空间尽可能小,以免在内存有限的翻盖手机和类似设备上占用太多空间。

  • 在 SQLite 的早期,首席开发人员寻求遵循 Postel 法则,并在接受输入时保持宽容和灵活。

  • 早期的 SQLite 解析器在接受一些奇怪的输入时存在错误。

  • 首席开发人员对 SQL 的了解不完善。

无论输入怪异的起源是什么,我们通常都会避免尝试“修复”它们,因为对输入语法的任何新限制都可能至少导致数百万使用 SQLite 的应用程序中的一部分崩溃。我们不希望那样。SQLite 开发团队的目标是尽可能保持向后兼容性。因此,如果一个语法怪癖是无害的,我们就不管它并在此处记录它,而不是试图修复它。

9.1. 奇怪的 JOIN 名称

SQLite 接受 JOIN 运算符的所有常用语法:

联合运营商:

NATURAL LEFT OUTER JOIN , RIGHT FULL INNER CROSS

但它并不止于此。SQLite 实际上在指定连接运算符方面非常灵活。一般语法是:

blah blah blah JOIN

其中有 1 到 3 个“ blah ”实例,每个实例可以是“CROSS”、“FULL”、“INNER”、“LEFT”、“NATURAL”、“OUTER”或“RIGHT”中的任何一个。SQLite 解析器将这些关键字中的每一个都视为连接的一个属性,它们可以按任何顺序组合。这创造了许多新的和创造性的连接类型的可能性,超出了语法图指定的范围。其中一些非标准连接类型是明确不允许的。例如,您不能说“INNER OUTER JOIN”,因为那样会自相矛盾。但是你可以说“OUTER LEFT NATURAL JOIN”之类的东西,这与“NATURAL LEFT OUTER JOIN”的意思相同。或者你可以说“LEFT RIGHT JOIN”,这与“FULL JOIN”相同

请记住:您可以使用这些非标准连接类型,但您不应该使用。坚持使用标准 JOIN 语法以实现与其他 SQL 数据库引擎的可移植性。

9.2. 逗号连接和交叉连接的优先级

在标准 SQL 中,使用 JOIN 关键字的联接比逗号联接具有更高的优先级。也就是说,JOIN 运算符出现在逗号运算符之前。在 SQLite 中不是这种情况,所有连接都具有相同的优先级。

考虑这个例子:

... FROM t1, t2 NATURAL FULL JOIN t3 ...

在标准 SQL 中,t2 和 t3 之间的 FULL JOIN 将首先发生,然后左连接的结果将与 t1 交叉连接。但是 SQLite 总是从左到右处理所有连接。因此,SQLite 将首先对 t1 和 t2 进行交叉连接,然后该交叉连接的结果将提供给与​​ t3 的 FULL JOIN。内连接是固有关联的,因此仅当您的 FROM 子句包含一个或多个外连接时,差异才会明显。

您可以通过遵守以下风格规则来解决这个问题,并使您的 SQL 语句在所有系统中都可移植:

  • 不要将逗号连接与 JOIN 关键字混合使用。使用逗号连接很好,但如果这样做,则整个 FROM 子句应仅使用逗号连接。

  • 首选 LEFT JOIN 优于其他外部联接运算符。

  • 如有疑问,请使用括号指定您想要的确切连接顺序。

这些建议中的任何一个都足以避免问题,大多数程序员本能地遵循所有这些建议而无需被告知,因此在实践中很少出现逗号连接和 SQLite 中的 JOIN 关键字之间没有优先级差异的情况。但是你应该意识到这个问题,以防它出现。