我有3张桌子。图像,类别,image_category。
image: id | title | imageURL
categories: cat_id | cat_name
image_category: image_id | cat_id
我当前查询以从最新到最旧的顺序选择所有图像:
SELECT image.id as ID, image.title as title, categories.cat_name as CAT
FROM image_category
LEFT JOIN image
ON image_category.image_id = image.id
INNER JOIN categories
ON image_category.cat_id = categories.cat_id
ORDER BY ID DESC
我想展示每个类别最新的4张图片。最大的image.id是最新的图像。
例如。如果我在每个类别中有3个类别和40个图像。我想展示每个类别的最新4张图片。我稍后会尝试在此之后显示每个类别的下一个4,然后是每个类别的下一个4,直到没有图像为止。
This解决方案似乎正在寻找。
SELECT i1.*
FROM item i1
LEFT OUTER JOIN item i2
ON (i1.category_id = i2.category_id AND i1.item_id < i2.item_id)
GROUP BY i1.item_id
HAVING COUNT(*) < 4
ORDER BY category_id, date_listed;
但我有一个连接我的image_id和category_id的关系表。无法弄清楚如何使用额外的表连接来实现它。
非常感谢SQL大师的帮助。
答案 0 :(得分:2)
你几乎就在那里,你只需要使用你的item_category
表进行分组,因为这就是cat_id的位置。
SELECT ...
FROM item_category AS c1
LEFT OUTER JOIN item_category AS c2
ON c1.cat_id = c2.cat_id AND c1.image_id < c2.image_id
GROUP BY c1.cat_id
HAVING COUNT(*) < 4
然后,一旦你有了这个,你知道c1
包含每个类别的前四个图像。然后,您可以将c1
加入image
表以获取其他属性:
SELECT i.id, i.title, c.cat_name AS CAT
FROM item_category AS c1
LEFT OUTER JOIN item_category AS c2
ON c1.cat_id = c2.cat_id AND c1.image_id < c2.image_id
INNER JOIN image AS on c1.image_id = i.id
INNER JOIN categories AS c on c1.cat_id = c.id
GROUP BY c1.image_id
HAVING COUNT(*) < 4;
虽然由于single-value rule这不是严格合法的SQL,但MySQL会允许它。
复制自评论主题:
我会获取完整的结果,将其存储在缓存中,然后使用应用程序代码迭代它。这将更简单,性能更好。 SQL功能强大,但另一种解决方案可能更容易开发,调试和维护。
您当然可以使用LIMIT
来遍历结果集:
SELECT i.id, i.title, c.cat_name AS CAT
FROM item_category AS c1
LEFT OUTER JOIN item_category AS c2
ON c1.cat_id = c2.cat_id AND c1.image_id < c2.image_id
INNER JOIN image AS on c1.image_id = i.id
INNER JOIN categories AS c on c1.cat_id = c.id
GROUP BY c1.image_id
HAVING COUNT(*) < 4
ORDER BY c.cat_id
LIMIT 4 OFFSET 16;
但请记住,执行OFFSET意味着每次查看另一组时都必须再次运行查询。 MySQL中有一些优化,一旦它找到足够的行就会退出查询,但如果你经常迭代它并且远远进入一系列页面,它仍然很昂贵。
您可以使用两种可能的优化:一种是缓存结果的部分,理论上很少有用户希望在大型分页结果的每个页面中前进。例如,fetch足以填充十页的结果,并缓存它。它大大减少了查询次数,也许用户只有1%的时间会进入下一组十页。
SELECT i.id, i.title, c.cat_name AS CAT
FROM item_category AS c1
LEFT OUTER JOIN item_category AS c2
ON c1.cat_id = c2.cat_id AND c1.image_id < c2.image_id
INNER JOIN image AS on c1.image_id = i.id
INNER JOIN categories AS c on c1.cat_id = c.id
GROUP BY c1.image_id
HAVING COUNT(*) < 4
ORDER BY c.cat_id
LIMIT 40 OFFSET 40; /* second set of ten pages */
如果您可以假设页面N
的任何视图来自页面N-1
的视图,则另一个优化是针对请求根据中查看的最大类别ID过滤类别N-1
st 页面。您需要这样做,因为OFFSET在结果集中按行号工作,但索引偏移量按在这些行上找到的值工作。如果可能存在间隙或未使用的cat_id值,则这些偏移量不同。
SELECT i.id, i.title, c.cat_name AS CAT
FROM item_category AS c1
LEFT OUTER JOIN item_category AS c2
ON c1.cat_id = c2.cat_id AND c1.image_id < c2.image_id
INNER JOIN image AS on c1.image_id = i.id
INNER JOIN categories AS c on c1.cat_id = c.id
WHERE c1.cat_id > 47 /* this value is the largest seen in previous page */
GROUP BY c1.image_id
HAVING COUNT(*) < 4
ORDER BY c.cat_id
LIMIT 40; /* no offset needed */
重新评论:
...使用LIMIT和OFFSET只会修剪这些结果而不会将我移到行列表中。
LIMIT
按预期工作;它适用于 GROUP BY
和HAVING
完成工作后生成的行。
我在每个类别查询的最大N之前的方式是由 1.拉入x个图像,
2.记住哪个是最后一张图像,然后是 3.在我的后续查询中使用子查询来获取下一个x数量的图像,其中id小于最后一个图像。这样的事情可能是每组最大的N吗?
这就是我的WHERE
子句在上一个示例中的作用,而不使用子查询。而且我假设您正在前进到下一个更高的cat_id集合。只有当您一次推进一个页面并且正向前进时,此解决方案才有效。
好吧,还有另一种适用于MySQL的最大n组的解决方案,但它依赖于用户变量功能。 SQLite没有此功能。
SELECT * FROM (
SELECT
p.id as image_ID, p.imageURL as URL, c.cat_name as CAT, ic.cat_id,
IF(@cat=ic.cat_id, @row:=@row+1, @row:=1) AS _row, @cat:=ic.cat_id AS _cat
FROM (SELECT @cat:=null, @row:=0) AS _init
CROSS JOIN image_category AS ic
INNER JOIN portfolio AS p ON ic.image_id = p.id
INNER JOIN categories AS c on ic.cat_id = c.cat_id
ORDER BY ic.cat_id, ic.image_id
) AS x
WHERE _row BETWEEN 4 AND 6; /* or choose any range you want */
这类似于使用标准SQL和大多数RDBMS支持的ROW_NUMBER() OVER (PARTITION BY cat_id)
,但SQLite还不支持。
答案 1 :(得分:0)
SELECT *
FROM (
SELECT a.id as ID,a.title as title,b.cat_name as CAT, row_number() OVER (PARTITION BY b.cat_id ORDER BY b.cat_id,a.id desc) AS n
from images a, categories b, image_category c
where a.id = c.image_id
and b.cat_id = c.cat_id
) x
WHERE n < 4
ORDER BY b.cat_id,a.id desc;