MySQL实现基于多个过滤器的搜索

Posted

技术标签:

【中文标题】MySQL实现基于多个过滤器的搜索【英文标题】:MySQL implement search based on multiple filters 【发布时间】:2011-09-06 17:06:59 【问题描述】:

从下面的表结构中,我希望能够提供基于属性组合的搜索过滤器:

表格:动物属性

id      attributeId     animalId
1       455             55
2       999             55
3       685             55
4       999             89
5       455             89
6       333             93
7       685             93
8       999             93
--------------------------------

前端会有复选框,例如

Animal options

Colour
[ ] Black      (id 685)
[x] Brown      (id 999)

Body Covering
[ ] Fur        (id 233)
[ ] Scales     (id 333)
[x] Feathers   (id 455)

我希望上面的复选框能够选择所有棕色并且有羽毛的动物。我可以通过以下查询获取此数据:

SELECT animalId
FROM animalAttributes
WHERE attributeId IN (999,455)
GROUP BY animalId 
HAVING COUNT(DISTINCT attributeId) = 2;

我遇到的问题是当从多个过滤器中选择多个选项时,例如

Animal options

Colour
[x] Black      (id 685)
[x] Brown      (id 999)

Body Covering
[x] Fur        (id 233)
[ ] Scales     (id 333)
[x] Feathers   (id 455)

我希望上面的复选框能够选择所有(黑色OR棕色)并且有(毛皮OR羽毛)的动物.我可以通过以下查询获取此数据:

SELECT animalId
FROM animalAttributes
WHERE
attributeId IN (685,233) ||
attributeId IN (685,455) ||
attributeId IN (999,233) ||
attributeId IN (999,455)
GROUP BY animalId 
HAVING COUNT(DISTINCT attributeId) = 2;

如果我想添加额外的过滤器,例如“有尾巴”、“可以飞”、“血型”等,我是否认为我需要计算所有组合 (cartesian product) 并按照和上面的模式一样吗?例如5 个过滤器,每个过滤器选择 1 个或多个选项

attributeId IN (x,x,x,x,x) ||
attributeId IN (x,x,x,x,x) ||
attributeId IN (x,x,x,x,x) ||
...
HAVING COUNT(DISTINCT attributeId) = 5;

其他参考表格

表格:属性

attributeId    attributeCategoryId    attribute

233            1                      Fur
333            1                      Scales
455            1                      Feathers
685            2                      Black
999            2                      Brown
-----------------------------------------------

表格:attributeCategories

attributeCategoryId    category
1                      Body covering
2                      Colour
------------------------------------

【问题讨论】:

拥有一些测试数据和预期的查询输出真的很有帮助,这样我们就可以尝试各种查询,直到得到正确的答案。 【参考方案1】:
attributeId IN (685,233) ||
attributeId IN (685,455) ||
attributeId IN (999,233) ||
attributeId IN (999,455)

将与您编写时相同: 属性 ID IN (685,233,455,999,233)

试试:

SELECT aa.animalId
FROM animalAttributes aa
LEFT JOIN attributes a ON a.attributeId = aa.attributeId
WHERE
(aa.attributeId IN (685,99) AND a.attributeCategoryId=1) AND
(aa.attributeId IN (223,455) AND a.attributeCAtegoryId=2)
GROUP BY animalId 

或者说: (黑色 OR 棕色 AND isColor)AND(毛皮 OR 羽毛 AND isBodyCovering)

Untestet,但你应该明白。

【讨论】:

但是你如何更普遍地做到这一点?如果您添加了另一个类别怎么办 - 那么您将不得不重写您的查询。必须有一个查询允许在不更改查询的情况下添加新的属性数据 如果你用新房间扩建你的房子,你将不得不在你的旧房子里盖一扇门。 a.attributeCategoryId 不可能同时是 12 实际上,现在看来您几乎成功了。您可能应该将“主要”AND(即WHERE (…) **AND** (…))更改为OR 并添加HAVING COUNT(DISTINCT a.attributeCategoryId) = 2【参考方案2】:

您也可以对交叉路口执行此操作。您可以编写一个查询,为每个过滤器生成一个结果集。然后,您可以将所有结果集相交以得出适合所有过滤器的单个结果集。

这种方法的缺点是您要对数据库执行多个查询。如果由于您的应用程序有大量流量而导致性能成为问题,您可以执行一些内存密集型但不是进程密集型的操作,例如缓存您正在查询的数据库表,以便您只调用一次数据库。

【讨论】:

【参考方案3】:

如果我正确理解您的问题,按位键可能会有所帮助。例如,假设每种动物都可以有多个二元选项。例如 has tail = Yes 或 No(0 或 1),Has Scales、Has Fir 等也一样。

然后通过将所有可能的属性添加为二进制选项集,您就可以拥有这个

尾(Y/N) 冷杉(Y/N) 鳞片(Y/N) 棕色(Y/N) 0 1 0 1

即这种动物没有尾巴,有冷杉,没有鳞片,而且是棕色的。

此键可以轻松过滤与该条件匹配的所有项目,并且无需对查询进行额外的代码更改即可添加新条件。查看位运算符以使用这些键。

【讨论】:

以上是关于MySQL实现基于多个过滤器的搜索的主要内容,如果未能解决你的问题,请参考以下文章

在 PHP/MYSQL 中计算过滤器记录 [重复]

在多表列中具有多个术语的简单 PHP 过滤器

PHP MySQL & AJAX 搜索过滤器时间延迟

[MySQL] 用通配符进行过滤

Android - 为 RecyclerView 实现搜索过滤器

Vue JS按多个数组对象项过滤