SQLite:如何在使用 CASE 语句时避免使用两个 CTE?

SQLite: How to avoid using two CTE when using CASE statement?

我有一个 table trx 具有以下架构:

| id   | p_id |
| 1    | 1    |
| 2    | 1    |
| 3    | 4    |
...
| 1000 | 2    |

其中 id 是交易 ID,p_id 是交易者的 ID。

我需要查询 trx 所以我得到一个 table 允许我绘制交易频率的直方图,这意味着我想知道有多少 p_id只有 1 笔交易,有多少人做了 2 笔交易,依此类推。我必须在 +10 bin 中汇总 11 笔或更多笔交易:

| n_trx | bin_size |
| 1     | 10       |
| 2     | 18       |
| 3     | 7        |
...
| +10   | 26       |

我知道 +10 需要 CASE 语句,我使用两个 CTE:

来完成任务
WITH new_trx_history
     AS (WITH trx_history
              AS (SELECT p_id,
                         Count(DISTINCT id) AS n_trx
                  FROM   trx
                  GROUP  BY p_id)
         SELECT CASE
                  WHEN n_trx < 11 THEN n_trx
                  ELSE '+10'
                END      AS n_trx,
                Count(*) AS bin_size
          FROM   trx_history
          GROUP  BY n_trx)
SELECT n_trx,
       Sum(bin_size)
FROM   new_trx_history
GROUP  BY n_trx; 

我想知道是否有一种比我实际的(working)查询更直接的方法

这里是SQLFiddle

您可以在 GROUP BY

中使用 CASE
     WITH trx_history
          AS (SELECT p_id,
                     Count(DISTINCT id) AS n_trx
              FROM   trx
              GROUP  BY p_id)
     SELECT CASE
              WHEN n_trx < 11 THEN n_trx
              ELSE '+10'
            END      AS n_trx,
            Count(*) AS bin_size
      FROM   trx_history
      GROUP  BY CASE
              WHEN n_trx < 11 THEN n_trx
              ELSE '+10'
            END
      ;

如果你使用 COUNT() window 函数,你可以在没有任何 CTE 的情况下做到这一点:

SELECT DISTINCT 
       CASE WHEN COUNT(*) >= 10 THEN '+10' ELSE COUNT(*) END AS n_trx,
       COUNT(*) OVER (PARTITION BY CASE WHEN COUNT(*) >= 10 THEN '+10' ELSE COUNT(*) END) AS bin_size
FROM trx
GROUP BY p_id 

参见demo
结果:

n_trx | bin_size
----- | --------
    3 |        1
    6 |        1
  +10 |        3

我建议这样写查询:

WITH p as (
     SELECT p_id,
            Count(*) AS n_trx
     FROM trx
     GROUP BY p_id
    )
SELECT (CASE WHEN n_trx < 11 THEN n_trx
             ELSE '+10'
        END) AS trx_grp,
       Count(*) AS bin_size
FROM p
GROUP BY trx_grp
ORDER BY MIN(n_trx);

备注:

  1. 您似乎不需要 COUNT(DISTINCT) CTE。 id 在事务中看起来是独一无二的 table 并且 COUNT(DISTINCT) 会产生额外的开销。
  2. 您可以在 SQLite 中通过列别名进行聚合。但是,如果它与 FROM 子句中的任何列具有不同的名称会更好。
  3. 您可以使用聚合功能轻松订购。
  4. SQLite 允许您在 CASE 表达式中混合类型。这是由于它对类型的神秘处理。任何其他 SQL 方言都会将 '+10' 转换为数字 10.