获取每个组的前 5 条记录,并在每个组中将它们连接成一行
Get top 5 records for each group and Concate them in a Row per group
我有一个 table Contacts
基本上如下所示:
Id | Name | ContactId | Contact | Amount
---------------------------------------------
1 | A | 1 | 12323432 | 555
---------------------------------------------
1 | A | 2 | 23432434 | 349
---------------------------------------------
2 | B | 3 | 98867665 | 297
--------------------------------------------
2 | B | 4 | 88867662 | 142
--------------------------------------------
2 | B | 5 | null | 698
--------------------------------------------
这里,ContactId
在整个table中是唯一的。 Contact
可以是 NULL
& 我想排除那些。
现在,我想 select 根据 Amount
每个 Id 的前 5 个联系人。我通过以下查询完成了:
WITH cte AS (
SELECT id, Contact, amount, ROW_NUMBER()
over (
PARTITION BY id
order by amount desc
) AS RowNo
FROM contacts
where contact is not null
)
select *from cte where RowNo <= 5
到目前为止一切正常。现在我想为每个组连接这些 (<=5) 记录,并通过连接它们将它们显示在一行中。
预期结果:
Id | Name | Contact
-------------------------------
1 | A | 12323432;23432434
-------------------------------
2 | B | 98867665;88867662
我正在使用以下查询来实现此目的,但它仍然在单独的行中提供所有记录,并且还包括 Null
值:
WITH cte AS (
SELECT id, Contact, amount,contactid, ROW_NUMBER()
over (
PARTITION BY id
order by amount desc
) AS RowNo
FROM contacts
where contact is not null
)
select *from id, name,
STUFF ((
SELECT distinct '; ' + isnull(contact,'') FROM cte
WHERE co.id= cte.id and co.contactid= cte.contactid
and RowNo <= 5
FOR XML PATH('')),1, 1, '')as contact
from contacts co inner join cte where cte.id = co.id and co.contactid= cte.contactid
以上查询仍然为我提供差异行中的所有前 5 个联系人,也包括 null。
一起使用CTE
和STUFF
是个好主意吗?请提出是否有比这更好的方法。
如果您是 运行 SQL Server 2017 或更高版本,您可以使用 string_agg()
:与大多数其他聚合函数一样,它会在设计上忽略 null
值。
select id, name, string_agg(contact, ',') within group (order by rn) all_contacts
from (
select id, name, contact
row_number() over (partition by id order by amount desc) as rn
from contacts
where contact is not null
) t
where rn <= 5
group by id, name
请注意,您在这里并不严格需要 CTE;您可以 return 从子查询中获取您需要的列,并在外部查询中直接使用它们。
在早期版本中,一种使用 stuff()
和 for xml path
的方法是:
with cte as (
select id, name, contact,
row_number() over (partition by id order by amount desc) as rn
from contacts
where contact is not null
)
select id, name,
stuff(
(
select ', ' + c1.concat
from cte c1
where c1.id = c.id and c1.rn <= 5
order by c1.rn
for xml path (''), type
).value('.', 'varchar(max)'), 1, 2, ''
) all_contacts
from cte
group by id, name
我在最后一个查询中遇到了问题:
我的期末考试 Select
不需要原版 Contact
table,因为我在 CTE
中已经拥有我需要的一切。此外,在 STUFF()
中,我正在使用 contactid
加入,这实际上是我在这里尝试连接的。由于我使用该条件进行连接,因此我得到了不同行中的记录。我已经删除了这 2 个条件并且它起作用了。
WITH cte AS (
SELECT id, Contact, amount,contactid, ROW_NUMBER()
over (
PARTITION BY id
order by amount desc
) AS RowNo
FROM contacts
where contact is not null
)
select *from id, name,
STUFF ((
SELECT distinct '; ' + isnull(contact,'') FROM cte
WHERE co.id= cte.id
and RowNo <= 5
FOR XML PATH('')),1, 1, '')as contact
from cte where rowno <= 5
您可以使用条件聚合:
身份证,姓名,联系方式,
select id, name,
concat(max(case when seqnum = 1 then contact + ';' end),
max(case when seqnum = 2 then contact + ';' end),
max(case when seqnum = 3 then contact + ';' end),
max(case when seqnum = 4 then contact + ';' end),
max(case when seqnum = 5 then contact + ';' end)
) as contacts
from (select c.*
row_number() over (partition by id order by amount desc) as seqnum
from contacts c
where contact is not null
) c
group by id, name;
我同意@GMB。 STRING_AGG() 是你需要的...
WITH
contacts(Id,nm,ContactId,Contact,Amount) AS (
SELECT 1,'A',1,12323432,555
UNION ALL SELECT 1,'A',2,23432434,349
UNION ALL SELECT 2,'B',3,98867665,297
UNION ALL SELECT 2,'B',4,88867662,142
UNION ALL SELECT 2,'B',5,NULL ,698
)
,
with_filter_val AS (
SELECT
*
, ROW_NUMBER() OVER(PARTITION BY id ORDER BY amount DESC) AS rn
FROM contacts
)
SELECT
id
, nm
, STRING_AGG(CAST(contact AS CHAR(8)),',') AS contact_list
FROM with_filter_val
WHERE rn <=5
GROUP BY
id
, nm
-- out id | nm | contact_list
-- out ----+----+-------------------
-- out 1 | A | 12323432,23432434
-- out 2 | B | 98867665,88867662
我有一个 table Contacts
基本上如下所示:
Id | Name | ContactId | Contact | Amount
---------------------------------------------
1 | A | 1 | 12323432 | 555
---------------------------------------------
1 | A | 2 | 23432434 | 349
---------------------------------------------
2 | B | 3 | 98867665 | 297
--------------------------------------------
2 | B | 4 | 88867662 | 142
--------------------------------------------
2 | B | 5 | null | 698
--------------------------------------------
这里,ContactId
在整个table中是唯一的。 Contact
可以是 NULL
& 我想排除那些。
现在,我想 select 根据 Amount
每个 Id 的前 5 个联系人。我通过以下查询完成了:
WITH cte AS (
SELECT id, Contact, amount, ROW_NUMBER()
over (
PARTITION BY id
order by amount desc
) AS RowNo
FROM contacts
where contact is not null
)
select *from cte where RowNo <= 5
到目前为止一切正常。现在我想为每个组连接这些 (<=5) 记录,并通过连接它们将它们显示在一行中。
预期结果:
Id | Name | Contact
-------------------------------
1 | A | 12323432;23432434
-------------------------------
2 | B | 98867665;88867662
我正在使用以下查询来实现此目的,但它仍然在单独的行中提供所有记录,并且还包括 Null
值:
WITH cte AS (
SELECT id, Contact, amount,contactid, ROW_NUMBER()
over (
PARTITION BY id
order by amount desc
) AS RowNo
FROM contacts
where contact is not null
)
select *from id, name,
STUFF ((
SELECT distinct '; ' + isnull(contact,'') FROM cte
WHERE co.id= cte.id and co.contactid= cte.contactid
and RowNo <= 5
FOR XML PATH('')),1, 1, '')as contact
from contacts co inner join cte where cte.id = co.id and co.contactid= cte.contactid
以上查询仍然为我提供差异行中的所有前 5 个联系人,也包括 null。
一起使用CTE
和STUFF
是个好主意吗?请提出是否有比这更好的方法。
如果您是 运行 SQL Server 2017 或更高版本,您可以使用 string_agg()
:与大多数其他聚合函数一样,它会在设计上忽略 null
值。
select id, name, string_agg(contact, ',') within group (order by rn) all_contacts
from (
select id, name, contact
row_number() over (partition by id order by amount desc) as rn
from contacts
where contact is not null
) t
where rn <= 5
group by id, name
请注意,您在这里并不严格需要 CTE;您可以 return 从子查询中获取您需要的列,并在外部查询中直接使用它们。
在早期版本中,一种使用 stuff()
和 for xml path
的方法是:
with cte as (
select id, name, contact,
row_number() over (partition by id order by amount desc) as rn
from contacts
where contact is not null
)
select id, name,
stuff(
(
select ', ' + c1.concat
from cte c1
where c1.id = c.id and c1.rn <= 5
order by c1.rn
for xml path (''), type
).value('.', 'varchar(max)'), 1, 2, ''
) all_contacts
from cte
group by id, name
我在最后一个查询中遇到了问题:
我的期末考试 Select
不需要原版 Contact
table,因为我在 CTE
中已经拥有我需要的一切。此外,在 STUFF()
中,我正在使用 contactid
加入,这实际上是我在这里尝试连接的。由于我使用该条件进行连接,因此我得到了不同行中的记录。我已经删除了这 2 个条件并且它起作用了。
WITH cte AS (
SELECT id, Contact, amount,contactid, ROW_NUMBER()
over (
PARTITION BY id
order by amount desc
) AS RowNo
FROM contacts
where contact is not null
)
select *from id, name,
STUFF ((
SELECT distinct '; ' + isnull(contact,'') FROM cte
WHERE co.id= cte.id
and RowNo <= 5
FOR XML PATH('')),1, 1, '')as contact
from cte where rowno <= 5
您可以使用条件聚合: 身份证,姓名,联系方式,
select id, name,
concat(max(case when seqnum = 1 then contact + ';' end),
max(case when seqnum = 2 then contact + ';' end),
max(case when seqnum = 3 then contact + ';' end),
max(case when seqnum = 4 then contact + ';' end),
max(case when seqnum = 5 then contact + ';' end)
) as contacts
from (select c.*
row_number() over (partition by id order by amount desc) as seqnum
from contacts c
where contact is not null
) c
group by id, name;
我同意@GMB。 STRING_AGG() 是你需要的...
WITH
contacts(Id,nm,ContactId,Contact,Amount) AS (
SELECT 1,'A',1,12323432,555
UNION ALL SELECT 1,'A',2,23432434,349
UNION ALL SELECT 2,'B',3,98867665,297
UNION ALL SELECT 2,'B',4,88867662,142
UNION ALL SELECT 2,'B',5,NULL ,698
)
,
with_filter_val AS (
SELECT
*
, ROW_NUMBER() OVER(PARTITION BY id ORDER BY amount DESC) AS rn
FROM contacts
)
SELECT
id
, nm
, STRING_AGG(CAST(contact AS CHAR(8)),',') AS contact_list
FROM with_filter_val
WHERE rn <=5
GROUP BY
id
, nm
-- out id | nm | contact_list
-- out ----+----+-------------------
-- out 1 | A | 12323432,23432434
-- out 2 | B | 98867665,88867662