4

编写 SQL 查询以在表中搜索具有多列的术语的最佳和优化方法是什么?

例如,我有一张产品表:

id | title   | color_id
-------------------
 1 | Dress   | 1 (red)
 2 | T-shirt | 3 (blue)
 3 | Pants   | 2 (green)
 4 | Socks   | 1 (red)
 5 | Dress   | 2 (green)
 6 | Shoes   | 2 (green)
 7 | Pants   | 3 (blue)

还有一张颜色表:

id | color
----------
 1 | Red
 2 | Green
 3 | Blue

如果用户输入术语Red Dress,结果他必须看到Productid 1,如果用户只输入Red,结果他必须看到Productsid14

更新:可能有一些输入,例如dress redred blue

真实版本的表格更复杂,但我试图用最简单的方式来解释。

4

3 回答 3

2

一个“有效”的简单解决方案可能是:

SELECT *
FROM products
JOIN colors ON colors.id = products.color_id
WHERE 
    ( title = "red" AND color = "dress" ) OR
    ( color = "red" AND title = "dress" )

如果优化器足够愚蠢以至于自己没有注意到它,这种情况可能会表现得更好:

WHERE 
    ( title = "red" OR title = "dress") AND
    ( color = "red" OR color = "dress")

如果您为问题添加更多属性(除了“标题”和“颜色”),并且如果存储不是问题,您可能希望将所有文本属性合并(和复制)到单个VARCHAR列中,并运行全文在这个栏目上搜索。

CREATE TABLE products_properties (
    product_id INT NOT NULL PRIMARY KEY,
    properties VARCHAR (255),
    FOREIGN KEY fk_product (product_id) REFERENCES products(id),
    FULLTEXT ftx_properties (properties)
);

搜索变得非常简单:

SELECT products.*, colors.*
FROM products
JOIN colors ON colors.id = products.color_id
JOIN products_properties AS pp ON pp.product_id = products.id
WHERE MATCH(properties) AGAINST ("+red +dress")

这在这个特定示例中显然没有意义,但是属性越多,查询速度就越快。products_properties这种非规范化也以增加维护表的复杂性为代价。

现在问题变得非常棘手,如果你

  • 必须处理标准化程度较低的输入,例如“红蓝”
  • 有属于这两个类别的单词(例如,如果“red”也是有效的布料标题)并且仍然想要区分它们(例如,像“red blue”这样的查询返回名为“red”的布料,颜色为“blue”)

但看起来这超出了您的问题范围。

于 2013-06-22T22:55:38.290 回答
1

如果我在 SQL 中执行此操作,我通常会在执行查询之前将查询分解为单个单词,然后根据单词的数量动态构建查询。

因此,对于您的示例,查询可能最终如下所示:

SELECT * 
FROM products p
JOIN colors c ON c.id = p.color_id
WHERE 
p.title LIKE '%red%' OR
c.color LIKE '%red%' OR
p.title LIKE '%dress%' OR
c.color LIKE '%dress%'

但是,如果您有很多桌子,这可能会开始变得非常复杂。它也不是很有效,因为它不太可能使用任何索引。

更好的解决方案是使用像 Lucene 这样的专用文本索引产品(但这完全是另一个问题......)

于 2013-06-20T19:54:03.043 回答
1

我的想法是使用这样的查询:

set @search = 'red dress';

SELECT *
FROM
  products INNER JOIN colors
  ON products.color_id = colors.id
WHERE
  (FIND_IN_SET(title, REPLACE(@search, ' ', ','))>0)+
  (FIND_IN_SET(color, REPLACE(@search, ' ', ','))>0) =
  LENGTH(@search)-LENGTH(REPLACE(@search, ' ', ''))+1;

FIND_IN_SET 中的两个替换用于制作以逗号分隔的属性列表:

red,dress

然后我正在检查是否title在这个集合中。如果存在,则值为:

(FIND_IN_SET(title, REPLACE(@search, ' ', ','))>0)

将被评估为 1,否则为 0。对于color.

搜索字符串中的属性数量可以计算为:

LENGTH(@search)-LENGTH(REPLACE(@search, ' ', ''))+1

(是的,这是一个肮脏的把戏!)。如果匹配的属性数量与@search 字符串中的属性相同,则返回该行。请注意,性能会很差。

小提琴在这里

于 2013-06-23T21:41:13.837 回答