【问题标题】:What is the best way to create a SQLITE table with REAL values including a UNIQUE constraint?创建具有 REAL 值(包括 UNIQUE 约束)的 SQLITE 表的最佳方法是什么?
【发布时间】:2019-11-17 15:37:13
【问题描述】:

我试图了解在 SQLITE3 中处理与浮点数比较的最佳方法。一般来说,我知道测试浮点数是否相等是一种不好的做法。但是,我不确定如何在 SQLITE 关系数据库中执行此操作。

对表中的行使用 UNIQUE 约束(根据定义)是测试相等性。那么,在使用 REAL 数据类型时,这是一种不好的做法吗?

如果我想选择那个唯一的行,只知道 REAL 值,我应该怎么做?

我在 SQLITE3 中有下表:

CREATE TABLE nodes(
    node_id INTEGER PRIMARY KEY,
    node_number INTEGER,
    x REAL,
    y REAL,
    z REAL,
    UNIQUE(node_number, x, y, z)
);

对于表格插入,我执行以下操作:

INSERT OR IGNORE INTO
    nodes(node_number, x, y, z)
VALUES
    (?, ?, ?, ?);

要查找 node_id(用于后续插入),我执行以下操作:

SELECT
    node_id
FROM
    nodes
WHERE
    node_number = ?
AND
    x = ?
AND
    y = ?
AND
    z = ?;

【问题讨论】:

  • stackoverflow.com/a/23690512/7919597 你可以检查类似(abs(a-b) >= 0.01)
  • 也许您可以定义一个自定义 https://docs.sqlalchemy.org/en/13/core/constraints.html#sqlalchemy.schema.UniqueConstraint 并覆盖 __eq__,但您必须询问 SQLAlchemy 专家。
  • 您提供的链接中的一个建议是使用 DECIMAL 类型。但是,当我阅读 Sqlite 文档时,似乎 Sqlite 只是将其变成了 REAL,而其他数据库引擎的工作方式与 Python 的 Decimal 类类似。我想知道是否可以乘以所需的精度(10 ^ 9)并将其存储为整数?这一切对我来说似乎比它应该的更hack。

标签: python sql database sqlite data-structures


【解决方案1】:

在 SQLite 中,CHECK 约束不能有子查询,因此如果您想在 SQLite 本身中执行邻近检查,则必须使用触发器,例如:

CREATE TRIGGER notequal
   BEFORE INSERT ON test
BEGIN
  SELECT CASE
    WHEN (EXISTS (SELECT r FROM test WHERE abs(NEW.r - r) < 0.1))
    THEN RAISE (ABORT,'Value too close to an existing value')
  END;
END;

当然,您也希望为 UPDATE 做同样的事情。

【讨论】:

  • 我不知道触发器。我想您应该将 0.1 更改为所需的准确度?
【解决方案2】:

一种选择可能是将值存储为格式化字符串并对其强制执行 UNIQUE 约束。

CREATE TABLE nodes(
    node_id INTEGER PRIMARY KEY,
    node_number INTEGER,
    x TEXT,
    y TEXT,
    z TEXT,
    UNIQUE(node_number, x, y, z)
);

浮点值的存储位置:

CREATE TABLE node_values(
    node_value_id INTEGER PRIMARY KEY,
    node_id REFERENCES nodes(node_id),
    x REAL,
    y REAL,
    z REAL,
    UNIQUE(node_id)
);

插入可能如下所示:

Python

...
node_values = [
    (nnum1, x1, y1, z1),
    (nnum2, x2, y2, z2),
    ...
]
sql_insert_text_vals = """
INSERT OR IGNORE INTO
    nodes(node_number, x, y, z)
VALUES
    (?, ?, ?, ?);
"""
text_values = [
    (node_number, f'{xval:.10f}', f'{yval:.10f}', f'{zval:.10f}')
    for node_number, xval, yval, zval
    in node_values
]
cursor.executemany(sql_insert_text_vals, text_values)

sql_query = """
SELECT
    node_id
VALUES
    (?, ?, ?, ?);
"""
node_ids = [
    cursor.fetchone()[0]
    for value in text_values
    for cursor.execute(sql_query, value)
]

sql_insert_node_values = """
INSERT OR IGNORE INTO
    node_values(node_id, x, y, z)
VALUES
    (?, ?, ?, ?);
"""
real_values = [
    (node_id, xval, yval, zval)
    for node_id, (node_number, xval, yval, zval)
    in zip(node_ids, node_values)
]
cursor.executemany(sql_insert_node_values, real_values)

这样更好吗?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-07
    • 2016-04-13
    • 1970-01-01
    • 2021-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-06
    相关资源
    最近更新 更多