当测试我的一个Android 5.0兼容性应用程序时,我发现 one 我的两个SQL查询不会在Lollipop上不再按预期工作了。与旧的Android版本相比,我的两个问题导致Lollipop的结果显着不同。
下面,我将更深入地描述这些问题及其解决方案,以防您遇到类似问题。
我的主要问题很简单:这些非向后兼容的更改是否记录在哪里?
似乎以下查询在Lollipop上不再起作用了:
SELECT title FROM ents JOIN ctt ON ctt.docid = ents.cttId WHERE (ctt MATCH '*ads*');
它不再返回任何结果,在它之前的Lollipop上(当然使用相同的数据库和相同的数据)。
如this question中所述,MATCH仅匹配字符串前缀。事实确实如此,搜索词前面的'*'在Android<< 5.0。
然而,Lollipop的SQLite不喜欢第一个'*',并且不会为此查询返回任何内容。我不得不将查询更改为以下内容以使其再次起作用:
SELECT title FROM ents JOIN ctt ON ctt.docid = ents.cttId WHERE (ctt MATCH 'ads*');
(我正在使用FTS3进行全文搜索。)
短篇小说:使用特定于Android的“COLLATE LOCALIZED”将原始名称引用的别名列与ORDER BY结合使用会在Lollipop上引发错误,但适用于以前的版本。 WTF!? : - )
长篇故事:
故事始于一个非常大的自动生成的查询,所以我修改,简化并缩短了导致问题的部分。我知道查询没有太大意义,如下所示,但它证明了问题。
SELECT
inner.title AS title,
ltrim(inner.title, '*') AS title2
FROM
(SELECT types.text AS title FROM types) AS inner
GROUP BY inner.title
UNION SELECT
inner.title AS title,
ltrim(inner.title, '*') AS title2
FROM
(SELECT types.text AS title FROM types) AS inner
GROUP BY inner.title
ORDER BY title2 COLLATE LOCALIZED ASC
以上查询适用于Andriod< 5.0,但在Lollipop中导致错误:
Error: no such column: inner.title
好的,我将“inner.title”别名为“title”,所以我尝试将“GROUP BY inner.title”更改为“GROUP BY title”,这实际上是Lollipop的SQLite的解决方案:
SELECT
inner.title AS title,
ltrim(inner.title, '*') AS title2
FROM
(SELECT types.text AS title FROM types) AS inner
GROUP BY title
UNION SELECT
inner.title AS title,
ltrim(inner.title, '*') AS title2
FROM
(SELECT types.text AS title FROM types) AS inner
GROUP BY title
ORDER BY title2 COLLATE LOCALIZED ASC
(顺便说一句,在this answer中,您可以找到Android中使用的SQLite版本的精彩概述)
现在出现了一个有趣的部分:如果在ORDER BY子句中删除了Android特定的“COLLATE LOCALIZED”,一切都会开始工作,即使是“GROUP BY inner.title”:
SELECT
inner.title AS title,
ltrim(inner.title, '*') AS title2
FROM
(SELECT indsntyps.text AS title FROM indsntyps) AS inner
GROUP BY inner.title
UNION SELECT
inner.title AS title,
ltrim(inner.title, '*') AS title2
FROM
(SELECT indsntyps.text AS title FROM indsntyps) AS inner
GROUP BY inner.title
ORDER BY title2 ASC
我的Lollipop体验基于SDK模拟器中使用Android 5.0 - API Level 21 ARM系统映像的测试。
这个secon问题对我来说似乎是一个Android特定的SQLite错误。或者有人可以解释我(在我看来)奇怪的行为?或者,这甚至在某处记录了吗? : - )
提前致谢!
答案 0 :(得分:8)
我从来都不是SQLite专家,而且我认为你打算让这个问题主要是修辞,但请允许我提出一些想法。
正如您已经指出的那样,MATCH
only considers prefix terms。将前缀(如果愿意)加上星号前缀会产生意外和不可预测的行为,这并不奇怪。
这似乎是一个有趣的错误。但是,您可以尝试使用EXPLAIN QUERY PLAN
来尝试和诊断它。
显然,我还没有告诉你任何你不知道的事情。你的问题"然而,是关于文档。首先,SQLite release notes can be found here。它们通常非常详细地说明版本之间的变化。
您的第一个问题实际上是编程错误。关于如何使用FTS前缀的文档已经存在。您不会得到有关您的特定语法停止工作的原因的解释。可以说,它从来都不应该起作用。
LOCALIZED
问题可能是一个错误因此缺少"文档" (我鼓励您向Google报告)。还要记住,SQLite是Android核心的一部分,它不仅具有自定义(如LOCALIZED
),还具有本机Java绑定。基础SQLite核心实现和绑定都可能随每个版本而变化。这让我想到了我的主要观点:
考虑使用私有SQLite实现部署您的应用。这样做的说明can be found here。这将使您能够控制应用程序使用的SQLite版本,并使您可以精确控制如何以及何时升级它。但是,这确实需要付出代价,因为您丢失了LOCALIZED
关键字,例如绑定只支持API 15或更高版本,我相信。
答案 1 :(得分:4)
观察到的变化是因为Lollipop附带SQLite 3.8(Android 4.x附带3.7.11)。以下是更改列表http://www.sqlite.org/releaselog/3_8_0.html
例如,“GROUP BY inner.title”没有这样的列的错误是由于“GROUP BY子句中的标识符总是更喜欢输出列名”。
答案 2 :(得分:0)
我发现OP有几个问题。
FTS使用百分比而非星号作为外卡。
FTS搜索单词边界,因此拥有一张领先的外卡从未使用过FTS,它会将它传递给sqlite来读取整个表格的速度非常慢但是OP让我们相信它确实有效在过去。因此,如果该查询确实运行,那么该查询在过去会运行缓慢。
联盟查询在使用的资源量方面总是过多,尤其是与某些组合使用时。因此,此查询在可伸缩业务应用程序中没有位置。