MySQL 复杂的子查询公式
MySQL complex subquery formulation
我有两个 tables - books
和 images
。 books
有 id
、name
、releasedate
、purchasecount
等列。 images
有bookid
(和书里的id
一样,基本上一本书可以有多个图,虽然我没有设置外键约束),bucketid
,poster
(每条记录指向某个桶中的一个图像文件,对于某个bookid
)。
Table 架构:
poster
在 images
中是唯一的,因此它是主键。
- 书籍封面索引:(
name
, id
, releasedate
)
- 图片覆盖索引:(
bookid
,poster
,bucketid
)
我的查询是,给定一个名字,从 table 本书中找到与该名字匹配的前十本书(按 purchasecount
的数量排序),对于那本书,return 来自 images
table.
的任何(最好是第一条)记录(bucketid
和 poster
)
显然这可以通过 运行 的两个查询来解决,第一个,然后使用它的结果来查询图像 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 两个结果 - fooe
和 fool
,以及每个结果一个(对应于每本书的多个海报中的任何一个)海报。但是我没有得到正确的结果。预期:
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
子句,因为给定的样本数据没有有用的书名可供搜索。那部分是微不足道的。哦,对大宽度主键的架构做些事情。但目前这是题外话。
我有两个 tables - books
和 images
。 books
有 id
、name
、releasedate
、purchasecount
等列。 images
有bookid
(和书里的id
一样,基本上一本书可以有多个图,虽然我没有设置外键约束),bucketid
,poster
(每条记录指向某个桶中的一个图像文件,对于某个bookid
)。
Table 架构:
poster
在images
中是唯一的,因此它是主键。- 书籍封面索引:(
name
,id
,releasedate
) - 图片覆盖索引:(
bookid
,poster
,bucketid
)
我的查询是,给定一个名字,从 table 本书中找到与该名字匹配的前十本书(按 purchasecount
的数量排序),对于那本书,return 来自 images
table.
bucketid
和 poster
)
显然这可以通过 运行 的两个查询来解决,第一个,然后使用它的结果来查询图像 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 两个结果 - fooe
和 fool
,以及每个结果一个(对应于每本书的多个海报中的任何一个)海报。但是我没有得到正确的结果。预期:
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
子句,因为给定的样本数据没有有用的书名可供搜索。那部分是微不足道的。哦,对大宽度主键的架构做些事情。但目前这是题外话。