一个字段搜索多个值

时间:2013-06-20 19:22:43

标签: mysql search

编写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

如果用户输入红色连衣裙这个词,他必须看到身份Product的{​​{1}},如果用户输入的话只是红色,因此他必须看到1,其身份为Products1

更新:可能还有一些输入,例如4dress red

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

3 个答案:

答案 0 :(得分: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")

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

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") 表的复杂性。

如果你

,现在问题变得非常毛茸茸
  • 必须处理较少规范化的输入,例如“red blue”
  • 具有属于这两个类别的单词(例如,如果“红色”也是有效的布料标题)并且仍然想要区分它们(例如,像“红色蓝色”的查询返回带有颜色的名为“红色”的布料“蓝色“)

但看起来这不属于你问题的范围。

答案 1 :(得分: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这样的专用文本索引产品(但这是一个完整的问题......)

答案 2 :(得分: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字符串中的属性相同,则返回该行。请注意,表演会很差。

小提琴是here