Sql 服务器:限制 string_agg 结果

Sql server: limit string_agg result

我有以下查询(为每个客户显示用户列表):

select cu.customer_id , STRING_AGG(u.first_name + ' ' + u.last_name , ',') as users   
from customer_user cu join user u on cu.user_id = u.id   
where ... 
group by cu.customer_id

如何限制 string_agg 函数为每个组仅聚合 10 个元素?

您可以尝试对行进行编号:

SELECT customer_id , STRING_AGG(first_name + ' ' + last_name , ',') AS users   
FROM (
   SELECT 
      cu.customer_id, u.first_name, u.last_name,
      ROW_NUMBER() OVER (PARTITION BY cu.customer_id ORDER BY (SELECT NULL)) AS rn
   FROM customer_user cu 
   JOIN user u ON cu.user_id = u.id
   -- WHERE ...
) t
WHERE rn <= 10
GROUP BY customer_id

我们可以在 sub-query 中使用 row_number。我在这个例子中限制为 2,您可以将 rn 的限制更改为 10 或其他数字。
我显示第一个查询没有限制,第二个查询有限制以显示差异。

create table users(
  id int, 
  first_name varchar(25),
  last_name varchar(25)
  );
insert into users values
(1,'Andrew','A'),
(2,'Bill','B'),
(3,'Charlie','C');
create table customer_user(
  customer_id int,
  user_id int);
insert into customer_user values
(1,1),(1,2),(1,3);
GO

6 行受影响

select 
  cu.customer_id , 
  STRING_AGG(u.first_name + ' ' + u.last_name , ',') as users   
from customer_user cu join users u on cu.user_id = u.id  
group by cu.customer_id
GO
customer_id | users                    
----------: | :------------------------
          1 | Andrew A,Bill B,Charlie C
select
  u.customer_id , 
  STRING_AGG(u.first_name + ' ' + u.last_name , ',') as users   
from (
  select
    row_number() over(partition by customer_id order by u.id) rn,
    cu.customer_id,
    u.first_name,
    u.last_name
  from customer_user cu 
  join users u on cu.user_id = u.id  
  ) u
where rn < 3
group by u.customer_id
GO
customer_id | users          
----------: | :--------------
          1 | Andrew A,Bill B

db<>fiddle here

这又是另一种情况,我发现在 CTE 中分离逻辑的“丑陋”部分(串联并确定“第一个”或“任何”10),然后直到之后才聚合:

; -- see sqlblog.org/cte
WITH src AS
(
  SELECT cu.customer_id, n = CONCAT(u.first_name, ' ', u.last_name),
      rn = ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY @@SPID)
    FROM dbo.customer_user AS cu
    INNER JOIN dbo.[user] AS u -- bad table name
    ON cu.user_id = u.id
    /* WHERE ... */
)
SELECT customer_id, users = STRING_AGG(n, N',')
  FROM src
  WHERE rn <= 10
  GROUP BY customer_id;