单表SELF JOIN替代/除/相交

时间:2018-03-14 23:51:56

标签: mysql sql database

我目前正在处理一个查询,该查询根据表的属性从表中搜索书籍。该表包含超过5000万行,具有以下结构:

-----------------------
| book_id | attr_id   |
-----------------------
| 2005207 | 35021     |
-----------------------
| 2005207 | 28106     |
-----------------------
| 2005207 | 27173     |
-----------------------
| 2005207 | 35109     |
-----------------------
| 2005207 | 34999     |
-----------------------
| 2005207 | 35107     |
-----------------------
| 2005207 | 35099     |
-----------------------
| 2005207 | 35105     |
-----------------------
| 2005207 | 28224     |
-----------------------
| ...     | .....     |    
-----------------------

属性列表示属性,例如绑定,发布年份,流派等等。 主键是复合键attr_id,book_id

一个示例查询可以是"查找所有书籍,其中流派是漫画或科幻小说,没有精装和#34;。

SELECT sql_no_cache a.book_id
FROM
  (SELECT book_id
   FROM attribute_books ab
   WHERE ab.attr_id IN (38571,
                        38576)) a
LEFT JOIN
  (SELECT book_id
   FROM attribute_books ab
   WHERE ab.attr_id = 35003) b ON b.book_id = a.book_id
AND b.book_id IS NULL;

这些类型的查询可以多次自我连接,目前性能非常差。我还可以使用intersect命令,而不是IN语句的内连接和NOT IN语句的左连接,这可以在某些SQL风格中使用。

我目前有以下问题:

  1. 这是类似查询的最有效查询吗?如果没有,有什么建议可以加快速度吗?
  2. 我应该切换到完全不同类型的数据库/引擎,例如更高效(更快)的查询吗?

2 个答案:

答案 0 :(得分:1)

一种方法使用条件聚合:

SELECT book_id
FROM attribute_books
GROUP BY book_id
HAVING
    SUM(CASE WHEN attr_id IN (38571, 38576) THEN 1 ELSE 0 END) > 1 AND
    SUM(CASE WHEN attr_id = 35003 THEN 1 ELSE 0 END) = 0;

第一个HAVING子句检查该类型是喜剧还是科幻小说,第二个HAVING子句检查该书不是精装本。您可以通过添加或删除其他键值对来扩展此查询。

答案 1 :(得分:1)

最有效的方法可能是existsnot exists

select b.*
from books b
where not exists (select 1
                  from attribute_books ab
                  where ab.attr_id in (38571, 38576) and b.book_id = ab.book_id
                 ) and
      exists (select 1
              from attribute_books ab
              where ab.attr_id = 35003 and b.book_id = ab.book_id
             )

为此,您需要attribute_books(book_id, attr_id)上的索引。