SQL:选择属于排除类别的记录,这些记录仅属于排除类别

时间:2009-02-12 06:49:36

标签: sql mysql optimization

我有一个可用的SELECT语句,并且在我的表上运行得足够快(对于50k +产品,3k +类别,<0.01秒)。但在我看来,它不是很优雅,并希望听到任何有关改善它的建议。

有3个感兴趣的表:

  • 产品 - 关键产品ID
  • 类别 - 关键类别ID
  • products_tree - 链接表(类别包含许多产品,产品可以属于多个类别)

我有一个排除的类别ID列表[例如1040,1050,1168] 我想选择属于这些排除类别之一的所有产品ID 仅当产品不属于另一个不排除的类别时

我的查询如下所示:

SELECT DISTINCT productID 
FROM products_tree 
WHERE 
  categoryID IN (1040,1050,1168) 
  AND productID NOT IN
    ( SELECT DISTINCT productID 
      FROM products_tree 
      WHERE 
      categoryID NOT IN (1040,1050,1168)
    );

3 个答案:

答案 0 :(得分:1)

我可以想到一些方法,每种方法根据索引和特定的数据库实现执行不同的方法。一些可能看起来很慢的东西可以用你可能没有想到的方式进行优化,因此值得试试它们并比较执行计划以查看发生的事情......

注1:我使用GROUP BY而不是DISTINCT,这是因为它允许omptimiser使用索引。我已经看到实现可以将DISTINCT转换为GROUP BY,但是确保使用GROUP BY非常值得。它也让你思考索引,这从来都不是坏事。

注意2:像这样的一些查询需要一段时间才能进行优化,因为优化器有很多选项可供评估。因此,通常值得编译存储过程中的所有不同选项并比较这些存储过程的执行。这可以确保您实际比较查询时间而不是不同的编译时间。

SELECT
   [tree].productID
FROM
   products_tree AS [tree]
WHERE
   [tree].productID IN (1040,1050,1168)
   AND NOT EXISTS (SELECT * FROM products_tree WHERE productID = [tree].productID AND categoryID NOT IN (1040,1050,1168)) 
GROUP BY
   [tree].productID


SELECT
   [tree].productID
FROM
   products_tree AS [tree]
LEFT OUTER JOIN
   (
      SELECT
         productID
      FROM
         product_tree
      WHERE
         productID NOT IN (1040,1050,1168)
      GROUP BY
         productID
    )
    AS [ok_products]
       ON [ok_products].productID = [tree].productID
WHERE
   [tree].productID IN (1040,1050,1168)
   AND [ok_products].productID IS NULL 
GROUP BY
   [tree].productID


SELECT
   [tree].productID
FROM
   products_tree AS [tree]
GROUP BY
   [tree].productID
HAVING
       MAX(CASE WHEN [tree].productID     IN (1040,1050,1168) THEN 1 ELSE 0 END) = 1
   AND MAX(CASE WHEN [tree].productID NOT IN (1040,1050,1168) THEN 1 ELSE 0 END) = 0

还有其他的,各自的变化,但这应该给你一个很好的开始。但我真的会强调使用GROUP BY和对INDEXES的考虑:)

答案 1 :(得分:0)

我相信您的查询非常好,但您可以将其与联接进行比较:

SELECT DISTINCT pt1.productID 
FROM products_tree pt1
LEFT JOIN products_tree pt2 ON pt2.productID = pt1.productID 
    AND pt2.categoryID  pt1.categoryID

WHERE pt1.categoryID IN (1040,1050,1168) 
  AND pt2.productID IS NULL

不确定我是否正确,但我认为你了解我的做法。然而,如果你想要,我会直接选择productinfo,然后连接会更有意义(内连接你想要的类别,左连接你不想要的那些并检查为空)

答案 2 :(得分:0)

您可以尝试“NOT EXISTS”变体:

SELECT 
  pt.productID 
FROM 
  products_tree pt
WHERE 
  pt.categoryID IN (1040,1050,1168)
  AND NOT EXISTS (
    SELECT 1 
    FROM products_tree 
    WHERE productID = pt.productID AND categoryID NOT IN (1040,1050,1168)
  )
GROUP BY
  pt.productID;