【问题标题】:Getting Paged Distinct Records Using SQL (Not Duplicate)使用 SQL 获取分页的不同记录(不重复)
【发布时间】:2018-07-10 13:29:58
【问题描述】:

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

用例,假设我想检索所有在纽约有地址的人,给定表 #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 的唯一记录?

更新:

实际用例是“高级搜索”功能,用户可以使用不同的过滤器进行搜索,即姓名、名字、姓氏、出生日期、地址等。查询中的&lt;WHERE_CLAUSE&gt;&lt;JOIN_STATEMENTS&gt;是动态添加的,所以GROUP BY 在这里不适用。

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

【问题讨论】:

    标签: sql sql-server tsql


    【解决方案1】:

    只需按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
    

    【讨论】:

    • 我想检索分页结果,而不是整个查询。另外,虽然您的答案适用于上述示例测试用例,但恐怕我不能在我的实际项目中使用GROUP BY
    【解决方案2】:

    在使用 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
    

    【讨论】:

    • 甚至没有解决我提到的分页功能。
    • DISTINCT 在我的用例中不可靠,因为 JOINS 和 WHERE 语句是动态添加的。请再次查看我的问题。我编辑了它。
    • 再次编辑。我看到了你的编辑。因此,如果您不能使用 DISTINCT,请将其删除。 DENSE_RANK() 不需要 DISTINCT。将 DISTINCT 移至外部 SELECT。
    【解决方案3】:

    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')  
    

    【讨论】:

      猜你喜欢
      • 2021-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-09
      • 2018-05-04
      • 1970-01-01
      相关资源
      最近更新 更多