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

虽然我知道我可以使用应用程序代码(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 |
+-----------------+-------+--------+----------+

最佳答案

由于@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 |
+-----------------+-------+--------+----------+


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

10-04 22:16
查看更多