考虑到两者都存储在 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 数据类型都可用作键。 INTEGERREAL 是 64 位,考虑到它们都是 8 字节,我想利用 rowid 性能提升。

sqlITE 但是只指定可以使用 INTEGER

问题:

  1. REAL 可以作为 rowid 的别名吗?
  2. 如果不是,为什么不完全是?这只是 sqlITE 开发人员的疏忽,还是有技术原因无法做到?
  3. 如果不是,我将如何在 Qt 上执行此操作,通过其字节签名而不是通过其值将 double 转换为 long long int

谢谢。

解决方法

  1. 没有

  2. 如果一个 rowid 表有一个由单列组成的主键并且该列的声明类型是“INTEGER”(大小写混合),则该列成为别名为rowid

    此外,SQLite 并没有真正的列类型。无论如何,行 ID 始终是整数。即使该列被标记为 REAL,它们仍然是整数值。您可以在 SQLite 的任何列中存储任何数据类型。

  3. 从数据库中读取行时,将其转换为您自己的程序代码。

,

rowid 是一个特殊的优化案例,除非你在表定义中特别使用 WITHOUT ROWID(很少使用)它总是存在的(使用 WITHOUT ROWID 你必须指定一个 PRIMARY KEY)

考虑到两者都存储在 8 个字节中?

INTEGERS 存储如下:- INTEGER。该值是一个有符号整数,根据值的大小存储在 1、2、3、4、6 或 8 个字节中。

REAL 可以作为 rowid 的别名吗?

不,因为 rowid 是一个特例。但是,您可以模拟使用实数并将其转换为整数它必须解析为唯一的整数。 (参见示例 2、3 和 4)。

如果不是,为什么不完全是?这只是 SQLITE 开发人员的疏忽,还是有技术原因无法做到?

  1. 因为 rowid 是 SQLite 设计的核心方面,就像大多数事情一样,您可以按预期使用它们(唯一标识一行,否则使用它们通常会导致焦虑)。

    • WITHOUT ROWID 子句是后来添加的。
  2. 处理整数比处理实数快。

    • Rowid 表的区别在于它们都有一个唯一的、非空的、有符号的 64 位整数 rowid,用作底层 B 树存储引擎中数据的访问键。
  3. 整数会根据需要占用 1-8 个字节的空间,实数总是使用 8 个字节。

  4. 这不是疏忽,而是设计功能(请参阅 5.)。如果您想要一个根据 REAL 值的唯一索引,那么您不会被阻止使用一个索引(可能是一个 WITHOUT ROWID 表),但它会带来性能下降,因为不使用识别行的最快方法。

  5. 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 即使没有别名也存在:-

enter image description here

第二个/第三个结果显示,要为 rowid 设置别名,它必须是 INTEGER PRIMARY KEY(隐式,即在表级别而不是列级别指定 PRIMARY KEY):-

  • 第一个(示例 2)结果不是别名:-

enter image description here

  • 第二个(示例 3)别名:- enter image description here

    • 注意 CAST 用于将 REAL 转换为 INTEGER 以进行第二次插入
    • 可以看出,第三次插入使用最大现有 rowid + 1 生成下一个 rowid(不保证为 + 1,但通常如此)

最后一个示例复制(触发器会自动执行)模仿 rowid 但对于 REAL :-

enter image description here

  • 如果不是唯一的,OR IGNORE 将跳过插入而不是失败

一些链接:-

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...