如何在SQL中实现过滤系统?

时间:2010-05-04 04:29:38

标签: sql mysql

现在我打算在我的网站上添加一个过滤系统。

示例:

(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)

所以现在我有兴趣做以下事情: SELECT ID FROM thisTable WHERE COLOR ='yellow'AND TASTE ='SWEET'

但我的问题是我在我的网站中为多个类别执行此操作,并且列不一致。 (如果表是用于手机,那么它将是品牌,3G-ENABLED,价格,颜色,波长等)

我怎样才能设计一个允许这个的通用模式?

现在我正在计划:

table(ID, KEY, VALUE)

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

有人可以为此推荐一个好的解决方案吗?请注意,列数将定期更改

4 个答案:

答案 0 :(得分:7)

您建议的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或其他键值商店。

答案 1 :(得分:1)

以下对我有用:

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”

答案 2 :(得分:0)

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

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

为了将数据转换为结果查询中的列,您需要创建通常称为交叉表查询的内容。有报告引擎会这样做,你可以做代码,但大多数数据库产品不会本地执行(意味着没有手动构建SQL字符串)。如果您有大量数据,那么当然性能不会很好,您将遇到过滤数据的问题。例如,假设某些值应该是数字。因为EAV的值部分可能是一个字符串,所以在对它们进行过滤之前,必须将这些值转换为整数,并假设数据可以转换为整数。

答案 3 :(得分:0)

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

否则,您可能希望寻找一个“反向ORM”来映射来自您的数据库,这样可以降低成本并提高性能。 (与ORM相比,起始成本略高,但长期性能和可靠性更高。)

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