MySQL复杂子查询公式

Posted

技术标签:

【中文标题】MySQL复杂子查询公式【英文标题】:MySQL complex subquery formulation 【发布时间】:2016-06-08 22:09:24 【问题描述】:

我有两张桌子 - booksimagesbooks 具有 idnamereleasedatepurchasecount 等列。 imagesbookid(和书本里的id一样,基本上一本书可以有多张图片,虽然我没有设置外键约束),bucketidposter(每条记录点到某个桶中的一个图像文件,对于某个bookid)。

表架构:

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

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

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

【问题讨论】:

海报是独一无二的吗?什么是海报? 图像文件名。所有图像都位于同一个存储桶中,因此名称必须是唯一的。 即使世界上最好的意愿,文件名也可以更改。这不是一个好的主键 文件名由第 3 方 API 生成,并保证是唯一的。另外,在插入存储桶之前,会进行额外的检查以查看是否存在该名称的此类文件,在这种情况下生成另一个名称(并继续),直到生成唯一的名称,然后将其转储到存储桶中。我们可能还需要通过图像名称进行查询(例如该图像有多少“喜欢”),因此是主键,而不是为其创建另一个唯一的 id,因为它无论如何都保证是唯一的。 我们喜欢在文本和示例数据中使用模式的问题。一个 sqlfiddle 很棒。否则我们经常逃跑。当你已经拥有它时,你是在要求别人把它敲出来。 【参考方案1】:

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

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

修改后的问题变成了每本书返回 1 行。从头开始,即使没有图像,也总是每本书 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 行(如果有的话)。

最终查询

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

【讨论】:

天哪,这一定是我见过的最复杂的查询。它会比首先查询 books 表以从 books 表中获取匹配记录,然后让客户端在单独的查询中为返回的每本书获取最受好评的海报要快得多吗? 这个查询就像是 2 或 3,从 1 到 10。好吧,也许是 3 或 4。这里的人比我聪明得多。 哦,好吧,那对我来说是艰难的时期.. :)

以上是关于MySQL复杂子查询公式的主要内容,如果未能解决你的问题,请参考以下文章

MySQL:来自子查询的总和

TP框架 mysql子查询

mysql 必知必会整理—子查询与连接表[八]

带有子查询的 CTE 查询在小型索引表上很慢;如何在 MySQL 上进行优化?

3.复杂查询

子选择或连接?有没有更好的方法来编写这个 mysql 查询?