MySQL 复杂子查询公式

标签 mysql subquery query-optimization inner-join mysql-variables

我有两个表 - booksimagesbooks 包含 idnamereleasedatepurchasecount 等列。 imagesbookid(和books中的id一样,基本上一本书可以有多个图片。虽然国外我没设置过key constraint), bucketid, poster(每条记录指向某个桶中的一个图片文件,对应某个bookid)。

表架构:

  1. posterimages中是唯一的,因此是主键。
  2. 书籍封面索引:(name, id, releasedate)
  3. 图片覆盖索引:(bookid,poster,bucketid)

我的查询是,给定一个名称,从 books 表中找到名称与该名称匹配的前十本书(按 purchasecount 的数量排序),并为该书返回任何(最好是首先)从 images 表中记录(bucketidposter)。

显然,这可以通过运行第一个查询并使用其结果查询图像表来通过两个查询来解决,但这会很慢,所以我想使用“join”和子查询一次性完成。但是,我正在尝试的并没有给我正确的结果:

select books.id,books.name,year(releasedate),purchasecount,bucketid,poster from books 
inner join (select bucketid,bookid, poster from images) t on 
t.bookid  = books.id  where name like "%foo%" order by purchasecount desc limit 2;

任何人都可以建议一个最佳查询来根据需要在此处获取结果集(包括更改表架构以缩短搜索时间的任何建议)吗?

更新 fiddle :http://sqlfiddle.com/#!9/17c5a8/1

示例查询应返回两个结果 - fooefool,以及每个结果的一个(对应于每本书的多个海报中的任何一个)海报。但是我没有得到正确的结果。预期:

fooe - 1973 - 459 - 11 - swt(或 fooe - 1973 - 459 - 11 - pqr)

fool - 1963 - 456 - 12 - xxx(或 fool - 1963 - 456 - 111 - qwe)

最佳答案

我同意 Strawberry 关于架构的观点。我们可以讨论改进性能的想法等等。但这是我对如何在几次聊天和对问题进行更改后解决此问题的看法。

请注意下面的数据更改以处理各种边界条件,包括该表中没有图像的书籍和平局。决胜局意味着使用 max(upvotes)。 OP 多次更改了问题并在图像表中添加了一个新列。

修改后的问题变成每本书返回 1 行。从头开始,即使没有图像,每本书也总是一行。要返回的图像信息将是投票最多的图像信息。

图书表

create table books 
(   id int primary key, 
    name varchar(1000), 
    releasedate date, 
    purchasecount int
) ENGINE=InnoDB;

insert into books values(1,"fool","1963-12-18",456);
insert into books values(2,"foo","1933-12-18",11);
insert into books values(3,"fooherty","1943-12-18",77);
insert into books values(4,"eoo","1953-12-18",678);
insert into books values(5,"fooe","1973-12-18",459);
insert into books values(6,"qoo","1983-12-18",500);

原始问题的数据变化。

主要是新的 upvotes 列。

下面包括添加的平局行。

create table images 
(   bookid int, 
    poster varchar(150) primary key, 
    bucketid int, 
    upvotes int -- a new column introduced by OP
) ENGINE=InnoDB;

insert into images values (1,"xxx",12,27);
insert into images values (5,"pqr",11,0);
insert into images values (5,"swt",11,100);
insert into images values (2,"yyy",77,65);
insert into images values (1,"qwe",111,69);
insert into images values (1,"blah_blah_tie_break",111,69);
insert into images values (3,"qwqqe",14,81);
insert into images values (1,"qqawe",8,45);
insert into images values (2,"z",81,79);

派生表的可视化

这只是为了帮助可视化最终查询的内部部分。它演示了决胜局的陷阱,因此是 rownum 变量。每次 bookid 更改时,该变量都会重置为 1,否则它会递增。最后(我们的最终查询)我们只需要 rownum=1 行,以便每本书(如果有的话)最多返回 1 行。

enter image description here

最终查询

select b.id,b.purchasecount,xDerivedImages2.poster,xDerivedImages2.bucketid
from books b
left join
(   select i.bookid,i.poster,i.bucketid,i.upvotes,
    @rn := if(@lastbookid = i.bookid, @rn + 1, 1) as rownum,
    @lastbookid := i.bookid as dummy
    from 
    (   select bookid,max(upvotes) as maxup
        from images
        group by bookid
    ) xDerivedImages
    join images i
    on i.bookid=xDerivedImages.bookid and i.upvotes=xDerivedImages.maxup
    cross join (select @rn:=0,@lastbookid:=-1) params
    order by i.bookid
) xDerivedImages2
on xDerivedImages2.bookid=b.id and xDerivedImages2.rownum=1
order by b.purchasecount desc
limit 10

结果

+----+---------------+---------------------+----------+
| id | purchasecount | poster              | bucketid |
+----+---------------+---------------------+----------+
|  4 |           678 | NULL                |     NULL |
|  6 |           500 | NULL                |     NULL |
|  5 |           459 | swt                 |       11 |
|  1 |           456 | blah_blah_tie_break |      111 |
|  3 |            77 | qwqqe               |       14 |
|  2 |            11 | z                   |       81 |
+----+---------------+---------------------+----------+

cross join 的意义仅仅是为2个变量引入和设置起始值。就是这样。

结果是按 purchasecount 降序排列的前十本书以及来自 images 的信息(如果它存在)(否则为 NULL)最赞的形象。所选图像遵循决胜局规则,如上文可视化部分所述,使用 rownum 选择第一个图像。

最后的想法

我将其留给 OP 在末尾插入适当的 where 子句,因为给定的示例数据没有可供搜索的有用书名。那部分是微不足道的。哦,对大宽度主键的架构做些事情。但目前这是题外话。

关于MySQL 复杂子查询公式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37713790/

相关文章:

MySQL/MariaDB JSON_EXTRACT 和 JSON_CONTAINS

mysql - 如何从同一列中获取多个最大记录?

mysql - 集成sql select语句

sql - 优化 SQL 查询查找表中不存在的条目

针对游戏的mysql查询优化

php - MySQL 子查询优化 - where not in(子查询)

mysql - 如何避免包含空值或零值的列

MySQL:是否保证多次插入的顺序?

java.sql.BatchUpdateException : Field 'myFK' doesn't have a default value

mySQL 排除计数列(如果低于)