【问题标题】:Count distinct over partition by计算不同的分区
【发布时间】:2021-05-26 15:09:08
【问题描述】:

我正在尝试对按角色划分的名称进行不同的计数。因此,在下面的示例中:我有一个包含姓名和人员角色的表格。

我想要一个角色计数列,该列提供该角色中不同人员的总数。例如,角色经理出现了四次,但该角色只有 3 个不同的人 - Sam 在不同的日期再次出现。

如果我删除日期列,它可以正常使用:

select
a.date,
a.Name,
a.Role,
count(a.Role) over (partition by a.Role) as Role_Count

from table a

group by a.date, a.name, a.role

包括日期列然后使其计算角色总数而不是按不同的名称(我知道我没有在分区中标识)。给 4 名经理和 3 名分析师。

我该如何解决这个问题?

期望的输出:

Date Name Role Role_Count
01/01 Sam Manager 3
02/01 Sam Manager 3
01/01 John Manager 3
01/01 Dan Manager 3
01/01 Bob Analyst 2
02/01 Bob Analyst 2
01/01 Mike Analyst 2

当前输出:

Date Name Role Role_Count
01/01 Sam Manager 4
02/01 Sam Manager 4
01/01 John Manager 4
01/01 Dan Manager 4
01/01 Bob Analyst 3
02/01 Bob Analyst 3
01/01 Mike Analyst 3

【问题讨论】:

  • COUNT(DISTINCT a.Name) ... 而不是 count(a.Role)
  • @gofr COUNT(DISTINCT...) OVER... 在 SQL Server 中不可用`
  • @Charlieface 哦,他们仍然没有用它做任何事情......我的错
  • 等一下,让我明白这一点:如果只有两行,Sam 的 Role_count 是怎样的?换句话说:你为什么要使用窗口聚合,为什么不使用常规聚合?我们可以有样本输入数据吗?
  • @Charlieface 我希望所有不同的人都担任这个角色。所以 Sam 在两个不同的日期出现了两次,但无论他出现多少次,仍然只有 3 个唯一的经理。示例数据是没有角色计数列的同一张表。

标签: sql sql-server count window-functions


【解决方案1】:

很遗憾,SQL Server(以及其他数据库)不支持将COUNT(DISTINCT) 作为窗口函数。幸运的是,有一个简单的技巧可以解决这个问题——DENSE_RANK()s 的总和减一:

select a.Name, a.Role,
       (dense_rank() over (partition by a.Role order by a.Name asc) +
        dense_rank() over (partition by a.Role order by a.Name desc) -
        1
       ) as distinct_names_in_role
from table a
group by a.name, a.role

【讨论】:

  • 您好,感谢您的回复。当只有唯一名称时,这很好用。日期列多次重复名称,因此导致此密集排名计数超过重复次数。我在示例代码中添加了 a.date 以澄清
  • @BaronG 。 . .不,这行得通。这就是它取代COUNT(DISTINCT) 的意义所在。如果你设置一个 dbfiddle,我可以说明它。
【解决方案2】:

很遗憾,COUNT(DISTINCT 不能用作窗口聚合。但是我们可以使用DENSE_RANKMAX的组合来模拟它:

select

a.Name,
a.Role,
MAX(rnk) OVER (PARTITION BY date, Role) as Role_Count

from (
    SELECT *,
        DENSE_RANK() OVER (PARTITION BY date, Role ORDER BY Name) AS rnk
    FROM table
) a

如果Name 可能有空值,那么我们需要考虑到这一点:

select

a.Name,
a.Role,
MAX(CASE WHEN Name IS NOT NULL THEN rnk END) OVER (PARTITION BY date, Role) as Role_Count

from (
    SELECT *,
        DENSE_RANK() OVER (PARTITION BY date, Role, CASE WHEN Name IS NULL THEN 0 ELSE 1 END ORDER BY Name) AS rnk
    FROM table
) a

【讨论】:

  • 嗨,我还需要 a.date 列的唯一性。
  • 好的,我想你现在明白了
猜你喜欢
  • 1970-01-01
  • 2018-05-24
  • 2015-11-28
  • 2021-03-22
  • 1970-01-01
  • 2021-01-27
  • 1970-01-01
  • 1970-01-01
  • 2015-04-22
相关资源
最近更新 更多