MySQL复杂的子查询公式

时间:2016-06-08 22:09:24

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

我有两张桌子 - booksimagesbooks包含idnamereleasedatepurchasecount等列。 imagesbookid(与书籍中的id相同,基本上一本书可以有多个图片。虽然我没有设置任何外键约束),bucketidposter(每个记录指向某个存储桶中的图像文件,某个bookid)。

表架构:

  1. posterimages中是唯一的,因此它是主键。
  2. 书籍上的覆盖索引:(nameidreleasedate
  3. 覆盖图像索引:(bookidposterbucketid
  4. 我的查询是,给出一个名称,从书名表中找到名称与该名称相匹配的前十本书(按purchasecount的数量排序),并为该书返回任何(最好是第一个)记录(bucketid表格中的posterimages)。

    显然,这可以通过运行第一个查询来解决,并使用其结果来查询图像表,但这样会很慢,所以我想使用'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;
    

    有人可以根据需要建议最佳查询来获取结果集(包括更改表模式以改善搜索时间的任何建议)吗?

    更新了小提琴: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

1 个答案:

答案 0 :(得分:1)

我同意草莓的架构。我们可以讨论提高性能的想法以及所有这些。但是,在我对这个问题进行一些聊天和更改之后,我将如何解决这个问题。

下面注意数据变化,以处理各种边界条件,包括该表中没有图像的书籍,以及打破中断。使用max(upvotes)进行打结。 OP改变了几次问题并在图像表中添加了一个新列。

修改后的排队成为每本书返回1行。划伤一下,即使没有图像,每本书也要一行。要返回的图像信息将是具有最大upvotes的图像信息。

书籍表

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子句,因为给出的样本数据没有可用的书名来搜索。那部分是微不足道的。哦,并为主键宽度大的架构做些什么。但目前这是偏离主题的。