我在SQL Server中有一个存储过程,它基于来自多个表的多个过滤器(例如DateOfBirth
,DisplayName
,...)来获取联系人。我需要更改存储过程以包括分页和总计数,因为分页是在后端完成的。 PartyId
是唯一的密钥。需要注意的是,一个人可以拥有多个电子邮件和电话,并且我们说我们会搜索DisplayName = "Sarah"
,查询将返回以下内容:
TotalCount PartyId DisplayName EmailAddress PhoneNumber
-----------------------------------------------------------------
3 1 Sarah sarah@gmail.com 1
3 1 Sarah sarah2@gmail.com 1
3 1 Sarah sarah@gmail.com 2
这大致是存储过程的作用,CurrentPage
和PageSize
的指定值以及底部的ORDER BY OFFSET
来测试分页:
DECLARE @CurrentPage int = 1
DECLARE @PageSize int = 1000
SELECT
COUNT(*) OVER () as TotalCount,
p.Id AS PartyId,
e.EmailAddress,
pn.PhoneNumber
etc.....
FROM
[dbo].[Party] AS p WITH(NOLOCK)
INNER JOIN
[dbo].[Email] AS e WITH(NOLOCK) ON p.[Id] = e.[PartyID]
INNER JOIN
[dbo].[PhoneNumber] AS pn WITH(NOLOCK) ON p.[Id] = pn.[PartyID]
etc.....
WHERE
p.PartyType = 1 /*Individual*/
GROUP BY
p.Id, e.EmailAddress, pn.PhoneNumber etc...
ORDER BY
p.Id
OFFSET (@CurrentPage - 1) * @PageSize ROWS
FETCH NEXT @PageSize ROWS ONLY
这是我们在后端按PartyId
分组并分配相应的电子邮件和电话。
var responseModel = unitOfWork.PartyRepository.SearchContacts(model);
if (responseModel != null && responseModel.Count == 0)
{
return null;
}
// get multiple phones/emails for a party
var emailAddresses = responseModel.GroupBy(p => new { p.PartyId, p.EmailAddress })
.Select(x => new {
x.Key.PartyId,
x.Key.EmailAddress
});
var phoneNumbers = responseModel.GroupBy(p => new { p.PartyId, p.PhoneNumber, p.PhoneNumberCreateDate })
.Select(x => new {
x.Key.PartyId,
x.Key.PhoneNumber,
x.Key.PhoneNumberCreateDate
}).OrderByDescending(p => p.PhoneNumberCreateDate);
// group by in order to avoid multiple records with different email/phones
responseModel = responseModel.GroupBy(x => x.PartyId)
.Select(grp => grp.First())
.ToList();
var list = Mapper.Map<List<SearchContactResponseModelData>>(responseModel);
// add all phones/emails to respective party
list = list.Select(x =>
{
x.EmailAddresses = new List<string>();
x.EmailAddresses.AddRange(emailAddresses.Where(y => y.PartyId == x.PartyId).Select(y => y.EmailAddress));
x.PhoneNumbers = new List<string>();
x.PhoneNumbers.AddRange(phoneNumbers.Where(y => y.PartyId == x.PartyId).Select(y => y.PhoneNumber));
return x;
}).ToList();
var sorted = SortAndPagination(model, model.SortBy, list);
SearchContactResponseModel result = new SearchContactResponseModel()
{
Data = sorted,
TotalCount = list.Count
};
return result;
响应将是:
{
"TotalCount": 1,
"Data": [
{
"PartyId": 1,
"DisplayName": "SARAH",
"EmailAddresses": [
"sarah@gmail.com",
"sarah2@gmail.com"
],
"PhoneNumbers": [
"1",
"2"
]
}
]
}
从存储过程返回的TotalCount显然不是真实的,并且在后端代码(我们通过id分配电子邮件/电话和组)之后,我们获得真实的totalCount,它是1而不是3.
如果我们有3个名为Sarah的人,由于多个电话/电子邮件,存储过程中的totalCount将被设为9,真实计数将为3,如果我执行存储过程以使人从1到2,由于9条记录,分页不起作用。
如何在上述场景中实现分页?
答案 0 :(得分:0)
您可以尝试使用CTE将查询与Party表隔离。这将允许您拉出正确的行数(以及正确的总行数),而无需担心电子邮件和电话号码的扩展。
它看起来像这样(重新排列上面的查询):
DECLARE @CurrentPage int = 1;
DECLARE @PageSize int = 1000;
WITH PartyList AS (
SELECT
COUNT(*) OVER () as TotalCount,
p.Id AS PartyId
FROM
[dbo].[Party] AS p WITH(NOLOCK)
WHERE
p.PartyType = 1 /*Individual*/
GROUP BY -- You might not need this now depending on your data
p.Id
ORDER BY
p.Id
OFFSET (@CurrentPage - 1) * @PageSize ROWS
FETCH NEXT @PageSize ROWS ONLY
)
SELECT
pl.TotalCount,
pl.PartyId,
e.EmailAddress,
pn.PhoneNumber
FROM PartyList AS pl
INNER JOIN
[dbo].[Email] AS e WITH(NOLOCK) ON pl.[PartyId] = e.[PartyID]
INNER JOIN
[dbo].[PhoneNumber] AS pn WITH(NOLOCK) ON pl.[PartyId] = pn.[PartyID];
请注意,CTE将要求先前的声明以分号结束。