【问题标题】:How to implement filter system in SQL?如何在 SQL 中实现过滤系统?
【发布时间】:2011-02-15 07:27:16
【问题描述】:

现在我正计划在我的网站上添加一个过滤系统。

例子:

(ID=apple, COLOR=red, TASTE=sweet, ORIGIN=US)
(ID=mango, COLOR=yellow, TASTE=sweet, ORIGIN=MEXICO)
(ID=banana, COLOR=yellow, TASTE=bitter-sweet, ORIGIN=US)

所以现在我有兴趣做以下事情: 从此表中选择 ID,其中 COLOR='yellow' AND TASTE='SWEET'

但我的问题是我正在为我网站中的多个类别执行此操作,并且列不一致。 (例如,如果该表是用于手机的,那么它将是 BRAND、3G-ENABLED、PRICE、COLOR、WAVELENGTH 等)

我如何设计一个允许这样做的通用架构?

现在我打算做:

table(ID, KEY, VALUE)

这允许任意数量的列,但对于查询,我使用 SELECT ID FROM table WHERE (KEY=X1 AND VALUE=V1) AND (KEY=X2 AND VALUE=V2), .. 返回一个空集。

有人可以推荐一个好的解决方案吗?请注意,列数会定期变化

【问题讨论】:

标签: sql mysql


【解决方案1】:

您的建议被称为实体-属性-值结构,并且高度不鼓励。例如,EAV 设计的(许多)大问题之一是数据完整性。你如何强制颜色只包含“红色”、“黄色”、“蓝色”等?简而言之,你不能没有很多黑客。另一个问题出现在查询(如您所见)和搜索数据中。

相反,我建议创建一个代表每种实体类型的表,因此每个表都可以具有特定于该类型实体的属性(列)。

为了在搜索时将数据转换为结果查询中的列,您需要创建通常称为交叉表查询的内容。有报告引擎可以做到这一点,您可以编写代码,但大多数数据库产品本身不会这样做(这意味着无需手动构建 SQL 字符串)。如果您有很多数据,性能当然不会很好,并且您会遇到过滤数据的问题。例如,假设某些值应该是数字。由于 EAV 的值部分可能是字符串,因此您必须先将这些值转换为整数,然后才能对其进行过滤,并且假定数据可以转换为整数。

