【问题标题】:SQL query to find products matching a set of categoriesSQL查询以查找与一组类别匹配的产品
【发布时间】:2012-06-24 20:17:25
【问题描述】:

我有 3 个表:产品、类别和 pro_cat_link。一个产品可以通过 pro_cat_link 表链接到一个或多个类别。

我的查询必须回答以下问题:查找与一组类别匹配的所有产品。例如:查找所有“黄色、水果和甜”的产品。

在 SO 中研究这个问题时,我只能找到我目前使用的解决方案: Complicated SQL Query--finding items matching multiple different foreign keys

就我而言,我的查询如下所示:

SELECT products.id, COUNT(DISTINCT categories.id) as countCat
FROM products
INNER JOIN pro_cat_link ON (pro_cat_link.product_id = products.id)
WHERE pro_cat_link.category_id IN (3,6,8,10)
GROUP BY product.id
ORDER BY product.date DESC
HAVING countCat = 4

换句话说,选择与类别 ID (3,6,8,10) 之一匹配的所有产品,并仅保留恰好匹配 4 个类别的产品。

这很好用,但我遇到了性能问题,因为 COUNT()、GROUP BY、ORDER BY 使得正确的索引非常有限。谁能想到更好的方法来解决这个问题?

【问题讨论】:

  • 我假设只是为每个类别进行连接需要太长时间?
  • @Jodaka 是的。管理人员要求用户可以选择任意数量的类别;)

标签: mysql performance indexing


【解决方案1】:

如果您将这些信息存储在某处,则可以消除分组和计数的性能问题。您可以在 Products 中添加一个名为 total_categories 的列,它会告诉您该产品参与了多少个类别。然后您可以直接说 where total_categories = 4。如果产品经常更改其类别,这可能更难以维护,因为您必须不断正确更新此字段 - 然后您必须决定是否要在应用程序代码或触发器或存储过程中执行此操作...

通常我认为将此类元数据直接存储在表中并不是一个好主意,但如果性能真的那么糟糕,则可能值得考虑。

【讨论】:

  • +1 摆脱GROUP BY 绝对是必经之路。我试试看。
【解决方案2】:

如果您没有太多类别,您可以使用一个位串来表示它所在的类别(即位置 i 处的 1 表示产品在类别 i 中,而不是跟踪列数, 0 表示不在该类别中)。然后,在搜索一组类别时,您会为该搜索生成一个位串,并生成AND 具有此字符串的所有类别字符串。正确类别中的将生成搜索字符串作为答案。

例如,假设您有十个类别。 Item1 在类别1, 3, 5, 6, 8, 10 中,因此其类别字符串为1010110101。 Item2 在类别1, 2, 4, 6, 8, 10 中,因此其类别字符串为1010101011。搜索 3、6、8 和 10 时,您将生成字符串 s = 1010100100Item1 & s = 1010100100 = sItem2 & s = 1010100000 <> s.

此外,您不必将其存储为字符串,您可以将其存储为实际的以 10 为底的等效值。所以 Item1、Item2 和 s 分别是 693、683 和 676。 693 & 676 = 676,但683 & 676 = 672。然后,如果您将产品添加到类别 i,只需将其类别编号更新 2^(i - 1),如果您要从类别 i 中删除,只需减去 2^(i - 1)。

当然,如果 MySQL int 中的类别多于位,这根本行不通。此外,正如 FrustratedWithFormsDes 在他的回答中指出的那样,这会引发更新 pro_cat_link 和此表的所有问题(当然,取决于 pro_cat_link 的用途,这可能会完全消除它)。此外,如果类别更改了数字,您必须更新所有内容。

【讨论】:

    猜你喜欢
    • 2012-08-06
    • 1970-01-01
    • 2015-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多