mySQL有排除优化

时间:2012-07-19 20:48:57

标签: mysql query-optimization having

我实际上已经设法让这个工作,但是想知道是否有任何Guru可能能够提供更优化的方法:

SELECT `cfc`.`card_face_id`
FROM `card_face_color` AS `cfc`
INNER JOIN `color` AS `c` ON c.color_id = cfc.color_id
WHERE c.c_url IN ('black', 'blue')
AND card_face_id NOT IN (
    SELECT `cfc`.`card_face_id`
    FROM `card_face_color` AS `cfc`
    INNER JOIN `color` AS `c` ON cfc.color_id = c.color_id
    WHERE c.c_url NOT IN ('black', 'blue')
)
GROUP BY `cfc`.`card_face_id`
HAVING (COUNT(DISTINCT c.c_url) = 2)

基本上我正在尝试选择包含黑色和蓝色的所有card_faces,但没有其他颜色(每个card_face可能最多为5个)。尝试使用内连接来做它,但这慢了近25倍。我对我的索引感到非常满意,我只是熟悉了Having子句。

更新

针对查询运行EXPLAIN会显示此信息(对格式化道歉)。

id select_type table type  possible_keys key      key_len ref                   rows Extra
1  PRIMARY     c      index PRIMARY,c_url c_url    50      NULL                  10   Using where; Using index; Using temporary; Using filesort
1  PRIMARY     cfc    ref   color_id      color_id 1       site.co.uk.c.color_id 1156 Using where; Using index
2  SUBQUERY    c      range PRIMARY,c_url c_url    50      NULL                  9    Using where; Using index
2  SUBQUERY    cfc    ref   color_id      color_id 1       site.co.uk.c.color_id 1156 Using index

2 个答案:

答案 0 :(得分:1)

这是另一种方法。我首先尝试将结果集限制为只有2种颜色的卡片面。如果表现更好,请告诉我。

SELECT cfc2.card_face_id, COUNT(*) AS cardcount2
FROM card_face_color cfc2 
  INNER JOIN color c ON cfc2.color_id=c.color_id AND c.c_url IN ('black','blue')
  INNER JOIN (SELECT cfc.card_face_id, COUNT(*) AS cardcount
              FROM card_face_color cfc
              GROUP BY cfc.card_face_id
              HAVING cardcount=2) AS color_counter ON cfc2.card_face_id=color_counter.card_face_id
GROUP BY cfc2.card_face_id
HAVING cardcount2=2                                                              

APPROACH 2

另一种离墙式方法:

SELECT card_face_id, SUM(U.counter) AS counter FROM
(
SELECT cfc2.card_face_id, 1 AS counter
FROM card_face_color cfc2 
  INNER JOIN color c ON cfc2.color_id=c.color_id AND c.c_url IN ('black','blue')

UNION ALL

SELECT cfc2.card_face_id, 100 AS counter
FROM card_face_color cfc2 
  INNER JOIN color c ON cfc2.color_id=c.color_id AND NOT c.c_url IN ('black','blue')

) AS U
GROUP BY card_face_id
HAVING counter=2

APPROACH 3 我不确定你有多灵活,但在这种方法中我消除了内连接:

SET @blue_id = (SELECT color_id FROM color WHERE c_url='blue');
SET @black_id = (SELECT color_id FROM color WHERE c_url='black');

SELECT card_face_id, SUM(U.counter) AS counter FROM
(
SELECT cfc2.card_face_id, 1 AS counter
FROM card_face_color cfc2 
WHERE cfc2.color_id IN (@blue_id,@black_id)

UNION ALL

SELECT cfc2.card_face_id, 100 AS counter
FROM card_face_color cfc2 
WHERE cfc2.color_id NOT IN (@blue_id,@black_id)

) AS U
GROUP BY card_face_id
HAVING counter=2

答案 1 :(得分:1)

以下是查询的几个版本

  1. 这是好还是坏? (〜30毫秒)
    按group_concat过滤

    SELECT `cfc`.`card_face_id`
    FROM `card_face_color` AS `cfc`
    INNER JOIN `color` AS `c` ON c.color_id = cfc.color_id
    GROUP BY `cfc`.`card_face_id`
    HAVING GROUP_CONCAT(c.c_url ORDER BY c.c_url) = 'black,blue'
    
  2. 这可能更好地使用指数(~35ms)
    好= 2,坏= 0

    SELECT `cfc`.`card_face_id`
    FROM `card_face_color` AS `cfc`
    INNER JOIN `color` AS `c` ON c.color_id = cfc.color_id
    GROUP BY `cfc`.`card_face_id`
    HAVING
      SUM(IF(c.c_url IN ('black','blue'), 1, NULL)) = 2 AND
      SUM(IF(c.c_url IN ('black','blue'), NULL, 1)) = 0
    
  3. 作为第二个游戏,我这次再次尝试使用DISTINCT(约34ms)
    总计= 2且良好= 2

    SELECT `cfc`.`card_face_id`
    FROM `card_face_color` AS `cfc`
    INNER JOIN `color` AS `c` ON c.color_id = cfc.color_id
    GROUP BY `cfc`.`card_face_id`
    HAVING
      COUNT(DISTINCT c.c_url) = 2 AND
      COUNT(DISTINCT IF(c.c_url IN ('black','blue'), c.c_url, NULL)) = 2
    
  4. 让我们再扭一点,
    总计= 2且坏= 0

    SELECT `cfc`.`card_face_id`
    FROM `card_face_color` AS `cfc`
    INNER JOIN `color` AS `c` ON c.color_id = cfc.color_id
    GROUP BY `cfc`.`card_face_id`
    HAVING
      COUNT(DISTINCT c.c_url) = 2 AND
      COUNT(IF(c.c_url IN ('black','blue'), NULL, c.c_url)) = 0
    
  5. 只是为了测试所有的合并,
    好= 2,坏= 0

    SELECT `cfc`.`card_face_id`
    FROM `card_face_color` AS `cfc`
    INNER JOIN `color` AS `c` ON c.color_id = cfc.color_id
    GROUP BY `cfc`.`card_face_id`
    HAVING
      COUNT(DISTINCT IF(c.c_url IN ('black','blue'), c.c_url, NULL)) = 2 AND
      COUNT(IF(c.c_url IN ('black','blue'), NULL, c.c_url)) = 0