值得对我们的数据进行去标准化吗?

时间:2016-07-20 22:46:19

标签: 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 ]

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

一个潜在的解决方案是在创建包时向Bundle表中添加一个字段。类似的东西:

    string: bundle_items

对于[特殊]可能是这样的:

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

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

更新

我将5次更新/迭代压缩成这一次。

也许我并不像以前那样明确,但性能问题是固有的。低功耗物联网级硬件,以及面向用户的实时过滤器,需要在输入的每个字符中搜索数据。我们预计无论我们如何构建它,它都不会像我们希望的那样快,因为任何延迟都会直接引起用户注意,甚至几分之一秒。我没有硬数字,因为在开发机器上执行基准测试模拟相当容易,而在真实硬件上却没有那么多。这是否意味着我们需要去标准化/优化无关紧要什么?或许,但我还没有真正了解这个事实,因此问题就在这里。另外,我想知道是否对我们正在考虑的特定去标准化方法(上文)有任何明显的担忧。

我知道如何查询非规范化数据,但我不知道如何在规范化数据上构建智能,合理优化的查询。这可以帮助指导我们做出决定。所以:

问题#1)对规范化数据进行智能(快速)查询会是什么样的,以实现上面列出的结果?

问题#2)有没有人看到我所描述的去标准化方法有任何明显的问题。在所描述的上下文中,它是否有意义和/或是否有不同的,更好的解决方案?

经过几次通过后,Bill Karwin的查询工作正常,所以回答第一部分,谢谢。第2部分最终可能会出现另一个问题。

如果有人跟进,不同类型的查询的实际百分比差异变化很大(取决于记录的数量),坦率地说我们需要深入挖掘。它的不同并不奇怪,但数量惊人。从大约15倍到超过35,000倍不等,并没有不合理的记录数量。即使在15x,这可能更接近真实世界,我会说我们倾向于去标准化,但是这给了一个正常的查询来进行测试。

2 个答案:

答案 0 :(得分:1)

评论太长了。

规范化是工具,可在为关系数据库设计数据模型时使用。它非常强大。但是,它的初衷是支持数据完整性。任何数据项都存储在一个地方,恰好一次。更新很简单,因为更新只发生一次。在更新数据时,规范化尤其重要,因此基础数据模型可以保持一致性。

通常,关系数据库用于其他目的,例如分析和报告。实际上,我经常使用一次创建的表,然后多次查询。必要时会重新创建它们。在这种情况下,规范化并不一定有用。

是否规范化数据以及如何规范化数据在很大程度上取决于应用程序。我倾向于误差归一化的大小;但是如果你有充分理由对数据进行非规范化,那么对于主要是只读的应用程序来说这是非常好的。

答案 1 :(得分:0)

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

经过几次编辑并测试此查询(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确认查询是否有效使用索引。