0

我有一个存储产品版本(product_versions)的表。这些产品版本可以有一组状态(active, draft, archived),我需要每天(历史上)获取这些状态的聚合结果集,以便生成图表。

虽然我知道我可以使用应用程序代码 (PHP) 或数据库触发器来构建和维护聚合表,但我希望在查询中解决这个问题(部分是因为我希望它优雅,部分是因为我很好奇怎么做)。如果我能做到这一点,我就可以缓存结果或类似的东西。

到目前为止,我能够获得这样的单一状态的结果:

SELECT
    pv.created_at_date,
    (
        SELECT CONCATcount(*)
        FROM `product_versions` p
        JOIN (
            SELECT product_id, MAX(id) AS latest_version
            FROM product_versions
            GROUP BY product_id
        ) grouped_versions ON p.product_id = grouped_versions.product_id AND p.id = grouped_versions.latest_version
        WHERE created_at_date = pv.created_at_date
        AND status = 'draft'
        ORDER BY id ASC
    ) as draft_status_count_subquery,
FROM product_versions pv
WHERE created_at_date >= date_sub(now(), interval 7 day)
GROUP BY pv.created_at_date
ORDER BY pv.created_at_date desc

结果:

+-----------------+-----------------------------+
| created_at_date | draft_status_count_subquery |
+-----------------+-----------------------------+
| 2013-09-09      |                           0 |
| 2013-09-06      |                          26 |
| 2013-09-05      |                          40 |
| 2013-09-04      |                          46 |
+-----------------+-----------------------------+

需要注意的是,我需要能够确定每天最新版本的状态(使用 grouped_versions 子查询)才能返回正确的聚合。

我所有其他加入其他状态的尝试都没有奏效(除了明显的是为每个我不愿意做的状态添加额外的子查询(status_count_subquery 1 .. n),因为性能已经很慢了1 个子查询)。

因此,我问外面的世界,有没有更好的方法来实现这个结果(或类似的)?

测试数据

以下是一些用于重新创建场景的示例数据:

CREATE TABLE `product_versions` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `product_id` int(10) unsigned DEFAULT NULL,
  `created_at_date` date DEFAULT NULL,
  `title` varchar(100) DEFAULT NULL,
  `status` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

INSERT INTO `product_versions` (`id`, `product_id`, `created_at_date`, `title`, `status`)
VALUES
    (1, 1, '2013-09-06', 'Foo Product', 'draft'),
    (2, 1, '2013-09-06', 'Foo Product', 'active'),
    (3, 2, '2013-09-06', 'Bar Product', 'draft'),
    (4, 1, '2013-09-07', 'Foo Product', 'archived'),
    (5, 2, '2013-09-07', 'Bar Product', 'active'),
    (6, 3, '2013-09-07', 'Baz Product', 'draft'),
    (7, 4, '2013-09-07', 'Fiz Product', 'draft');

希望有这样的输出

+-----------------+-------+--------+----------+
| created_at_date | draft | active | archived |
+-----------------+-------+--------+----------+
| 2013-09-07      |     2 |      1 |        1 |
| 2013-09-06      |     1 |      1 |        0 |
+-----------------+-------+--------+----------+
4

1 回答 1

1

感谢@Goat CO 的建议,成功了(耶!):

SELECT
    p.created_at_date,
    SUM(status = 'draft') as draft,
    SUM(status = 'active') as active,
    SUM(status = 'archived') as archived
FROM `product_versions` p
JOIN (
    SELECT product_id, MAX(id) AS latest_version
    FROM product_versions
    GROUP BY created_at_date, product_id
) grouped_versions ON p.product_id = grouped_versions.product_id AND p.id = grouped_versions.latest_version
GROUP BY created_at_date
ORDER BY created_at_date DESC

结果

+-----------------+-------+--------+----------+
| created_at_date | draft | active | archived |
+-----------------+-------+--------+----------+
| 2013-09-07      |     2 |      1 |        1 |
| 2013-09-06      |     1 |      1 |        0 |
+-----------------+-------+--------+----------+

让我知道是否有其他值得研究的解决方案,或者该解决方案是否有错误。

于 2013-09-11T09:46:28.183 回答