5

由于关系数据库的性质,我认为这样的查询会很容易,但它似乎让我很合适。我也四处搜寻,但没有发现任何真正有帮助的东西。情况如下:

假设我对产品和产品标签有一个简单的关系。这是一对多的关系,所以我们可以有以下内容:

productid  |  tag
========================
1          |  Car
1          |  Black
1          |  Ford
2          |  Car
2          |  Red
2          |  Ford
3          |  Car
3          |  Black
3          |  Lexus
4          |  Motorcycle
4          |  Black
5          |  Skateboard
5          |  Black
6          |  Skateboard
6          |  Green

查询 all的最有效(Ford OR Black OR Skateboard) AND NOT (Motorcycles OR Green)方法是什么?我需要做的另一个查询是 all (Car) or (Skateboard) or (Green AND Motorcycle) or (Red AND Motorcycle)

products 表中有大约 150k 条记录,tags 表中有 600k 条记录,因此查询需要尽可能高效。这是我一直在搞乱的一个查询(示例#1),但它似乎需要大约 4 秒左右。任何帮助将非常感激。

SELECT p.productid
FROM   products p
       JOIN producttags tag1 USING (productid)
WHERE  p.active = 1
       AND tag1.tag IN ( 'Ford', 'Black', 'Skatebaord' )
       AND p.productid NOT IN (SELECT productid
                               FROM   producttags
                               WHERE  tag IN ( 'Motorcycle', 'Green' ));

 

更新

到目前为止我发现的最快的查询是这样的。它需要 100-200 毫秒,但它看起来非常不灵活和丑陋。基本上我会抓住所有匹配Ford,Black或的产品Skateboard。他们我将这些匹配产品的所有标签连接到一个冒号分隔的字符串中,并删除所有与:Green:AND匹配的产品:Motorcycle:。有什么想法吗?

SELECT p.productid,
       Concat(':', Group_concat(alltags.tag SEPARATOR ':'), ':') AS taglist
FROM   products p
       JOIN producttags tag1 USING (productid)
       JOIN producttags alltags USING (productid)
WHERE  p.active = 1
       AND tag1.tag IN ( 'Ford', 'Black', 'Skateboard' )
GROUP  BY tag1.productid
HAVING ( taglist NOT LIKE '%:Motorcycle:%'
         AND taglist NOT LIKE '%:Green:%' ); 
4

5 回答 5

3

我会编写没有子查询的排除连接:

SELECT p.productid
FROM   products p
INNER JOIN producttags AS t ON p.productid = t.productid
LEFT OUTER JOIN producttags AS x ON p.productid = x.productid 
       AND x.tag IN ('Motorcycle', 'Green')
WHERE  p.active = 1
       AND t.tag IN ( 'Ford', 'Black', 'Skateboard' )
       AND x.productid IS NULL;

确保您按该顺序在两列(活动,productid)上具有产品索引。

您还应该按该顺序在两列(productid、tag)上的 producttags 上建立索引。

我需要做的另一个查询是 all (Car) or (Skateboard) or (Green AND Motorcycle) or (Red AND Motorcycle)。

有时这些复杂的条件对于 MySQL 优化器来说是困难的。一种常见的解决方法是使用 UNION 来组合更简单的查询:

SELECT p.productid
FROM   products p
INNER JOIN producttags AS t1 ON p.productid = t1.productid
WHERE  p.active = 1
   AND t1.tag IN ('Car', 'Skateboard')

UNION ALL

SELECT p.productid
FROM   products p
INNER JOIN producttags AS t1 ON p.productid = t1.productid
INNER JOIN producttags AS t2 ON p.productid = t2.productid 
WHERE  p.active = 1
   AND t1.tag IN ('Motorcycle')
   AND t2.tag IN ('Green', 'Red');

PS:您的标记表不是实体-属性-值表。

于 2012-10-29T22:06:17.617 回答
2

我将获取所有唯一 ID 匹配项和要过滤掉的唯一 ID,然后 LEFT JOIN 那些列表(根据tigeryan)并过滤掉任何匹配的 ID。通过将所有查询分开,查询也应该更易于阅读和修改。它也应该相当快,尽管它可能看起来不像。

SELECT * FROM products p
WHERE 
p.active=1 AND
productid IN (
SELECT matches.productid FROM (
  SELECT DISTINCT productid FROM producttags 
  WHERE tag IN ('Ford','Green','Skatebaord')
) AS matches
LEFT JOIN (
  SELECT DISTINCT productid FROM producttags 
  WHERE tag IN ('Motorcycles','Green')
) AS filter ON filter.productid=matches.productid
WHERE filter.productid IS NULL
)

有时 JOIN 比 IN 快,这取决于 mysql 如何优化查询:

SELECT p.* FROM (
SELECT matches.productid FROM (
  SELECT DISTINCT productid FROM producttags 
  WHERE tag IN ('Ford','Green','Skatebaord')
) AS matches
LEFT JOIN (
  SELECT DISTINCT productid FROM producttags 
  WHERE tag IN ('Motorcycles','Green')
) AS filter ON filter.productid=matches.productid
WHERE filter.productid IS NULL
) AS idfilter
    JOIN products p ON p.productid=idfilter.productid AND p.active=1

第二个查询应该强制连接顺序,因为必须首先完成内部选择。

于 2012-10-26T02:11:54.547 回答
0

我通常会通过尝试消除来自...的记录来解决这个问题

select p.productid 
from product p 
left join producttags tag1 
    on p.productid = tag1.productid and tag1.tag NOT IN ('Motorcycles','Green')
where tag1.tag IN ('Ford','Black','Skateboard') and p.active = 1
于 2012-10-24T02:49:43.577 回答
0

这个如何:

SELECT DISTINCT p.id FROM products AS p
JOIN producttags AS included ON (
    included.productid = p.id
    AND included.tag IN ('Ford', 'Black', 'Skatebaord') 
)
WHERE active = 1
AND p.id NOT IN (
    SELECT DISTINCT productid FROM producttags
    WHERE tag IN ('Motorcycle', 'Green')
)
于 2012-10-29T21:57:39.630 回答
0

CONCAT/LIKE 解决方案的替代方案:

SELECT p.productid
FROM products p
JOIN producttags USING (productid)
WHERE p.active = 1
AND tag IN ('Ford', 'Black', 'Skateboard')
GROUP BY p.productid
HAVING SUM(IF(tag IN ('Motorcycle','Green'), 1, 0)) = 0;
于 2021-03-23T16:15:07.903 回答