简体   繁体   English

SQL Server rownumbering和筛选结果

[英]SQL Server rownumbering and filtered results

I have an application in which I need to display "pages" of data in a list. 我有一个应用程序,我需要在列表中显示数据的“页面”。 The basic idea is fairly common. 基本思想相当普遍。 The display shows a list of items and at the bottom of the display are some kind of controls that allow you to go to the next "page" of data. 显示屏显示项目列表,显示屏底部是某种控件,可让您转到下一个“页面”数据。

All well and good. 一切都很好。 I have this working. 我有这个工作。

Following is the SQL in a view I am using to support the "next" behavior. 以下是我用于支持“下一步”行为的视图中的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

Here is how I use the VIEW 这是我如何使用VIEW

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

This simulates grabbing the "first page" of 100 results from the VIEW. 这模拟了从VIEW中抓取100个结果的“第一页”。 Pages thru each set of results just peachy. 页面通过每组结果只是桃子。

Great.. BUT: 太棒了..但是:

As some of you that have dealt with this probably are aware, this is flawed. 正如你们中的一些人所知道的那样,这可能是有缺陷的。 If I want to search on TPT.LastName like 'R%' and get the first set of results, I'm doomed. 如果我想在TPT.LastName like 'R%'上搜索TPT.LastName like 'R%'并获得第一组结果,我注定要失败。

I'm gonna want to start looking at RowNumber = 0 , stop at RowNumber = 100 , but the "R" results will probably be well outside that range. 我想要开始查看RowNumber = 0 ,停在RowNumber = 100 ,但“R”结果可能远远超出该范围。 Upshot: list comes back empty. Upshot:列表返回空白。

And it gets stickier: the user wants to be able to "filter" on LastName, FirstName, DoB, Location, Phone, Zip, anything. 它变得更加棘手:用户希望能够“过滤”LastName,FirstName,DoB,Location,Phone,Zip等等。

**edit: i can't really put the filter on the "inner" query, as it's in a view, and the filter can change arbitrarily **编辑:我无法将过滤器放在“内部”查询上,因为它在视图中,并且过滤器可以任意更改

Anybody have any ideas how to get this result set have a row_number() on the filtered set of results? 任何人都有任何想法如何让这个结果集在过滤的结果集上有一个row_number()

Something like this should do... 这样的事应该做​​......

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

However, you should be using the column names, not '*'. 但是,您应该使用列名,而不是'*'。 I don't know what your tables look like so I can't fill that in for you. 我不知道你的桌子是什么样的,所以我不能为你填写。

首先尝试使用proc而不是视图,因为用户要求你有这么多过滤,然后proc只是你得到的解决方案.Inside proc使用所有这些过滤器过滤数据,然后生成row_number然后显示说首先100记录或类似的。

I wish there were something a bit more elegant than this. 我希望有一些比这更优雅的东西。 This is my stored proc solution. 这是我的存储过程解决方案。 The best my lame SQL skills can conjure up. 我最蹩脚的SQL技能可以让人联想到。

The OUT parameter in the parameter list is for the total rows in this set so the front end knows how many pages all together with this particular filter set. 参数列表中的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

So then I got started thinking. 所以我开始思考了。 Rather than use this tricky, bug-prone proc (which by the way works fairly well), since I am in .NET, I wonder if there is a nice tight solution there. 而不是使用这个棘手的,容易出错的过程(顺便说一下工作得很好),因为我在.NET中,我想知道那里是否有一个很好的紧密解决方案。

Now I know the original question has nothing to do with .NET, so I'm leaving the above proc available for those interested in the strict SQL solution, which, I think, works fairly well. 现在我知道原始问题与.NET无关,所以我将上述proc留给那些对严格SQL解决方案感兴趣的人,我认为这些解决方案运行得相当好。

So I started digging into the IQueryable interface and struck gold: 所以我开始深入研究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
    ;

And that's it. 就是这样。 The .NET approach is nice if available. 如果可用,.NET方法很好。 The Stored Proc is great when such an API is not available. 当这样的API不可用时,Stored Proc很棒。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM