Android Lollipop - 改变 SQLite 的行为
Posted
技术标签:
【中文标题】Android Lollipop - 改变 SQLite 的行为【英文标题】:Android Lollipop - changed behavior of SQLite 【发布时间】:2015-01-05 03:34:29 【问题描述】:在测试我的一个应用程序的 android 5.0 兼容性时,我发现 一个 我的两个 SQL 查询 不 在 Lollipop 上不再按预期工作。与旧的 Android 版本相比,我的两个问题导致 Lollipop 的结果明显不同。
下面,我将更深入地描述这些问题及其解决方案,以防您遇到类似问题。
我的主要问题很简单:这些不向后兼容的更改是否记录在案?
问题一:匹配
以下查询似乎不再适用于 Lollipop:
SELECT title FROM ents JOIN ctt ON ctt.docid = ents.cttId WHERE (ctt MATCH '*ads*');
它不再返回任何结果,在棒棒糖之前它确实返回了(当然,使用相同的数据库和相同的数据)。
如this question 中所述,例如,MATCH 仅匹配字符串前缀。确实如此,搜索词前面的“*”在 Android
然而,Lollipop 的 SQLite 不喜欢第一个 '*' 并且不为此查询返回任何内容。我不得不将查询更改为以下内容以使其再次工作:
SELECT title FROM ents JOIN ctt ON ctt.docid = ents.cttId WHERE (ctt MATCH 'ads*');
(我正在使用 FTS3 进行全文搜索。)
问题二:整理本地化
简短的故事:使用 Android 特定的“COLLATE LOCALIZED”将原始名称引用的别名列与 ORDER BY 结合使用 GROUPing BY 会在 Lollipop 上引发错误,但适用于以前的版本。哇!? :-)
长篇大论:
故事从一个相当大的自动生成查询开始,所以我对其进行了修改、简化和缩短到导致问题的部分。我知道查询没有多大意义,如下所示,但它说明了问题。
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
上面的查询适用于
错误:没有这样的列: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 体验基于使用 Android 5.0 - API Level 21 ARM 系统映像的 SDK 模拟器中的测试。
对我来说,这个问题似乎是 Android 特有的 SQLite 错误。或者有人可以向我解释这个(在我看来)奇怪的行为吗?或者,再一次,这甚至在某个地方有记录吗? :-)
提前致谢!
【问题讨论】:
还值得在 Android 5.1 和 6.0 上进行测试,它们都带有不同的 SQLite 版本***.com/a/4377116/444761 【参考方案1】:观察到的变化是因为 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 子句中的标识符总是首选输出列名”。
【讨论】:
3.8 理论上应该将 SQLite 性能提高 50%。在棒棒糖升级后的实践中,由于某种原因,我的查询时间翻了一番,令人难以忍受.. 我不明白为什么他们改变了一些行为并且不使其与旧版本兼容。如果某个查询在某些版本中“正常工作”,那么它根本无法在较新的版本中中断!【参考方案2】:我发现 OP 存在一些问题。
FTS 使用百分比而不是星号作为通配符。
FTS 在字边界上搜索,因此拥有一个前导通配符从未与 FTS 一起使用,它会通过它传递给 sqlite 以非常缓慢地读取整个表格,但 OP 让我们相信它确实在过去的。因此,如果这个查询在过去确实运行过,它会运行得很慢。
联合查询总是占用过多的资源,尤其是与某些分组依据结合使用时。因此,此查询在可扩展的业务应用程序中没有位置。
【讨论】:
FTS3 uses an asterisk for a wildcard. 1。您将FTS MATCH
与SQL LIKE
混淆了
我阅读了文档。也许我没有错。您应该将文档固定到您的评论中。 FTS 进行单词边界搜索。【参考方案3】:
无论如何,我都不是 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 或更高版本。
【讨论】:
以上是关于Android Lollipop - 改变 SQLite 的行为的主要内容,如果未能解决你的问题,请参考以下文章