MySQL 复杂的子查询公式

MySQL complex subquery formulation

我有两个 tables - booksimagesbooksidnamereleasedatepurchasecount 等列。 imagesbookid(和书里的id一样,基本上一本书可以有多个图,虽然我没有设置外键约束),bucketid ,poster(每条记录指向某个桶中的一个图像文件,对于某个bookid)。

Table 架构:

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

我的查询是,给定一个名字,从 table 本书中找到与该名字匹配的前十本书(按 purchasecount 的数量排序),对于那本书,return 来自 images table.

的任何(最好是第一条)记录(bucketidposter

显然这可以通过 运行 的两个查询来解决,第一个,然后使用它的结果来查询图像 table,但是那会很慢,所以我想使用 '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;

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

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

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

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

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

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

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

修改后的问题变成了 return 每本书 1 行。从头开始,即使没有图像,每本书也总是一行。 return 的图像信息将是获得最多赞成票的图像信息。

书籍table

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);

派生的可视化Table

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

最终查询

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 子句,因为给定的样本数据没有有用的书名可供搜索。那部分是微不足道的。哦,对大宽度主键的架构做些事情。但目前这是题外话。