按另一列值分组和计数

Posted

技术标签:

【中文标题】按另一列值分组和计数【英文标题】:Group and count by another columns value 【发布时间】:2018-09-11 12:06:36 【问题描述】:

我有一张如下表:

CREATE TABLE public.test_table
(
    "ID" serial PRIMARY KEY NOT NULL,
    "CID" integer NOT NULL,
    "SEG" integer NOT NULL,
    "DDN" character varying(3) NOT NULL
)

数据看起来像这样:

ID  CID SEG DDN
1   1   1   "711"
2   1   2   "800"
3   1   3   "124"
4   2   1   "711"
5   3   1   "711"
6   3   2   "802"
7   4   1   "799"
8   5   1   "799"
9   5   2   "804"
10  6   1   "799"

我需要按 CID 列对这些数据进行分组,并获取列计数取决于 DDN 列的第一个值,但计数必须给我两个不同的信息,如果它大于 1。

如果不能解释清楚,我真的很抱歉。让我告诉你我需要什么..

DDN END TRA
711 1   2
799 2   1

如您所见,DDN:711 有 1 条单次计数记录 (ID:4)。这是 END 列。 但是 2 次有多个 SEG 计数(ID:1to3 和 ID:5to6)。这是 TRA 列。

我无法确定 group 子句中应该包含哪一列!

我的解决方案:

刚刚找到如下解决方案

WITH x AS (
    SELECT
        (SELECT t1."DDN" FROM public.test_table AS t1
         WHERE t1."CID"=t."CID" AND t1."SEG"=1) AS ddn,
        COUNT("CID") AS seg_count       
    FROM public.test_table AS t
    GROUP BY "CID"
)

SELECT ddn, COUNT(seg_count) AS "TOTAL",    
    SUM(CASE WHEN x.seg_count=1 THEN 1 ELSE 0 END) as "END",
    SUM(CASE WHEN x.seg_count>1 THEN 1 ELSE 0 END) as "TRA"
FROM x
GROUP BY ddn;

【问题讨论】:

你必须给我们一个更好的解释你想要什么。我不明白您在示例中如何计算 END 和 TRA。 再次抱歉!如果您查看前 3 条记录,CID 值是相同的,并且有 3 条记录意味着我应该算作 TRA(TRAnsferred)。现在让我们看一下第 4 条记录,它的 CID 值为 2,它的单个记录,这意味着我应该将其算作 END(ENDed)。 一点解释; ID:1to3 具有相同的 CID,第一个 DDN 是 711。我应该将这个 TRA 计算为 711。ID:4 有单个 CID,第一个 DDN 是 711。我应该将这个 END 计算为 711。ID:5to6 具有相同的 CID,第一个 DDN 是711. 我应该把这个 TRA 算作 711。ID:7 有单个 CID,第一个 DDN 是 799。我应该把这个 END 算作 799。ID:8to9 有相同的 CID,第一个 DDN 是 799。我应该把这个 TRA 算作 799。 ID:10 有单个 CID,第一个 DDN 是 799。我应该把这个 END 算作 799。 请不要将其他信息发布为 cmets edit 您的问题。 我看到您找到了自己的解决方案。我还建议你一个作为答案。 【参考方案1】:

等效的、更快的查询:

SELECT "DDN"
     , COUNT(*) AS "TOTAL"
     , COUNT(*) FILTER (WHERE seg_count = 1) AS "END"
     , COUNT(*) FILTER (WHERE seg_count > 1) AS "TRA"
FROM  (
   SELECT DISTINCT ON ("CID")
         "DDN"          -- assuming min "SEG" is always 1
       , COUNT(*) OVER (PARTITION BY "CID") AS seg_count
   FROM   test_table
   ORDER  BY "CID", "SEG"
   ) sub
GROUP  BY "DDN";

db小提琴here

注意事项

CTE 通常速度较慢,只应在 Postgres 需要的地方使用。

这相当于问题中的查询假设每个“CID”的最小“SEG”总是1 - 因为这个查询返回具有最小值的行“SEG”,而您的查询返回带有"SEG" = 1 的那个。通常,您需要“第一个”段,而我的查询更可靠地实现了此要求,但问题并不清楚。

COUNT(*)COUNT(column) 稍快,但不涉及 NULL 值(适用于此处)。相关:

PostgreSQL: running count of rows for a query 'by minute'

关于DISTINCT ON

Select first row in each GROUP BY group?

聚合 FILTER 语法需要 Postgres 9.4+:

Conditional SQL count

【讨论】:

@James:我又增加了一项改进。【参考方案2】:

这是我提出的解决方案,我猜查询可以简化。

CREATE TABLE test_table
(
    ID serial PRIMARY KEY NOT NULL,
    CID integer NOT NULL,
    SEG integer NOT NULL,
    DDN character varying(3) NOT NULL
);



insert into test_table(CID,SEG,DDN)
values
(   1,   1,   '711'),
(   1,   2,   '800'),
(   1,   3,   '124'),
(   2,   1,   '711'),
(   3,   1,   '711'),
(   3,   2,   '802'),
(   4,   1,   '799'),
(   5,   1,   '799'),
(   5,   2,   '804'),
(   6,   1,   '799');


with summary as (with ddn_t as (select cid,ddn,row_number() OVER( PARTITION BY cid)from test_table)
select a.cid,count(distinct a.ddn),b.ddn
 from ddn_t a 
 join ddn_t b on b.cid=a.cid and b.row_number=1
group by a.cid, b.ddn)
select ddn,
sum (case when count >1 then 1 else 0 end) as TRA,
sum (case when count = 1 then 1 else 0 end) as END
from summary
group by ddn;

【讨论】:

以上是关于按另一列值分组和计数的主要内容,如果未能解决你的问题,请参考以下文章

MySQL - 选择列值仅为0的行,按另一列分组?

R,按另一列中的值缩放直方图计数

如何在 MYSQL 中选择具有 MAX(列值)的行,按另一列进行分区?

按列分组,结果限制并按另一列轨道排序

SQL Server:按分组列求和并按另一列排序

pandas/matplotlib 中一列按另一列分组的箱形图