如何解决考虑到两者都存储在 8 个字节中,SQLITE 中的 REAL 类型是否可以用作像 INTEGER 这样的 rowid 的别名?
来自文档:
实数存储为 8 个字节:
真实的。该值是一个浮点值,存储为 8 字节 IEEE 浮点数。
RowID 也是 8 字节/64 位
sqlite 表中的所有行都有一个 64 位有符号整数键,唯一标识其表中的行
因此有一个功能,您可以使用 INTEGER 列并将其用作 RowID
如果一个 rowid 表有一个由单列组成的主键,并且该列的声明类型是大小写混合的“INTEGER”,那么该列将成为 rowid 的别名。 这样的列通常称为“整数主键”。
如果您考虑以下几点,这很重要:
rowid 表的数据存储为 B 树结构,每个表行包含一个条目,使用 rowid 值作为键。 这意味着按 rowid 检索或排序记录的速度很快。搜索具有特定 rowid 的记录或具有指定范围内的 rowid 的所有记录的速度大约是两倍作为通过指定任何其他 PRIMARY KEY 或索引值进行的类似搜索。
我正在 Qt/C++ 中构建一个以 sqlITE 作为后端的键值存储库,其中任何 INTEGER,REAL,BLOB,TEXT
数据类型都可用作键。 INTEGER
和 REAL
是 64 位,考虑到它们都是 8 字节,我想利用 rowid 性能提升。
sqlITE 但是只指定可以使用 INTEGER
。
问题:
- REAL 可以作为 rowid 的别名吗?
- 如果不是,为什么不完全是?这只是 sqlITE 开发人员的疏忽,还是有技术原因无法做到?
- 如果不是,我将如何在 Qt 上执行此操作,通过其字节签名而不是通过其值将
double
转换为long long int
?
谢谢。
解决方法
-
没有
-
如果一个 rowid 表有一个由单列组成的主键并且该列的声明类型是“INTEGER”(大小写混合),则该列成为别名为rowid
此外,SQLite 并没有真正的列类型。无论如何,行 ID 始终是整数。即使该列被标记为 REAL,它们仍然是整数值。您可以在 SQLite 的任何列中存储任何数据类型。
-
从数据库中读取行时,将其转换为您自己的程序代码。
rowid 是一个特殊的优化案例,除非你在表定义中特别使用 WITHOUT ROWID(很少使用)它总是存在的(使用 WITHOUT ROWID 你必须指定一个 PRIMARY KEY)
考虑到两者都存储在 8 个字节中?
INTEGERS 存储如下:- INTEGER。该值是一个有符号整数,根据值的大小存储在 1、2、3、4、6 或 8 个字节中。
REAL 可以作为 rowid 的别名吗?
不,因为 rowid 是一个特例。但是,您可以模拟使用实数并将其转换为整数但它必须解析为唯一的整数。 (参见示例 2、3 和 4)。
如果不是,为什么不完全是?这只是 SQLITE 开发人员的疏忽,还是有技术原因无法做到?
-
因为 rowid 是 SQLite 设计的核心方面,就像大多数事情一样,您可以按预期使用它们(唯一标识一行,否则使用它们通常会导致焦虑)。
- WITHOUT ROWID 子句是后来添加的。
-
处理整数比处理实数快。
- Rowid 表的区别在于它们都有一个唯一的、非空的、有符号的 64 位整数 rowid,用作底层 B 树存储引擎中数据的访问键。
-
整数会根据需要占用 1-8 个字节的空间,实数总是使用 8 个字节。
-
这不是疏忽,而是设计功能(请参阅 5.)。如果您想要一个根据 REAL 值的唯一索引,那么您不会被阻止使用一个索引(可能是一个 WITHOUT ROWID 表),但它会带来性能下降,因为不使用识别行的最快方法。
-
SQLite 文档包括
- 上述所有复杂情况(以及此处未提及的其他问题)都源于需要为流通中的数千亿 SQLite 数据库文件保持向后兼容性。在完美的世界中,不会有“rowid”这样的东西,所有表都将遵循作为 WITHOUT ROWID 表实现的标准语义,只是没有额外的“WITHOUT ROWID”关键字。不幸的是,生活是一团糟。 SQLite 的设计者对当前的混乱表示诚挚的歉意。 Rowid Tables
如果不是,我将如何在 Qt 上执行此操作,在那里我通过其字节签名而不是通过其值将 double 转换为 long long int?
您可以CAST
(参见示例 2 和 3) 但结果必须是唯一的整数(如果是 PRIMARY KEY 或 UNIQUE 索引)。当然,您可以根据 REAL 值增加一个索引。
以下是一些示例,可以证明以上内容:-
DROP TABLE IF EXISTS example1;
DROP TABLE IF EXISTS example2;
DROP TABLE IF EXISTS example3;
DROP TABLE IF EXISTS example4;
/* rowid always exists for table unless WITHOUT ROWID table */
/* note cannot specify rowid value */
CREATE TABLE IF NOT EXISTS example1 (col1);
INSERT INTO example1 VALUES('x'),('y'),('z');
SELECT *,rowid,oid,_rowid_ FROM example1;
/* Ooops not an alias as INTEGER not specified,but rowid exists*/
CREATE TABLE IF NOT EXISTS example2 (col1,rowid_alias,PRIMARY KEY(rowid_alias));
INSERT INTO example2 VALUES('a',null),('b',CAST(10.4567 AS INTEGER)),('c',null);
SELECT *,_rowid_ FROM example2;
/* rowid_alias is an alias of the rowid */
CREATE TABLE IF NOT EXISTS example3 (col1,rowid_alias INTEGER,PRIMARY KEY(rowid_alias));
INSERT INTO example3 VALUES('a',_rowid_ FROM example3;
/* sort of mimic rowid using real */
CREATE TABLE IF NOT EXISTS example4 (mimic_rowid);
INSERT OR IGNORE INTO example4 VALUES
((coalesce((SELECT max(mimic_rowid) FROM example4),0.1234) + 1.11))
;
INSERT OR IGNORE INTO example4 VALUES
((coalesce((SELECT max(mimic_rowid) FROM example4),0.1234) + 1.11))
;
SELECT *,rowid FROM example4;
DROP TABLE IF EXISTS example1;
DROP TABLE IF EXISTS example2;
DROP TABLE IF EXISTS example3;
DROP TABLE IF EXISTS example4;
运行上面的:-
第一个结果显示 rowid 即使没有别名也存在:-
第二个/第三个结果显示,要为 rowid 设置别名,它必须是 INTEGER PRIMARY KEY(隐式,即在表级别而不是列级别指定 PRIMARY KEY):-
- 第一个(示例 2)结果不是别名:-
-
- 注意 CAST 用于将 REAL 转换为 INTEGER 以进行第二次插入
- 可以看出,第三次插入使用最大现有 rowid + 1 生成下一个 rowid(不保证为 + 1,但通常如此)
最后一个示例复制(触发器会自动执行)模仿 rowid 但对于 REAL :-
- 如果不是唯一的,OR IGNORE 将跳过插入而不是失败
一些链接:-
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。