Sql 获取每组的前5项记录,并将它们按组排列成一行
我有一个表联系人,基本上如下所示:Sql 获取每组的前5项记录,并将它们按组排列成一行,sql,sql-server,string,common-table-expression,Sql,Sql Server,String,Common Table Expression,我有一个表联系人,基本上如下所示: Id | Name | ContactId | Contact | Amount --------------------------------------------- 1 | A | 1 | 12323432 | 555 --------------------------------------------- 1 | A | 2 | 23432434 | 349 ------------
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在整个表中是唯一的。联系人可以为空&我想排除这些联系人
现在,我想根据他们的数量为每个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
我使用以下查询来实现这一点,但它仍然在单独的行中提供所有记录,还包括空值:
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和其他东西一起使用是个好主意吗?请建议是否有比此更好的方法。如果您运行的是SQL Server 2017或更高版本,您可以使用string\u agg:与大多数其他聚合函数一样,它通过设计忽略空值
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
如果您运行的是SQL Server 2017或更高版本,则可以使用string_agg:作为大多数其他聚合函数,它通过设计忽略空值
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中拥有了我所需要的一切。另外,在内部,我使用contactid加入,这就是我在这里要做的。因为我将该条件用于连接,所以我将获得不同行中的记录。我已经删除了这两个条件,它的工作
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
我的最后一个问题是: 在我的最终选择中,我不需要原始的联系人表,因为我已经在CTE中拥有了我所需要的一切。另外,在内部,我使用contactid加入,这就是我在这里要做的。因为我将该条件用于连接,所以我将获得不同行中的记录。我已经删除了这两个条件,它的工作
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;
您可以使用条件聚合: 身份证,姓名,联系人
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。字符串是你需要的
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
我同意@GMB。字符串是你需要的
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
谢谢,但我认为我们使用的是SQL Server 2012或更低版本。是的。我尝试了不同的事情,得到了同样的问题并发布了同样的帖子。您的回答似乎也是正确的,但我认为您不需要分组。谢谢,但我认为我们使用的是SQL Server 2012或更低版本。是的。我尝试了不同的事情,得到了同样的问题并发布了同样的帖子。你的答案似乎也是正确的,但我认为你不需要在那里分组。我知道这一点,但正如我在评论中提到的,如果我可以使用像String_Split和String_Agg这样的函数,生活会容易得多。谢谢你的回答。如果你不需要在这个过程中添加排名/前n名的评估…我知道这一点,但正如我在评论中提到的,如果我可以使用像String_Split和String_Agg这样的函数,生活会容易得多。谢谢您的回答。如果您需要在过程中添加排名/前n名评估,则不需要。。。