【问题标题】:SQL Server rownumbering and filtered resultsSQL Server 行编号和筛选结果
【发布时间】:2012-07-23 04:34:36
【问题描述】:

我有一个应用程序,我需要在列表中显示数据的“页面”。基本思想相当普遍。显示屏显示项目列表,显示屏底部有一些控件,可让您转到下一个数据“页面”。

一切顺利。我有这个工作。

以下是我用来支持“下一个”行为的视图中的 SQL。

CREATE VIEW CampaignParticipants AS
SELECT  row_number() OVER (ORDER BY TPT.LastName, TPT.FirstName, TPT.DoB) AS RowNumber
        ,CGP.*
        ,TPT.*
FROM    tblCampaignGEDPush CGP
JOIN    tblParticipants TPT
ON      CGP.PartID = TPT.PartID

这是我使用 VIEW 的方式

SELECT  *
FROM    CampaignParticipants
WHERE   RowNumber >= 0
AND     RowNumber <= 100

这模拟了从 VIEW 中抓取 100 个结果的“第一页”。每组结果的页面都很漂亮。

很好……但是:

正如你们中的一些处理过这个问题的人可能知道的那样,这是有缺陷的。如果我想在TPT.LastName like 'R%' 上搜索并获得第一组结果,那我就完蛋了。

我想开始查看RowNumber = 0,停在RowNumber = 100,但“R”结果可能会远远超出该范围。结果:列表返回为空。

而且它变得更加棘手:用户希望能够“过滤”姓氏、名字、DoB、位置、电话、邮编等任何内容。

**edit:我不能真正将过滤器放在“内部”查询上,因为它在视图中,并且过滤器可以任意更改

任何人有任何想法如何获得此结果集在过滤结果集上有一个row_number()

【问题讨论】:

  • 好吧 Austin Powers 你为什么要限制你的视野?
  • 不知道你在问什么

标签: sql-server row-number


【解决方案1】:

应该这样做......

SELECT * FROM (
SELECT  *, ROW_NUMBER() OVER (ORDER BY LastName, FirstName, DoB) AS __RN
FROM    CampaignParticipants
WHERE   LastName LIKE 'R%') innerData WHERE __RN BETWEEN 1 and 100

但是,您应该使用列名,而不是“*”。我不知道您的表格是什么样的,所以我无法为您填写。

【讨论】:

  • 此解决方案的问题在于它要求过滤器位于“内部”选择内。因为我无法预测过滤器将如何在迭代之间运行,所以在视图中执行此操作会变得很棘手......正如另一张海报所暗示的那样,它是一个过程。只是想看看那里是否有一个聪明的单线。
  • 您不会在视图中执行此操作。你会在一个过程中做到这一点。而且,如果我没记错的话,这是“一行”——即一条语句。无论如何,我个人不会将 ROW_NUMBER() 放在视图中 - 因为您不知道输出是否会被过滤。我猜这就是你问这个问题的原因。
  • [我猜,你问这个问题的原因是什么]...exactamundo
【解决方案2】:

首先尝试使用 proc 而不是视图,因为用户要求您进行如此多的过滤,那么 proc 是您获得的唯一解决方案。在 proc 内部使用所有这些过滤器过滤数据,然后生成 row_number 然后显示说前 100 条记录之类的。

