在 1 个查询中更新多行及其所有父行的多对多计数器缓存
考虑一个博客应用程序,其中包含帖子、类别表和将帖子与一个或多个类别链接的查找表。类别是有层次的。帖子可以分配给任何类别,而不仅仅是叶节点。
类别表有一个 post_count
字段,用于缓存分配给特定类别的帖子数量。它还具有 MPTT 的 parent_id
、lft
和 rght
列。
但它还有一个 under_post_count
字段,用于缓存分配给它或其任何子类别的不同帖子的数量。这很有用,因此您可以显示类别的分层列表,并在其旁边显示分配给该类别或其子级之一的帖子数量。
我的应用程序已经达到这样的程度:在创建带有类别的帖子、编辑其类别或删除具有类别的帖子后,我有一个新旧类别的类别 ID 列表,其 post_count 字段需要更新。我希望接下来可以做的是在一个查询中,更新所有已识别类别及其所有父类别的
under_post_count
字段,以及分配给每个类别的不同帖子的数量或它的任何一个孩子。
以下是创建表和类别的一些测试数据所需的 SQL:
CREATE TABLE `categories` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) DEFAULT NULL,
`lft` int(11) DEFAULT NULL,
`rght` int(11) DEFAULT NULL,
`name` varchar(255) NOT NULL,
`post_count` int(11) NOT NULL DEFAULT '0',
`under_post_count` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM;
CREATE TABLE `categories_posts` (
`category_id` int(11) NOT NULL,
`post_id` int(11) NOT NULL,
PRIMARY KEY (`category_id`,`post_id`)
) ENGINE=MyISAM;
INSERT INTO `categories` (`id`, `parent_id`, `lft`, `rght`, `name`) VALUES
(1, NULL, 1, 8, 'Cat 1'),
(4, 1, 2, 3, 'Cat 1.1'),
(5, 1, 4, 5, 'Cat 1.2'),
(6, 1, 6, 7, 'Cat 1.3'),
(2, NULL, 9, 16, 'Cat 2'),
(7, 2, 10, 11, 'Cat 2.1'),
(8, 2, 12, 13, 'Cat 2.2'),
(9, 2, 14, 15, 'Cat 2.3'),
(3, NULL, 17, 24, 'Cat 3'),
(10, 3, 18, 19, 'Cat 3.1'),
(11, 3, 20, 21, 'Cat 3.2'),
(12, 3, 22, 23, 'Cat 3.3');
运行几次,为 categories_posts
表创建一些测试数据:
INSERT IGNORE INTO `categories_posts` (`category_id`, `post_id`)
SELECT `id`, CEILING(10 * RAND()) FROM `categories` ORDER BY RAND() LIMIT 6
任何人都可以弄清楚这一点,非常感谢您的帮助?
Consider a blog application which has tables for posts, categories and a lookup table linking a post with one or more categories. Categories are hierarchical. Posts can be assigned to any category, not just leaf nodes.
The categories table has a post_count
field which caches the number of posts assigned to the specific category. It also has parent_id
, lft
and rght
columns for MPTT.
But it also has a under_post_count
field which caches the number of distinct posts assigned to it or any of it's child categories. This is useful so you can display a hierarchical list of categories with the number of posts assigned to it, or one of its children, next to it.
My application has got to the point where after a post is created with categories, or it's categories are edited or one which had categories is deleted, I have a list of category IDs of the old and new categories, whose post_count
field needs updating. What I was hoping I could do next is in a single query, update the under_post_count
fields for all those categories identified, and all their parents, with the number of distinct posts assigned to each category or any of it's children.
Here's the SQL required to create the tables and some test data for categories:
CREATE TABLE `categories` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) DEFAULT NULL,
`lft` int(11) DEFAULT NULL,
`rght` int(11) DEFAULT NULL,
`name` varchar(255) NOT NULL,
`post_count` int(11) NOT NULL DEFAULT '0',
`under_post_count` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM;
CREATE TABLE `categories_posts` (
`category_id` int(11) NOT NULL,
`post_id` int(11) NOT NULL,
PRIMARY KEY (`category_id`,`post_id`)
) ENGINE=MyISAM;
INSERT INTO `categories` (`id`, `parent_id`, `lft`, `rght`, `name`) VALUES
(1, NULL, 1, 8, 'Cat 1'),
(4, 1, 2, 3, 'Cat 1.1'),
(5, 1, 4, 5, 'Cat 1.2'),
(6, 1, 6, 7, 'Cat 1.3'),
(2, NULL, 9, 16, 'Cat 2'),
(7, 2, 10, 11, 'Cat 2.1'),
(8, 2, 12, 13, 'Cat 2.2'),
(9, 2, 14, 15, 'Cat 2.3'),
(3, NULL, 17, 24, 'Cat 3'),
(10, 3, 18, 19, 'Cat 3.1'),
(11, 3, 20, 21, 'Cat 3.2'),
(12, 3, 22, 23, 'Cat 3.3');
Run this a few times to create some test data for the categories_posts
table:
INSERT IGNORE INTO `categories_posts` (`category_id`, `post_id`)
SELECT `id`, CEILING(10 * RAND()) FROM `categories` ORDER BY RAND() LIMIT 6
Can anyone figure this out, your help would be much appreciated?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
好吧,这里有几种给猫剥皮的方法(假设是 5.1 和触发器)
您可以从应用程序层更新所有内容
您可以触发对
post_count 的更新
从categories_posts
并触发更新(级联)从categories
到
under_post_count
最后,您可以触发来自
的所有更新>categories_posts
另外,根据类别的实际数量,您可能不需要将
under_post_count
反规范化为获取它应该相当容易且便宜 获取精确匹配的实际计数
结合两者给出包括层次结构的计数
编辑
从上面构造更新语句应该不那么困难
应该适用
post_count
under_post_count
的情况有所不同,因为 mysql 不喜欢听到在 where 部分提到目标表,因此你必须做一些像这样的怪物编辑2
实际上,上述所有查询都存在错误 - 每当我加入类别和帖子时,我应该加入
cc.id = cp.category_id
而不是cp.post_id
,然后我没有检查。不想纠正...但仅限于最后一个查询EDIT3
只需注意几点:
under_post_count
和post_count
无论数据的状态如何,post_count = post_count +/- 1
(类似于under_post_count
),well there are a few ways to skin a cat here (assuming 5.1 and triggers)
you can update everything from application layer
you can trigger updates to
post_count
fromcategories_posts
and trigger updates (cascade) tounder_post_count
fromcategories
finally, you can trigger all updates from
categories_posts
Also depending on the actual number of categories, you might not need to denormalize
under_post_count
as it should be rather easy and inexpensive to fetch it withFetching the actual counts on exact match is
Combining the two gives the counts including the hierarchies
EDIT
Constructing update statements from the above should not be so hard
should work for the
post_count
Situation for the
under_post_count
is different since mysql does not like to hear that target table is mentioned in the where part, therefore you have to do some monstrosity like thisEDIT2
Actually there is an error in all of the above queries - whenever I joined categories and posts I should have joined on
cc.id = cp.category_id
and notcp.post_id
, which then I didn't check. Don't feel like correcting... but only in this last queryEDIT3
Just few notes:
under_post_count
andpost_count
regardless of the state of the data,post_count = post_count +/- 1
on appropriate records in statuses (similary forunder_post_count
),