0

我实际上已经设法让这个工作,但想知道是否有任何 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 倍。我对我的索引很满意,我只是对 Have 子句没有经验。

更新

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
4

2 回答 2

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                                                              

方法 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

方法 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
于 2012-07-19T22:45:40.143 回答
1

这是查询的几个版本

  1. 这是更好还是更糟?(~30ms)
    按 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. 让我们再扭曲一下,
    total = 2 and bad = 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
    
于 2012-07-19T22:54:40.937 回答