【讨论】:

  • 我害怕有人会说这样的话。 =(
【解决方案3】:

我希望有比这更优雅的东西。 这是我的存储过程解决方案。我蹩脚的 SQL 技能能想到的最好的东西。

参数列表中的 OUT 参数用于该集合中的总行数,因此前端知道有多少页与该特定过滤器集合一起。

CREATE PROC [dbo].[procCampaignGEDPushSelect]
    @LastName       VARCHAR(50)     = null
    ,@FirstName     VARCHAR(50)     = null
    ,@Location      VARCHAR(255)    = null
    ,@DoB           DateTime        = null
    ,@Zip           VARCHAR(50)     = null
    ,@Phone         VARCHAR(50)     = null
    ,@Email         VARCHAR(255)    = null
    ,@Gender        VARCHAR(20)     = null
    ,@IsGED         Bit             = 0
    ,@IsBTT         Bit             = 0
    ,@IsOACE        Bit             = 0
    ,@Completed     Bit             = 0
    ,@TotalCount    INT             OUT
AS
BEGIN

SELECT @LastName    = @LastName     + '%'
SELECT @FirstName   = @FirstName    + '%' 
SELECT @Location    = @Location     + '%' 
SELECT @Zip         = @Zip          + '%' 
SELECT @Phone       = @Phone        + '%' 
SELECT @Email       = @Email        + '%' 
SELECT @Gender      = @Gender       + '%' 

SELECT     row_number() OVER (ORDER BY LastName, FirstName, DoB) AS RowNumber
    , TPT.LastName
    , TPT.FirstName
    , TPT.WF1Site
    , TPT.DOB
    , TPT.Zip
    , TPT.Telephone
    , TPT.CellPhone
    , TPT.Email
    , TPT.Gender
    , TPT.IsBTT
    , TPT.IsGED
    , TPT.IsOACE
    , TPT.IsSRS
    ,CGP.*

FROM        tblCampaignGEDPush CGP

JOIN        tblParticipants TPT
ON          CGP.PartID = TPT.PartID

WHERE       1=1

AND         1 = (CASE WHEN @LastName    IS NOT NULL THEN (CASE WHEN TPT.LastName    LIKE @LastName  THEN 1 ELSE 0 END) ELSE 1 END)  
AND         1 = (CASE WHEN @FirstName   IS NOT NULL THEN (CASE WHEN TPT.FirstName   LIKE @Firstname THEN 1 ELSE 0 END) ELSE 1 END)
AND         1 = (CASE WHEN @Location    IS NOT NULL THEN (CASE WHEN TPT.WF1Site     LIKE @Location  THEN 1 ELSE 0 END) ELSE 1 END)  
AND         1 = (CASE WHEN @Zip         IS NOT NULL THEN (CASE WHEN TPT.Zip         LIKE @Zip       THEN 1 ELSE 0 END) ELSE 1 END) 
AND
(           1 = (CASE WHEN @Phone       IS NOT NULL THEN (CASE WHEN TPT.Telephone   LIKE @Phone     THEN 1 ELSE 0 END) ELSE 1 END) 
    OR      1 = (CASE WHEN @Phone       IS NOT NULL THEN (CASE WHEN TPT.CellPhone   LIKE @Phone     THEN 1 ELSE 0 END) ELSE 1 END) 
)
AND         1 = (CASE WHEN @Email       IS NOT NULL THEN (CASE WHEN TPT.Email       LIKE @Email     THEN 1 ELSE 0 END) ELSE 1 END) 
AND         1 = (CASE WHEN @Gender      IS NOT NULL THEN (CASE WHEN TPT.Gender      LIKE @Gender    THEN 1 ELSE 0 END) ELSE 1 END) 
AND         1 = (CASE WHEN @DoB         IS NOT NULL THEN (CASE WHEN TPT.DoB         = @DoB          THEN 1 ELSE 0 END) ELSE 1 END) 
AND         1 = (CASE WHEN @IsGED       != 0        THEN (CASE WHEN TPT.IsGED       = 1             THEN 1 ELSE 0 END) ELSE 1 END) 
AND         1 = (CASE WHEN @IsBTT       != 0        THEN (CASE WHEN TPT.IsBTT       = 1             THEN 1 ELSE 0 END) ELSE 1 END) 
AND         1 = (CASE WHEN @IsOACE      != 0        THEN (CASE WHEN TPT.IsOACE      = 1             THEN 1 ELSE 0 END) ELSE 1 END) 

AND         1 = (CASE WHEN @Completed   != 0        THEN (CASE WHEN CGP.Completed   = 1             THEN 1 ELSE 0 END) ELSE 1 END)

ORDER BY    TPT.LastName
            , TPT.FirstName
            , TPT.DoB


SELECT @TotalCount = @@ROWCOUNT

END

于是我开始思考。由于我在 .NET 中,所以我不使用这个棘手的、容易出错的过程(顺便说一句,它工作得很好),我想知道那里是否有一个很好的紧凑解决方案。

现在我知道最初的问题与 .NET 无关,所以我将上面的过程留给那些对严格的 SQL 解决方案感兴趣的人可用,我认为它工作得很好。

于是我开始深入研究 IQueryable 接口并获得了成功:

IQueryable<queryParticipant>    qparticipant = db.queryParticipants.AsQueryable();
...
qparticipant = qparticipant.Where( ... any filter you choose );
...

return qparticipant

    .OrderBy( p => p.LastName )
    .OrderBy( p => p.FirstName )
    .OrderBy( p => p.DOB )
    .Select( ... whatever you like ... )
    .Skip( StartRecordNumber )          // This is the trick! Start the query here..
    .Take( PageSize )                   // Take only as many as you need
    ;

就是这样。如果可用,.NET 方法很好。当这样的 API 不可用时,存储过程非常有用。

【讨论】:

  • 所以您将与 SQL Server 无关的答案标记为正确?这将有助于未来的访问者。
  • 不完全是。 sqlserver 解决方案仍然存在。也许它在您的浏览器中丢失了?并且 sql server 解决方案在我的标记答案中得到了充分的体现,而不仅仅是一个建议。 .net 解决方案是为在构建 .net 应用程序时可能实际尝试解决问题的任何人提供的。为什么不呢?
  • 你知道这里的SQL server版本是把整个数据集输出到客户端吧?
  • 除非您传入过滤查询中结果集的参数,否则它会。试一试。
  • 我不需要,我知道它会做什么。我希望任何使用类似东西的人都不能使用大数据。将 1000000 行发送回客户端以显示 20 页并不是最佳实践。
猜你喜欢
  • 2020-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-02
  • 2018-09-26
  • 2017-07-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多