使用SQL获取分页的不同记录(不重复)

时间:2018-01-31 12:25:48

标签: sql sql-server tsql

按照@mdb对应用pagination using SQL SERVER的回答,我发现当主表连接到其他表以获得一对多关系时很难检索不同的记录,即一个人有很多地址。 / p>

用例,假设我要检索所有在纽约有地址#temp_person#temp_addresses的地址的人,我会在PersonIDOwnerID加入他们

当一个人有多个地址时会出现问题,结果集包含重复记录。

为了更清楚,这是一个包含数据的示例查询:

示例数据:

create table #temp_person (
    PersonID int not null,
    FullName varchar(max) not null
)

create table #temp_addresses(
    AddressID int identity not null,
    OwnerID int not null,
    Address1 varchar(max),
    City varchar(max)
)

insert into #temp_person
values 
    (1, 'Sample One'),
    (2, 'Sample Two'),
    (3, 'Sample Three')

insert into #temp_addresses (OwnerID,  Address1, City)
values
(1, 'Somewhere East Of', 'New York'),
(1, 'Somewhere West Of', 'New York'),
(2, 'blah blah blah', 'Atlantis'),
(2, 'Address2 Of Sample Two', 'New York'),
(2, 'Address3 Of Sample Two', 'Nowhere City'),
(3, 'Address1 Of Sample Three', 'New York'),
(3, 'Address2 Of Sample Three', 'Seattle')

--drop table #temp_addresses, #temp_person

分页查询:

 SELECT
    (
        CAST( RowNum as varchar(MAX) ) 
        + '/' 
        + CAST(TotalCount as varchar(MAX))
     ) as ResultPosition
    , PersonID
    , FullName
FROM (
SELECT DISTINCT
        ROW_NUMBER() OVER(ORDER BY p.FullName ASC) as RowNum 
        , p.PersonID
        , p.FullName
        , Count(1) OVER() as TotalCount
    FROM #temp_person p
    LEFT JOIN #temp_addresses a
        ON p.PersonID = a.OwnerID
    WHERE City = 'New York'
) as RowConstrainedResult
WHERE RowNum > 0 AND RowNum <= 3
ORDER BY RowNum

预期结果:

ResultPosition  PersonID    FullName
   1/3           1         Sample One
   2/3           2         Sample Two
   3/3           3        Sample Three

实际结果:

ResultPosition  PersonID    FullName
   1/4            1        Sample One
   2/4            1        Sample One
   3/4            3       Sample Three

如您所见,内部查询由于与#temp_addresses的连接而返回多个记录。

我们是否只能通过PersonID返回唯一记录?

更新

实际用例是“高级搜索”功能,用户可以使用不同的过滤器进行搜索,即姓名,名字,姓氏,出生日期,地址等。<WHERE_CLAUSE>和{{1}查询中的}是动态添加的,因此<JOIN_STATEMENTS>不适用于此。

另外,请解决此问题的“分页”方案。也就是说,我想从GROUP BY仅检索N个结果,同时还检索结果的总计数,就好像它们没有被分页一样。即,我从总共500个结果中只检索了25行。

3 个答案:

答案 0 :(得分:0)

只需按PersonID进行分组,无需使用subquery

SELECT 
         cast(row_number() over (order by (select 1)) as varchar(max)) +'/'+
         cast(Count(1) OVER() as varchar(max)) ResultPosition,  
         p.PersonID,
         max(p.FullName) FullName
FROM #temp_person p
LEFT JOIN #temp_addresses a ON p.PersonID = a.OwnerID
WHERE City = 'New York'
group by p.PersonID

编辑:我会使用CTE进行分页

;with cte as
(
    SELECT 
         row_number() over(order by (select 1)) rn,
         cast(row_number() over (order by (select 1)) as varchar(max)) +'/'+
         cast(Count(1) OVER() as varchar(max)) ResultPosition,  
         p.PersonID,
         max(p.FullName) FullName
    FROM #temp_person p
    LEFT JOIN #temp_addresses a ON p.PersonID = a.OwnerID
    WHERE City = 'New York'
    group by p.PersonID
) 
select * from cte 
where rn > 0  and rn <= 2

<强>结果:

ResultPosition  PersonID    FullName
1/3             1           Sample One
2/3             2           Sample Two
3/3             3           Sample Three

答案 1 :(得分:0)

在使用ROW_NUMBER()之前,您需要拥有不同的行。

如果您按City进行过滤,则无需使用LEFT JOIN。请改用INNER JOIN

select ResultPosition = cast(row_number() over (order by (r.PersonID)) as varchar(max)) +'/'+ cast(Count(r.PersonID) OVER() as varchar(max)), *
from(
    SELECT distinct p.PersonID,
                    p.FullName
    FROM #temp_person p
        JOIN #temp_addresses a ON 
            p.PersonID = a.OwnerID
    WHERE City = 'New York') r

修改 考虑分页

declare @page int =1, @rowsPage int = 25

select distinct position, ResultPosition = cast(position as varchar(10)) + '/' + cast(count(*) OVER() as varchar(10)), *
from(
    SELECT  position = DENSE_RANK () over (order by p.PersonID),
            p.PersonID,
            p.FullName
    FROM #temp_person p
        LEFT JOIN #temp_addresses a ON 
            p.PersonID = a.OwnerID
    WHERE City = 'New York'
    ) r
where position between @rowsPage*(@page-1)+1 and @rowsPage*@page

答案 2 :(得分:0)

Geoman Yabes,检查是否有这个帮助...在您的示例中给出预期的结果,并且您可以使用RowNum进行分页: -

SELECT *
FROM
(SELECT ROW_NUMBER() OVER(ORDER BY RowConstrainedResult.PersonId ASC) As RowNum,
        Count(1) OVER() As TotalRows,
        RowConstrainedResult.PersonId,
        RowConstrainedResult.FullName
 FROM (
    SELECT 
          RANK() OVER(PARTITION BY p.PersonId ORDER BY a.Address1 ASC) as Ranking 
        , p.PersonID
        , p.FullName
    FROM #temp_person p
    INNER JOIN #temp_addresses a ON p.PersonID = a.OwnerID WHERE City = 'New York'
) as RowConstrainedResult WHERE Ranking = 1) Filtered
Where RowNum > 0 And RowNum <= 4

示例数据:

insert into #temp_person
values 
    (1, 'Sample One'),  
    (2, 'Sample Two'),  
    (3, 'Sample Three'),  
    (4, 'Sample 4'),  
    (5, 'Sample 5'),  
    (6, 'Sample 6')  

insert into #temp_addresses (OwnerID,  Address1, City)
values
(1, 'Somewhere East Of', 'New York'),  
(1, 'Somewhere West Of', 'New York'),  
(2, 'blah blah blah', 'Atlantis'),  
(2, 'Address2 Of Sample Two', 'New York'),  
(2, 'Address3 Of Sample Two', 'Nowhere City'),  
(3, 'Address1 Of Sample Three', 'New York'),  
(3, 'Address2 Of Sample Three', 'Seattle'),  
(4, 'Address1 Of Sample 4', 'New York'),  
(4, 'Address1 Of Sample 4', 'New York 2'),  
(5, 'Address1 Of Sample 5', 'New York'),  
(6, 'Address1 Of Sample 6', 'New York')  

enter image description here