Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/asp.net/34.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mysql 在一个查询中更新多行及其所有父行的多对多计数器缓存_Mysql_Many To Many - Fatal编程技术网

Mysql 在一个查询中更新多行及其所有父行的多对多计数器缓存

Mysql 在一个查询中更新多行及其所有父行的多对多计数器缓存,mysql,many-to-many,Mysql,Many To Many,考虑一个博客应用程序,它有一个帖子、类别表和一个链接帖子和一个或多个类别的查找表。类别是分层的。帖子可以分配给任何类别,而不仅仅是叶节点 类别表有一个post\u count字段,用于缓存分配给特定类别的帖子数量。它还有MPTT的parent\u id、lft和rght列 但它还有一个under_post_count字段,用于缓存分配给它或其任何子类别的不同帖子的数量。这很有用,因此您可以显示类别的分层列表,其中包含分配给它的帖子数量,或其子项之一,并在其旁边 我的应用程序已经达到了这样的程度:

考虑一个博客应用程序,它有一个帖子、类别表和一个链接帖子和一个或多个类别的查找表。类别是分层的。帖子可以分配给任何类别,而不仅仅是叶节点

类别表有一个
post\u count
字段,用于缓存分配给特定类别的帖子数量。它还有MPTT的
parent\u id
lft
rght

但它还有一个
under_post_count
字段,用于缓存分配给它或其任何子类别的不同帖子的数量。这很有用,因此您可以显示类别的分层列表,其中包含分配给它的帖子数量,或其子项之一,并在其旁边

我的应用程序已经达到了这样的程度:在使用类别创建帖子、编辑其类别或删除具有类别的帖子之后,我有一个新旧类别的类别ID列表,其
post\u 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\u posts
表创建一些测试数据:

INSERT IGNORE INTO `categories_posts` (`category_id`, `post_id`) 
SELECT `id`, CEILING(10 * RAND()) FROM `categories` ORDER BY RAND() LIMIT 6

任何人都能弄明白这一点,我们将非常感谢您的帮助。

这里有几种剥猫皮的方法(假设5.1和触发器)

  • 您可以从应用程序层更新所有内容

  • 您可以从
    categories\u posts
    触发对
    post\u count
    的更新,并从
    categories
    触发更新(级联)到
    下的

  • 最后,您可以从
    categories\u post

另外,根据类别的实际数量,您可能不需要在_post_count
下对
进行反规范化,因为获取它应该相当简单且便宜

SELECT c.id, SUM(cc.post_count) 
FROM categories c 
LEFT JOIN categories cc ON c.lft <= cc.lft AND c.rght >= cc.rght 
GROUP BY c.id;
将这两者结合起来,可以得到包括层次结构在内的计数

SELECT c.id, COUNT(*) 
FROM categories c 
LEFT JOIN categories cc ON c.lft <= cc.lft AND c.rght >= cc.rght 
LEFT JOIN categories_posts cp ON cc.id = cp.post_id
GROUP BY c.id;
应该适用于
post\u计数

由于mysql不喜欢在where部分中提到目标表,因此必须这样做

UPDATE categories LEFT JOIN 
       (SELECT c.id, COUNT(*) AS result 
        FROM categories c 
        LEFT JOIN categories cc ON c.lft <= cc.lft AND c.rght >= cc.rght 
        INNER JOIN categories_posts cp ON cc.id = cp.post_id
        GROUP BY c.id) AS x ON categories.id = x.id
SET under_post_count = x.result
EDIT3
请注意:

  • 无论数据的状态如何,上面的查询都将修复_post_count
post_count
下的

  • 如果数据访问层得到适当的抽象和保护,并且能够保证原子性,那么有些查询会更便宜——这些查询只会对状态中的适当记录执行
    post\u count=post\u count+/-1
    (类似于
    下的
  • 如果您无法从应用程序级别可靠地模拟触发器,那么检查是否需要运行上述查询可能会更便宜(尽管mysql在这方面很好,但如果您希望不依赖数据库),或者采取一些策略,通常只增加/减少计数器,只定期重新计算数字
    @Unreason,谢谢你的回复,很抱歉我没有在问题中详细说明,但是有没有一种不依赖触发器的方法可以做到这一点?也就是说,纯粹在SQL更新语句中?@neilcrookes,我用更新语句更新了问题。你应该能把他们连在一起。演出不会很精彩。我可以问一下为什么不触发吗?@Unreason,太棒了,非常感谢。最后一个问题(promise):我如何在posts表上添加范围条件,例如posts.status=‘published’?@neilcrokes,updated-有一个错误通过所有查询传播。顺便说一句,你没有回答-为什么不呢?通常情况下,数据库保持数据完整性比在应用程序级别保持数据完整性更好。@Unreason,太好了,非常感谢。没有触发器的原因是这个查询实际上是由我的应用程序中的ORM层实现的,它与dbms无关。此外,查询是在应用程序的一个组件中构建的,该组件可以应用于具有这种关系的任何数据。+1表示具有良好DDL和样本数据的定义良好的问题
    UPDATE categories 
    SET post_count = (SELECT COUNT(*) 
                      FROM categories_posts cp 
                      WHERE cp.post_id = categories.id)
    
    UPDATE categories LEFT JOIN 
           (SELECT c.id, COUNT(*) AS result 
            FROM categories c 
            LEFT JOIN categories cc ON c.lft <= cc.lft AND c.rght >= cc.rght 
            INNER JOIN categories_posts cp ON cc.id = cp.post_id
            GROUP BY c.id) AS x ON categories.id = x.id
    SET under_post_count = x.result
    
    UPDATE categories LEFT JOIN 
           (SELECT c.id, COUNT(*) AS result 
            FROM categories c 
            LEFT JOIN categories cc ON c.lft <= cc.lft AND c.rght >= cc.rght 
            INNER JOIN categories_posts cp ON cc.id = cp.category_id
            INNER JOIN posts p ON cp.post_id = p.id
            WHERE p.status = 'published'
            GROUP BY c.id) AS x ON categories.id = x.id
    SET under_post_count = x.result,
        post_count = (SELECT COUNT(*) 
                      FROM categories_posts cp 
                      WHERE cp.category_id = categories.id)