TSQL:按一列分组,计算所有行并根据 row_number 将值保留在第二列

TSQL: Group by one column, count all rows and keep value on second column based on row_number

我有一个查询 returns 一个 ID、一个名称和 Row_Number() 基于一些规则。

查询看起来像这样

SELECT 
    tm.id AS Id,
    pn.Name AS Name,
    ROW_NUMBER() OVER(PARTITION BY tm.id ORDER BY tm.CreatedDate ASC) AS Row
FROM 
    #tempTable AS tm
LEFT JOIN 
    names pn WITH (NOLOCK) ON tm.nameId = pn.NameId
WHERE ....

上面查询的输出看起来像下面的 table 和虚拟数据

CREATE TABLE people
(
    id int,
    name varchar(55),
    row int
);

INSERT INTO people 
VALUES (1, 'John', 1), (1, 'John', 2), (2, 'Mary', 1),
       (3, 'Jeff', 1), (4, 'Bill', 1), (4, 'Bill', 2),
       (4, 'Bill', 3), (4, 'Billy', 4), (5, 'Bobby', 1),
       (5, 'Bob', 2), (5, 'Bob' , 3), (5, 'Bob' , 4);

我尝试做的是按 id 字段分组,计算所有行,但对于名称,使用 row = 1

我的尝试是这样的,但是,很明显,我得到了不同的行,因为我在分组依据中包含了 x.name

SELECT 
    x.id,
    x.name,
    COUNT(*) AS Value
FROM
    (SELECT 
         tm.id AS Id,
         pn.Name  AS Name,
         ROW_NUMBER() OVER(PARTITION BY tm.id ORDER BY tm.CreatedDate ASC) AS Row
     FROM 
         #tempTable AS tm
     LEFT JOIN 
         names pn WITH (NOLOCK) ON tm.nameId = pn.NameId
     WHERE ....
) x
GROUP BY 
    x.id, x.name
ORDER BY 
    COUNT(*) DESC

虚拟数据的预期结果是:

id    name   count
------------------
 1    John     2
 2    Mary     1
 3    Jeff     1
 4    Bill     4
 5    Bobby    4

这可能是解决您的问题的一种方法:对 id 和目标名称 (case when p.row = 1 then p.name end) 进行分组以进行计数。向分组添加 with rollup 将“汇总”计数聚合。然后可以使用 id 上的另一个聚合来合并来自中间数据集的行值(在 fiddle 中可见)。

with cte as
(
  select p.id,
         case when p.row = 1 then p.name end as name,
         count(1) as cnt
  from people p
  group by p.id, case when p.row = 1 then p.name end with rollup
  having grouping(p.id) = 0
)
select cte.id,
       max(cte.name) as name,
       max(cte.cnt) as [count]
from cte
group by cte.id;

Fiddle


这将是另一种解决方案:在 id 上分组进行常规计数查询,然后使用 cross apply.

获取所需的名称
with cte as
(
  select p.id,
         count(1) as cnt
  from people p
  group by p.id
)
select cte.id,
       n.name,
       cte.cnt as [count]
from cte
cross apply ( select p.name
              from people p
              where p.id = cte.id
                and p.row = 1 ) n;

Fiddle

您可以使用 FIRST_VALUE() window 函数获取行号 = 1 的行的名称,使用关键字 DISTINCT 则不需要 GROUP BY:

SELECT DISTINCT tm.id AS Id
     , FIRST_VALUE(pn.Name) OVER (PARTITION BY tm.id ORDER BY tm.CreatedDate ASC) AS Name
     , COUNT(*) OVER (PARTITION BY tm.id) AS counter
FROM #tempTable AS tm
LEFT JOIN names pn WITH (NOLOCK) ON tm.nameId = pn.NameId
WHERE ....

如果您不能使用 FIRST_VALUE() 那么您可以使用条件聚合来实现:

SELECT id,
       MAX(CASE WHEN Row = 1 THEN Name END) AS NAME,
       COUNT(*) AS Counter
FROM (
  SELECT tm.id AS Id
       , pn.Name  AS Name
       , ROW_NUMBER() OVER(PARTITION BY tm.id ORDER BY tm.CreatedDate ASC) AS Row
  FROM #tempTable AS tm
  LEFT JOIN names pn WITH (NOLOCK) ON tm.nameId = pn.NameId
  WHERE ....
) t
GROUP BY id