一、概述
SQLite 中的 SQL 函数可以是“确定性的”或“非确定性的”。
当确定性函数具有相同的输入时,它总是给出相同的答案。SQLite 中的大多数内置 SQL 函数都是确定性的。例如,只要输入 X 相同 , abs(X)函数就始终返回相同的答案。
非确定性函数可能会在每次调用时给出不同的答案,即使参数始终相同。以下是非确定性函数的示例:
random()函数显然是不确定 的,因为它每次被调用时都会给出不同的答案。changes() 和last_insert_rowid()的答案取决于先前的 SQL 语句,因此它们也是不确定的。sqlite3_version()函数大部分是不变的 ,但它可以在 SQLite 升级时改变,因此即使它总是为任何特定会话返回相同的答案,因为它可以跨会话改变答案,它仍然被认为是不确定的。
2.非确定性函数的使用限制
SQLite 中有一些上下文不允许使用非确定性函数:
在上述情况下,函数返回的值会影响存储在数据库文件中的信息。CHECK 约束中函数的值确定哪些条目对表有效,部分索引的 WHERE 子句或表达式的索引中的函数计算存储在索引 b 树中的值。如果这些函数中的任何一个稍后返回不同的值,则数据库可能不再是良构的。因此,为避免数据库损坏,只能在上述上下文中使用确定性函数。
3.日期/时间函数的特例处理
SQLite内置的日期和时间函数是一个特例。这些函数通常被认为是确定性的。但是,如果这些函数使用字符串“now”作为日期,或者如果它们使用localtime modifier或utc modifier,则它们被认为是不确定的。因为函数输入在运行时之前不一定是已知的,所以如果日期/时间函数在只允许确定性函数的上下文中遇到任何非确定性特征,它们将抛出异常。
在 SQLite 3.20.0 (2017-08-01) 之前,所有日期/时间函数始终被认为是不确定的。3.20.0 版本添加了日期/时间函数有时是确定性的而有时是非确定性的功能,具体取决于它们的参数。
3.1. 版本 3.35.2 中的错误修复
当对 SQLite 3.20.0 进行增强时,日期/时间函数将被认为是确定性的,因为它们不依赖于当前时间,一种情况被忽略了:许多日期/时间函数可以在没有参数的情况下调用. 这些无参数日期/时间函数的行为就好像它们只有一个“ 'now' ”参数一样。因此,“ datetime() ”和“ datetime('now') ”都产生当前日期和时间。然而,只有第二种形式被认为是不确定的。这意味着开发人员可以偷偷使用非确定性的“ datetime()" 形成 CHECK 约束、索引表达式、生成的列表达式以及非确定性函数没有意义的类似地方。此疏忽已在版本 3.35.2 (2021-03-17) 中修复。但是,可能存在遗留数据库由 SQLite 版本 3.20.0 到 3.35.1 创建的循环,它们的模式中具有不确定的日期/时间函数。
4.应用程序定义的确定性函数
默认情况下,应用程序定义的 SQL 函数被认为是不确定的。但是,如果 sqlite3_create_function_v2()的第 4 个参数与SQLITE_DETERMINISTIC进行 OR 运算,则 SQLite 会将该函数视为确定性函数。
请注意,如果一个非确定性函数被标记为 SQLITE_DETERMINISTIC并且如果该函数最终被用于部分索引的 WHERE 子句或 表达式索引中,那么当该函数开始返回不同的答案时,关联的索引可能会损坏. 如果 SQL 函数几乎是确定性的(也就是说,如果它很少更改,如sqlite_version())并且它用于损坏的索引,则可以通过运行REINDEX修复损坏。
构建一个有时是确定性的,有时是非确定性的函数所必需的接口,取决于它们的输入,例如内置的日期/时间函数,没有发布。通用应用程序定义的 SQL 函数必须始终是确定性的或始终是非确定性的。