sql - 值得去规范化我们的数据吗?

标签 sql sqlite normalization

关于标准化数据有很多帖子和讨论。在大多数情况下,我看到人们很难坚持规范化,但并非总是如此,这似乎是个案的情况,所以我将描述一下我们的案例。它似乎并不复杂,但是我感觉也许我只是缺少一些优雅的东西。我希望有人可以:


给我或指出我的特定解决方案或解决方案类型,或者
支持我正在考虑的非规范化想法。


最主要的是,我们将要做的是近实时搜索,当用户在搜索字段中输入文本时,逐个字符地过滤结果,因此需要快速响应。但是功耗很低的硬件-想想物联网。搜索需要返回单个项目名称,捆绑软件名称以及找到的捆绑软件中单个项目的列表。物品和捆绑包具有多对多关系,尽管任何捆绑包中的物品数量是有限的,所以值得限制。

Ex DB: 
[ items ]
    int: item_id
    string: name
    ….
[ bundles ]
    int: bundle_id
    string: bundle_name
    ….
[ items_x_bundles ]
    int: item_id
    int: bundle_id


想象一下礼品篮中的不同食物包,在给定的购物篮组合中,食物通常不超过10个,但没有绝对固定的限制。新的捆绑软件很少创建,并且永远不会更改。

可以说有各种单独的项目,例如:

apple, orange, pear, banana, saltines, cheez-its, ritz, 
potato chips, carrots, peas, beans, oreos, gummies, 
hershey bars, coke, gatorade, milk, etc.


和捆绑包,例如:

special : [ apple, saltines, peas, gummies, coke ], 
deluxe: [ pear, carrots, potato chips, oreos ],
fancy: [ orange, ritz, beans, gummies, milk ],
mondo: [ banana, pear, saltines, carrots, peas, oreos, coke, milk ]


搜索“ delu”将返回:

[ deluxe: [ pear, carrots, potato chips, oreos ]


搜索“ appl”将返回:

[ apple ] 
[ special : [ apple, saltines, peas, gummies, coke ] ]


搜索“牛奶”将返回:

[ milk ]
[ fancy: [ orange, ritz, beans, gummies, milk ]
[ mondo: [banana, pear, saltines, carrots, peas, oreos, coke, milk ]


如果我们使数据完全标准化,就很容易找到单个项目的名称,但是要返回每个包含搜索字符串的购物篮中单个项目的列表则要复杂得多。效率很重要,因为这将再次在低功耗IoT硬件上运行。如果重要,请使用sqlite3。

一种潜在的解决方案是在创建捆绑包时向捆绑包表添加一个字段。就像是:

    string: bundle_items


对于[特殊],它可能看起来像:

    "apple / saltines / peas / gummies / coke".


这使得一切都更快/更容易地进行搜索,但却以冗余为代价。对我来说,这就像是一个“ hack”,但是我没有看到明显的优雅,有效的解决方案。

更新

我正在将5个更新/迭代压缩为仅此一个。

上面我可能没有像以前那样清楚,但是性能问题是固有的。低功耗的IoT级硬件,以及面向用户的实时过滤器,需要使用输入的每个字符来搜索数据。我们预计,无论我们如何构造它,都不会像我们想要的那样快,因为任何延迟都将直接被用户察觉,甚至不到一秒钟。我没有确切的数字,因为在开发机上执行基准测试仿真相当容易,而在实际硬件上则没有那么多。这是否意味着我们需要取消规范化/优化“无所谓”?也许吧,但我还不十分了解这一事实,因此这里是一个问题。另外,我想知道我们正在考虑的特殊非规范化方法是否有明显的顾虑。

我知道如何查询非标准化数据,但不知道如何对标准化数据构造一个智能的,合理优化的查询。这可以帮助我们做出决定。所以:

问题#1)对标准化数据进行智能(快速)查询会达到上面列出的结果吗?

问题#2)有人看到我描述的反规范化方法有什么明显的问题吗?在所描述的上下文中,这是否有意义和/或是否存在其他更好的解决方案?

经过一对夫妇之后,Bill Karwin的以下查询有效,因此请回答第一部分,谢谢。第2部分可能最终会遇到另一个问题。

如果有人继续关注,不同类型查询的实际百分比差异变化很大(取决于记录数),坦率地说,我们需要更深入地研究。有所不同不足为奇,但数量惊人。从大约15倍变化到超过35,000倍,记录数量也不算不合理。即使是15倍(这可能更接近真实世界),我也会说我们倾向于去规范化,但这提供了一个有效的规范化查询进行测试。

最佳答案

如果将数据保存在标准化表中,则可以执行以下查询:

经过几次编辑并测试了此查询(SQLFiddle):

SELECT CONCAT(b1.bundle_name, ' : ', GROUP_CONCAT(i1.name))
FROM bundles b1 
JOIN items_x_bundles bi1 USING (bundle_id)
JOIN items i1 USING (item_id)
WHERE b1.bundle_name LIKE CONCAT('milk', '%')
GROUP BY b1.bundle_id
UNION ALL
SELECT CONCAT(b2.bundle_name, ' : ', GROUP_CONCAT(i2b.name))
FROM bundles b2
JOIN items_x_bundles bi2 ON (b2.bundle_id=bi2.bundle_id)
JOIN items i2 ON (bi2.item_id=i2.item_id)
JOIN items_x_bundles bi2b ON (b2.bundle_id=bi2b.bundle_id)
JOIN items i2b ON (bi2b.item_id=i2b.item_id)
WHERE i2.name LIKE CONCAT('milk', '%')
GROUP BY b2.bundle_id
UNION ALL
SELECT i3.name
FROM items i3
WHERE i3.name LIKE CONCAT('milk', '%')


?占位符是您绑定搜索词的地方。是的,您必须将其绑定三遍。

将索引放在items(name)bundles(bundle_name)items_x_bundles(item_id,bundle_id)items_x_bundles(bundle_id,item_id)上。

然后使用EXPLAIN确认查询有效地使用了索引。

关于sql - 值得去规范化我们的数据吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38492106/

相关文章:

java - 准备好的语句未更新记录

android - 从 Android 应用程序中的 sqlite 数据库中删除条目。在哪里获取该行 id 以将其传递给删除函数

java - 数据未插入到 android 上的 SQLite 数据库中?

mysql - 规范化超出 MySQL 知识范围?

mysql - 使用 mySQL 查询 Top contributor

mysql - 在同一查询中对同一 WHERE 子句执行 COUNT 和 SUM

python - 标准化包含过大值的数据集

mysql - MySQL检查数据库是否规范化和功能正常

python - 如何从表中选择 2 个不同的随机行?

javascript - electronjs中ipc通信的问题