【讨论】:

    【解决方案2】:

    您建议的entity-attribute-value 模型适合这种情况。

    关于过滤查询,您必须了解,使用 EAV 模型会牺牲大量查询能力,因此这会变得相当棘手。然而,这是解决您的问题的一种方法:

    SELECT    stuff.id 
    FROM      stuff 
    JOIN      (SELECT    COUNT(*) matches
               FROM      table
               WHERE     (`key` = X1 AND `value` = V1) OR 
                         (`key` = X2 AND `value` = V2) 
               GROUP BY  id
              ) sub_t ON (sub_t.matches = 2 AND sub_t.id = stuff.id)
    GROUP BY  stuff.id;
    

    这种方法的一个不优雅的特点是您需要指定您希望在sub_t.matches = 2 中匹配的属性/值对的数量。如果我们有三个条件,我们将不得不指定sub_t.matches = 3,等等。

    让我们构建一个测试用例:

    CREATE TABLE stuff (`id` varchar(20), `key` varchar(20), `value` varchar(20));
    
    INSERT INTO stuff VALUES ('apple',  'color',  'red');
    INSERT INTO stuff VALUES ('mango',  'color',  'yellow');
    INSERT INTO stuff VALUES ('banana', 'color',  'yellow');
    
    INSERT INTO stuff VALUES ('apple',  'taste',  'sweet');
    INSERT INTO stuff VALUES ('mango',  'taste',  'sweet');
    INSERT INTO stuff VALUES ('banana', 'taste',  'bitter-sweet');
    
    INSERT INTO stuff VALUES ('apple',  'origin',  'US');
    INSERT INTO stuff VALUES ('mango',  'origin',  'MEXICO');
    INSERT INTO stuff VALUES ('banana', 'origin',  'US');
    

    查询:

    SELECT    stuff.id 
    FROM      stuff 
    JOIN      (SELECT    COUNT(*) matches, id
               FROM      stuff
               WHERE     (`key` = 'color' AND `value` = 'yellow') OR 
                         (`key` = 'taste' AND `value` = 'sweet')
               GROUP BY  id
              ) sub_t ON (sub_t.matches = 2 AND sub_t.id = stuff.id)
    GROUP BY  stuff.id;
    

    结果:

    +-------+
    | id    |
    +-------+
    | mango |
    +-------+
    1 row in set (0.02 sec)
    

    现在让我们用color=yellowtaste=sweet 插入另一个水果:

    INSERT INTO stuff VALUES ('pear', 'color', 'yellow');
    INSERT INTO stuff VALUES ('pear', 'taste', 'sweet');
    INSERT INTO stuff VALUES ('pear', 'origin', 'somewhere');
    

    同样的查询会返回:

    +-------+
    | id    |
    +-------+
    | mango |
    | pear  |
    +-------+
    2 rows in set (0.00 sec)
    

    如果我们想将此结果限制为具有origin=MEXICO 的实体,则必须添加另一个OR 条件并检查sub_t.matches = 3 而不是2

    SELECT    stuff.id 
    FROM      stuff 
    JOIN      (SELECT    COUNT(*) matches, id
               FROM      stuff
               WHERE     (`key` = 'color' AND `value` = 'yellow') OR 
                         (`key` = 'taste' AND `value` = 'sweet') OR 
                         (`key` = 'origin' AND `value` = 'MEXICO')
               GROUP BY  id
              ) sub_t ON (sub_t.matches = 3 AND sub_t.id = stuff.id)
    GROUP BY  stuff.id;
    

    结果:

    +-------+
    | id    |
    +-------+
    | mango |
    +-------+
    1 row in set (0.00 sec)
    

    与每种方法一样,使用 EAV 模型时也有一定的优点和缺点。确保您在应用程序的上下文中广泛研究该主题。您甚至可能需要考虑替代关系数据库,例如CassandraCouchDBMongoDBVoldemortHBaseSimpleDB 或其他键值存储。

    【讨论】:

    • 哇,这看起来很复杂。感谢您的解决方案。很多人反对我用这个设计,所以现在我在认真考虑是否应该用EVA模型
    • @sadvaw:反对意见主要源于这样一个事实,即当您在关系数据库中使用 EAV 模型时,就像使用面包车在城市中行驶一样:因此您没有使用它它的目的是什么。然而它仍然可以完成,并且这种做法的可行性通常取决于规模(你做了多少,或者有多大)。因此,我会说,如果您在数据库中所做的一切就是这样,那么我实际上会考虑 RDBMS 的替代品。但是,如果您有一个更大的数据库,而这只是一小部分,那么这些考虑因素可能就不那么重要了。
    【解决方案3】:

    从长远来看,您在此阶段为简单的桌子设计付出的代价会降低您的性能。使用ORM 来降低修改数据库以使数据适应适当结构的成本可能是一个很好的时间投资,尽管 ORM 的性能成本很高。

    否则,您可能需要寻找一种“反向 ORM”,该“逆向 ORM”可以从您的数据库中映射代码,它的好处是成本更低且性能更高。 (与 ORM 相比,起步成本略高,但长期性能和可靠性更好。)

    无论您如何切片,这都是一个代价高昂的问题。您想现在支付开发时间还是稍后支付性能下降? (“稍后付款”是错误的答案。)

    【讨论】:

    • 你能推荐一个适合你答案的桌子设计吗?我不太明白你指的是什么。
    • 我偶然发现了我所暗示的理论的名称:锚建模。来源有点学术:syslab.dsv.su.se/profiles/blogs/anchor-modeling 所以你可能会发现这个解释更容易理解:askmonty.org/wiki/Manual:Table_Elimination 过程数据库转换(ORM 或反向 ORM 技术)的(相关但独立的)点是减少代码量您必须编写代码才能访问具有卓越性能、规范化和关系特征的更复杂、更专业的数据结构。
    【解决方案4】:

    以下内容对我有用:

    SELECT * FROM mytable t WHERE 
        t.key = "key" AND t.value = "value" OR
        t.key = "key" AND t.value = "value" OR
        ....
        t.key = "key" AND t.value = "value"
    GROUP BY t.id having count(*)=3;
    

    count(*)=3 必须匹配数量

    t.key = "key" AND t.value = "value"

    案例

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-15
      • 2012-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-17
      • 2011-10-19
      • 2010-12-21
      相关资源
      最近更新 更多