具有小数/浮点比较的 sqlite 选择查询不起作用

Posted

技术标签:

【中文标题】具有小数/浮点比较的 sqlite 选择查询不起作用【英文标题】:sqlite select query with decimal/float comparison doesn't work 【发布时间】:2011-04-06 23:29:39 【问题描述】:

我需要使用简单的select来进行十进制比较操作,例如:

select * from table_a where time > 0.0004

身份证时间


0 0.000502 1 0.000745 2 0.000725 4 0.000197 5 0.000176 6 0.000833 7 0.000269 8 0.000307 9 0.000302

结果包含满足比较的值。简而言之,“时间 > 0.0004”总是计算为真,而“时间

我尝试将时间列的类型从十进制转换为浮点型,但没有成功。

如果我使用子查询,它会正确执行查询:

select * from table_a where time > (select time from table_a where id=8);

身份证时间


0 0.000502 1 0.000745 2 0.000725 6 0.000833

表创建:

CREATE TABLE "table_a" ("id" integer NOT NULL PRIMARY KEY, "time" decimal NOT NULL);

【问题讨论】:

SELECT id, time, typeof(time) FROM table_a 返回什么? typeof(time) 为所有行返回“文本”。我使用“.import”命令加载表,看来这就是问题所在。如果我使用“INSERT”插入记录,则值的类型是“真实”而不是“文本”。 感谢您发布有关数据如何以文本形式插入的详细说明。如果您不注意那些数据类型的事故,SQLite 有时会有点棘手。就个人而言,我们所有的模式在列上都有check 约束以强制执行特定的数据类型。 【参考方案1】:

感谢大家的建议,我找到了问题的根源:

“时间”列值被作为“文本”处理。为什么?

我正在使用“.import”将数据导入表,而我的 CSV 文件包含十进制值周围的空格。不知何故,即使类型不匹配,SQLite 的导入命令也允许插入发生。从 CSV 文件中删除空格后,数据将作为“真实”插入,这样可以正确进行数字比较。但是,它没有解释为什么“十进制”列中的数据是“真实”类型而不是“十进制”或“数字”类型,如他们的文档中所述:http://www.sqlite.org/datatype3.html

select id, time, typeof(time) from table_a;
id    time           type
----  -------------  ----
0      0.000502      text
1      0.000745      text
2      0.000725      text
4      0.000197      text
5      0.000176      text
6      0.000833      text
7      0.000269      text
8      0.000307      text
9      0.000302      text

select id, time, typeof(time) from table_b;
id    time           type
----  -------------  ----
0     0.000502       real
1     0.000745       real
2     0.000725       real
4     0.000197       real
5     0.000176       real
6     0.000833       real
7     0.000269       real
8     0.000307       real
9     0.000302       real

【讨论】:

啊哈,空格。跟你说了。 :-) 至于类型:“十进制”和“数字”是 SQLite 中的列类型,但不是数据类型。它们告诉 SQLite 对列中的数据尝试哪些转换,但它们本身并不是数据的可能类型。再看看你链接到的那个页面。【参考方案2】:

SQLite 有一个非正统的动态类型系统,其中列的类型并不能确定列中实际数据的类型,它只是鼓励 SQLite 在可能的情况下将数据转换为特定类型。

当尝试比较字符串和数字时,如果字符串不是格式正确的数字,则认为它大于数字。

所以一个明显的猜测是,出于某种原因,这些时间值实际上不是数字,而是字符串。这令人费解有两个原因。 (1) 由于time 列的类型为decimal,它应该具有“数字”亲和性,它应该获取存储在其中的任何看起来像数字转换为数字的东西。 (2) 即使值被存储为字符串,它们仍然应该被转换为数字,以便与 0.0004 进行比较。

为什么他们没有被转化?可能性 #1:它们可能包含额外的空格或类似的东西。可能性 2:也许您的语言环境想要使用 . 以外的其他内容作为小数点。 (可能还有其他我没有想到的可能性。)

如果您将一条记录插入到实际包含数字(insert into table_a (id,time) values (999,0.0001) 或类似的东西)的表中,该记录是否会包含在您的选择中?

【讨论】:

0.0001 的 INSERT 确实包含在选择中。请参阅我对 Samuel Neff 的回答。【参考方案3】:

老问题,但我想给出另一个答案。

因为 SQLite 倾向于将所有内容都视为文本,所以会发生以下情况:

如果您查询... where a < 1000,如果 a 被视为文本,它将永远找不到任何东西

如果您查询... where a < '1000',它会将千位作为某种文本进行比较,如果数字具有不同的字符长度,则会出现问题。 1000 是 4 个字符,100 是 3 个等等

但是有CAST表达式http://www.sqlite.org/lang_expr.html#castexpr

所以... where cast(a as Integer) < 1000 会得到你想要的结果(可以用 REAL 来完成浮点数)

【讨论】:

【参考方案4】:

我无法重现您看到的行为。即使我得到正确的结果

将时间列定义为varchar, 在单引号内插入值, 在双引号内插入值, 将时间列定义为十进制和 首先插入文本文字(“测试文本”)

但是,像 Samuel Neff 一样,我仍然希望在您的数据库中看到 SELECT id, time, typeof(time) FROM table_a; 的结果。你知道还有什么有趣的吗?结果

select * from table_a order by time;

这些结果来自版本 3.7.4,可能不是当前版本。

sqlite> .dump table_a
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE "table_a" ("id" integer NOT NULL PRIMARY KEY, "time" decimal NOT NULL);
INSERT INTO "table_a" VALUES(1,0.4);
INSERT INTO "table_a" VALUES(2,0.6);
INSERT INTO "table_a" VALUES(3,0.0005);
INSERT INTO "table_a" VALUES(4,0.0006);
INSERT INTO "table_a" VALUES(5,0.0004);
COMMIT;

sqlite> select * from table_a where time < 0.0005;
5|0.0004

sqlite> SELECT id, time, typeof(time) FROM table_a;
1|0.4|real
2|0.6|real
3|0.0005|real
4|0.0006|real
5|0.0004|real

【讨论】:

“ORDER BY time”确实返回了一个有序的结果。但是,我运行了您的交易并且选择工作正常。我认为问题在于我使用“.import”来加载表格。我会做更多的测试并发布我的结果。 我确信它会被订购。我不确定它是否会按数字排序。【参考方案5】:

显然这不是您的情况,但请记住,有时某些管理工具(例如 SQLite Administrator 或 SQLite Expert)可能会向您显示相同的真实值,尽管它们并非如此。这是一个例子:

差异很小,但可能足以让您头疼。

【讨论】:

以上是关于具有小数/浮点比较的 sqlite 选择查询不起作用的主要内容,如果未能解决你的问题,请参考以下文章

System.Data.Sqlite 1.0.99 引导比较不起作用

具有多态连接的 Laravel 查询生成器在 SQLite 上不起作用

Sqlite FTS5 标点符号在选择查询中不起作用

请问sqlite中精度的问题

将字符串浮点数列表转换为具有 2 个小数点的浮点数列表

python:格式化具有最大小数位数的浮点字符串[重复]