如何减去基于不同状态的两列合一 table 的聚合计数?

How do I subtract aggregated counts based on different states two columns in one table?

使用 MariaDB,我试图获取每个月创建的项目减去当月删除的项目的月度总数。如果没有项目被删除,总数应该只是当月创建的项目数。如果删除的项目多于创建的项目,则总数应为负数。

table 有一个永远不会为空的 created_at 列和一个 deleted_at 列,该列在项目被 'deleted'

后设置

为了说明,(简化的)模式是这样的: TABLE 项:

+----------------------------------------------------------------------------+
|   idItem  | created_at                     | deleted_at                    |
+----------------------------------------------------------------------------+
|  1        |  2020-03-20T04:28:41.000+00:00 | 2021-07-27T02:36:05.000+00:00 |
|  2        |  2020-03-20T04:28:41.000+00:00 | 2021-07-27T02:36:05.000+00:00 |
|  3        |  2021-03-02T21:39:10.000+00:00 |  ∅                            |
|  4        |  2021-03-05T21:13:13.000+00:00 |  ∅                            |
|  5        |  2021-06-08T13:49:11.000+00:00 | 2021-07-27T02:36:05.000+00:00 |
|  6        |  2021-07-13T02:36:05.000+00:00 |  ∅
|  7        |  2021-09-17T21:12:13.000+00:00 |  ∅                            |
+----------------------------------------------------------------------------+

我需要的信息是没有被删除的月总计,像这样:

+-----------------------------------+
|   total_existing  | during_month  |     
+-----------------------------------+
|    2              | 2020-03       | -- two were added
+-----------------------------------+
|    4              | 2021-03       | -- another two were created
+-----------------------------------+
|    5              | 2021-06       | -- another was added
+-----------------------------------+
|    3              | 2021-07       | -- three deleted, one added
+-----------------------------------+
|    4              | 2021-09       | -- one added
+-----------------------------------+

最终,我需要显示每个月的总数。 我已经试过了,但这是不对的。

SELECT
  count(created.idItem) AS monthly_created_count,
  count(deleted.idItem) AS monthly_deleted_count,
  count(created.idItem) - count(deleted.idItem) as total,
  DATE_FORMAT(created.created_at, '%Y-%m') as created_month ,
  DATE_FORMAT(deleted.deleted_at, '%Y-%m') as deleted_month 
FROM 
    Item created 
  LEFT JOIN 
    Item deleted 
  ON 
  DATE_FORMAT(deleted.deleted_at, '%Y-%m') = DATE_FORMAT(created.created_at, '%Y-%m')
GROUP BY DATE_FORMAT(created.created_at, '%Y-%m'), DATE_FORMAT(deleted.deleted_at, '%Y-%m')

我一直认为我很接近,但是当我们查看设置了 deleted_at 日期的行时,很明显我错了。

如果您要查找累计行数 created/deleted,一种方法是计算 month/year 分别 创建和删除的记录数.然后将计数与 UNION ALL 结合在一起并计算总和:

SELECT t.YearMonth
      , SUM(t.TotalCreated) - SUM(t.TotalDeleted) AS TotalExisting
FROM  (
         SELECT DATE_FORMAT(created_at, '%Y-%m') AS YearMonth
                , COUNT(*) AS TotalCreated
                , 0 AS TotalDeleted
         FROM   Item
         GROUP BY DATE_FORMAT(created_at, '%Y-%m')
         
         UNION ALL

         SELECT DATE_FORMAT(deleted_at, '%Y-%m') AS YearMonth
                , 0 AS TotalCreated 
                , COUNT(*) AS TotalDeleted
         FROM   Item
         WHERE  deleted_at IS NOT NULL
         GROUP BY DATE_FORMAT(deleted_at, '%Y-%m')
) t         
GROUP BY t.YearMonth
ORDER BY t.YearMonth

结果:

 YearMonth | TotalExisting
 :-------- | ------------:
 2020-03   |             2
 2021-03   |             2
 2021-06   |             1
 2021-07   |            -2
 2021-09   |             1

然后将这些语句包装在 CTE 中并使用 Window Function 计算滚动总数:

另见 db<>fiddle

WITH cte AS (        
    SELECT t.YearMonth
           , SUM(t.TotalCreated) - SUM(t.TotalDeleted) AS TotalExisting
    FROM  (
             SELECT DATE_FORMAT(created_at, '%Y-%m') AS YearMonth
                   , COUNT(*) AS TotalCreated
                   , 0 AS TotalDeleted
             FROM   Item
             GROUP BY DATE_FORMAT(created_at, '%Y-%m')
             UNION ALL
             SELECT DATE_FORMAT(deleted_at, '%Y-%m') AS YearMonth
                    , 0 AS TotalCreated 
                    , COUNT(*) AS TotalDeleted
             FROM   Item
             WHERE  deleted_at IS NOT NULL
             GROUP BY DATE_FORMAT(deleted_at, '%Y-%m')
     ) t         
     GROUP BY t.YearMonth
     ORDER BY t.YearMonth
)
SELECT YearMonth, SUM(TotalExisting) OVER (ORDER BY YearMonth) AS TotalExisting
FROM   cte;

最终结果:

YearMonth | TotalExisting
:-------- | ------------:
2020-03   |             2
2021-03   |             4
2021-06   |             5
2021-07   |             3
2021-09   |             4