我无意中写了这段代码
SELECT COUNT(*) "Table0" WHERE "Column0" = ? LIMIT 1;
当我打算写这段代码时:
SELECT COUNT(*) FROM "Table0" WHERE "Column0" = ? LIMIT 1;
我真的很惊讶第一行编译时没有出现任何错误。我已经检查了 SQLite 文档中的语法,看起来这不应该编译,但实际上却可以。这是怎么回事?SQLite 如何解释它?
即使表中存在匹配,错误的行也总是返回 0。
而且有趣的是,LibSQL 确实失败了,但出现了一条关于“Column0”不存在的消息......一旦我们理解了它是如何被解释的,也许这会更有意义......
AS
指定别名时,关键字是可选的,因此含义SELECT COUNT(*) "Table0"
与 相同SELECT COUNT(*) AS "Table0"
。未指定时,默认表
FROM
只有一行且没有列。更典型的使用方法是使用类似SELECT 1 + 1
或 的表达式SELECT EXISTS (…)
。由于它不是一个可解析的名称,因此由于MySQL 兼容性缺陷
"Column0"
而被视为字符串,该缺陷正逐渐从 SQLite 中删除;您的 LibSQL 显然配置为禁用该选项,或者可能已从 fork 中完全删除。(您可以通过将其作为参数值传递并查看结果从 0 变为 1 来确认这一点。)'Column0'
我想这就是你第一次查询时发生的情况:
SELECT COUNT(*) "Table0" WHERE "Column0" = ? LIMIT 1;
神奇的事情就在这里发生,
SELECT COUNT(*) "Table0"
当你没有写的时候FROM
,SQL 就将其Table0
作为查询的别名Count(*)
,开始对一个隐式的空表进行操作,这个表没有行,所以不管你表中有多少匹配的记录,它总是会因为缺少FROM
句子而返回 0 行。来到查询的第二部分
WHERE "Column0" = ?
,由于没有提到子句FROM
,因此Column0
也被视为表达式(在后端 SQLite 认为它是您可能使用的别名或列名)好问题,我喜欢它背后的好奇心!:)
问题在于,当您意外省略FROM子句时,SQLite 如何解释您的查询。在 SQLite 中,语法足够宽松,允许看似无效但在 SQL 语法中技术上可接受的构造。发生的事情是:
在此查询中:
COUNT(*) "表 0":
SQLite 将“Table0”解释为COUNT(*)结果的别名。这意味着查询正在选择虚拟结果集中所有行的计数,并将结果列命名为“Table0”。WHERE“Column0”=?:
由于没有FROM子句,查询将对 SQLite 提供的虚拟单行表进行操作。此表不包含实际列,但 SQLite 允许您编写类似“Column0”= ?的表达式,即使“Column0”不存在。SQLite 将此条件评估为 false(或更准确地说,它对虚拟表的所有行都评估为 false)。LIMIT 1。
LibSQL是 SQLite 的衍生产品,它似乎更严格地要求WHERE子句中的列名必须存在于查询的表中。由于“Column0”不存在于隐式虚拟表中(或由于缺少 FROM 子句而存在于任何表中),因此它会引发错误。
我认为这将有助于您了解实际发生的情况。