SQLSERVER 分页

自SQLSERVER 2008 起新增的RowNumber确实简化了很多分页的代码,目前百万数据中

RowNumber 配合颠倒Top仍然是最佳实践

现在项目上的搜索比较恶心,主表与从表是个一对多关系,搜索条件里可能带有从表数据,并将符合结果的主表数据以及第一条从表数据(按创建时间排序)取出,注意主表数据不能因为inner join而重复。

像这么恶心的搜索我还是第一次遇到,听说还有种动态视图(可创建自适应索引),但我没去试,还是用了个比较靠谱的,思路如下:

如果无从表搜索,则直接搜索主表,取出分页数,并join 从表

如果有从表,则只能先join再分页。值得一提的是分页排序规则是根据当前页数与总页数相比较得到(所以要先得到总页数)

而从表只取第一条我们也可以巧妙得在RowNumber 中用关键字 PARTITION BY 来达成

最后结果,百万数据的两表join 搜索都在1秒内,部分字段的StartWith搜索也只需要2秒

最后贴出代码,索引啥就不贴了

有从表查询:

  1 USE [GEMS_DM]
  2 GO
  3 /****** Object:  StoredProcedure [dbo].[P_PM_SearchPatronByHasIdentifyCriteria]    Script Date: 12/23/2013 16:38:41 ******/
  4 SET ANSI_NULLS ON
  5 GO
  6 SET QUOTED_IDENTIFIER ON
  7 GO
  8 
  9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 ALTER PROCEDURE [dbo].[P_PM_SearchPatronByHasIdentifyCriteria]    
 20      @IsActive bit = NULL,
 21      @PatronNo bigint=NULL,
 22      @BirthDate date=NULL,
 23      @PNFirstName nvarchar(50)=NULL,
 24      @PNMiddleName nvarchar(50)=NULL,
 25      @PNLastName nvarchar(50)=NULL,
 26      @PreferredName nvarchar(40)=NULL,
 27      @EXFirstName nvarchar(50)=NULL,
 28      @EXLastName nvarchar(50)=NULL,
 29      @DisplayNameType nvarchar(30)=NULL,
 30      @Gender nvarchar(5)=NULL,
 31      @MobileCountryCode nvarchar(5)=NULL,
 32      @Mobile nvarchar(15)=NULL,
 33      @Email nvarchar(30)=NULL,
 34      @MembershipClass nvarchar(30)=Null,
 35      @IDType  nvarchar(5)=NULL,
 36      @IDNumber nvarchar(20)=NULL,
 37      @CountryID  smallint=NULL,
 38      @CityName nvarchar(50)=NULL,
 39      @DocType nvarchar(5)='',     
 40      @Page int = 1,
 41      @PageSize int = 10,
 42      @OrderSQL nvarchar(max)=null,
 43      @TotalRow int output
 44 
 45     
 46 
 47       -- Add the parameters for the stored procedure here    
 48 as
 49 BEGIN
 50 
 51 SET NOCOUNT ON;
 52 SET FMTONLY OFF  
 53 
 54 
 55 declare @Start nvarchar(20),@End nvarchar(20),@RowNumberSQL nvarchar(200),@BetweenSQL nvarchar(100),
 56 @rowCountSql nvarchar(max);
 57 IF (@Page is null) or (@Page <= 0)
 58     SET @Page = 1
 59 IF (@PageSize is null) or (@PageSize <=0)
 60     SET @PageSize = 10
 61 SET @Start=CONVERT(nvarchar(10),(@Page-1)*@PageSize+1);
 62 SET @End=CONVERT(nvarchar(10),(@Page)*@PageSize);
 63 Set @BetweenSQL = ' ROWNUMVE BETWEEN {Start} AND {End} ';
 64 
 65 set @RowNumberSQL = ' ROW_NUMBER() over (order by Patron_No {0} ) AS ROWNUMVE';
 66 
 67 
 68 declare @PatronTableSql nvarchar(max),@IdentificationTableSql nvarchar(max),@sqlwhere nvarchar(max),
 69 @FinalPatronTableSql nvarchar(max)
 70 
 71 set @PatronTableSql = 'SELECT Patron_ID AS PatronID,Patron_No AS PatronNo,PN_First_Name AS PNFirstName,PN_Middle_Name as PNMiddleName,PN_Last_Name AS PNLastName,
 72               Preferred_Name AS PreferredName,EX_First_Name AS EXFirstName,EX_Last_Name AS EXLastName,Birth_Date AS BirthDate,
 73               IsActive,IsMerged,Gender,Membership_Class AS MembershipClass,eMail,Mobile_Country_Code AS MobileCountryCode,
 74               Mobile_No AS MobileNo,Doc_ID as DocID,Patron_No as Patron_No FROM GSMA_Patron ';
 75 if(@PatronNo is not null)
 76     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('Patron_No=',@PatronNo));
 77 if(@BirthDate is not null)
 78     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('Birth_Date=''',@BirthDate,''''));
 79 if(@IsActive is not null)
 80     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('IsActive=',@IsActive));
 81 if(@PNFirstName is not null)
 82     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('PN_First_Name like N''',@PNFirstName,'%'''));
 83 if(@PNMiddleName is not null)
 84     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('PN_Middle_Name like N''',@PNMiddleName,'%'''));
 85 if(@PNLastName is not null)
 86     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('PN_Last_Name like N''',@PNLastName,'%'''));
 87 if(@PreferredName is not null)
 88     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('Preferred_Name like N''',@PreferredName,'%'''));
 89 if(@EXFirstName is not null)
 90     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('EX_First_Name like N''',@EXFirstName+'%'''));
 91 if(@EXLastName is not null)
 92     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('EX_Last_Name like N''',@EXLastName,'%'''));
 93 if(@Gender is not null)
 94     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('Gender = N''',@Gender,''''));
 95 if(@MembershipClass is not null)
 96     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('Membership_Class in (''',Replace(@MembershipClass,',',''','''),''')'));
 97 if(@Email is not null)
 98     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('eMail like N''',@Email+'%'''));
 99 
100 if(@sqlwhere is not null and @sqlwhere <> '')
101     set @PatronTableSql = concat(@PatronTableSql,' where ',@sqlwhere);
102 set @FinalPatronTableSql = concat('(',@PatronTableSql,') A ');
103 
104 
105 --reset @sqlwhere for Identification filter
106 set @sqlwhere = ' IsActive = 1 ';
107 
108 set @IdentificationTableSql = '(SELECT IDType,IDNo,Patron_ID,CountryID,CityName From 
109 (SELECT ID_Type as IDType,ID_No as IDNo,Patron_ID,
110 Country_ID AS CountryID,City_Name as CityName
111 ,ROW_NUMBER() OVER(PARTITION BY Patron_ID ORDER BY ISNULL(Modified_Date,Created_Date) DESC) AS RK FROM GSMA_Patron_Identification WHERE ';
112 if(@IDType is not null)
113     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('ID_Type=N''',@IDType,''''));
114 if(@IDNumber is not null)
115     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('ID_No like N''',@IDNumber,'%'''));
116 if(@CountryID is not null)
117     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('Country_ID=',@CountryID));
118 if(@CityName is not null)
119     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('City_Name like N''',@CityName,'%'''));
120 set @IdentificationTableSql = concat(@IdentificationTableSql,@sqlwhere,') ID WHERE RK = 1 ) B ');
121 
122 declare @PISql nvarchar(max) = concat('Select * from
123 (Select * {0} from ',char(10),@FinalPatronTableSql,char(10),' inner join',char(10),@IdentificationTableSql,'
124  ON A.PatronID = B.Patron_ID) PLI ')
125 
126 -- remove template string and get total count sql
127 set @rowCountSql = concat('Select @rowCount = count(1)  from (',REPLACE(@PISql,'{0}',''),') WS ');
128 -- add paging rownumber 
129 set @PISql = REPLACE(@PISql,'{0}',concat(',',@RowNumberSQL))
130 -- add between sql
131 set @PISql = concat(@PISql,' WHERE ',@BetweenSQL);
132 set @PISql = concat('(',@PISql,') PI');
133 
134 declare @pageSql nvarchar(max) = concat('SELECT PI.PatronID,PI.PatronNo,PI.PNFirstName,PI.PNMiddleName,PI.PNLastName,PI.PreferredName,PI.EXFirstName,
135 PI.EXLastName,PI.BirthDate,G.Lkp_Value as GenderName,PI.MembershipClass,M.Lkp_Value as MembershipClassName,
136 PI.MobileCountryCode,PI.MobileNo,PI.eMail,PI.IsActive,PI.IsMerged,I.Lkp_Value as IDTypeName,PI.IDNo, C.Country_Name as CountryName,
137 PI.CityName, D.[FileName],T.Tier_ID as TierID, T.Security_level as Securitylevel,PC.Club_Name as ClubName FROM ',@PISql,char(10),
138        'left join (SELECT [File_Name] as [FileName],Doc_ID FROM GSAL_Document_Detail WHERE IsActive = 1 AND Doc_Type = ''DOCPH'') D 
139             on PI.DocID = D.Doc_ID         
140         left join (Select Lkp_Value,Lookup_Cd FROM GSRE_GenLKP) G on PI.Gender =G.Lookup_Cd
141         left join (Select Lkp_Value,Lookup_Cd FROM GSRE_GenLKP) M on PI.MembershipClass =M.Lookup_Cd
142         left join (Select Lkp_Value,Lookup_Cd FROM GSRE_GenLKP) I on PI.IDType =I.Lookup_Cd
143         left join (Select Country_ID,Country_Name FROM GSRE_Country) C on PI.CountryID =C.Country_ID
144         left join (Select Patron_ID,Tier_ID FROM GSPM_Patron_Tier WHERE IsActive=1) PT on PI.PatronID = PT.Patron_ID
145         join  (Select Tier_ID,Club_ID,Security_level FROM GSRE_Tier) T on PT.Tier_ID=T.Tier_ID
146         left join (Select Club_Name,Club_ID FROM GSRE_Club where IsActive=1) PC on T.Club_ID =  PC.Club_ID  ')
147 
148     Execute sp_executesql @rowCountSql, N'@rowCount int output', @TotalRow output;
149     
150     if((@TotalRow/(@PageSize * 2)) >= @Page)
151     begin        
152         set @pageSql =  REPLACE(@pageSql,'{0}',' asc ');
153         set @pageSql =  REPLACE(@pageSql,'{Start}',@Start);
154         set @pageSql =  REPLACE(@pageSql,'{End}',@End);
155     end
156     else
157     begin
158         set @pageSql =  REPLACE(@pageSql,'{0}',' desc ');
159         set @pageSql =  REPLACE(@pageSql,'{Start}',@TotalRow - @End + 1);
160         set @pageSql =  REPLACE(@pageSql,'{End}',@TotalRow - @Start + 1);        
161         set @pageSql = concat(' select * from (',@pageSql,') DE order by PatronNo asc');
162     end
163     exec (@pageSql)
164 END
View Code

无从表查询:

  1 USE [GEMS_DM]
  2 GO
  3 /****** Object:  StoredProcedure [dbo].[P_PM_SearchPatronByNoIdentifyCriteria]    Script Date: 12/23/2013 16:41:24 ******/
  4 SET ANSI_NULLS ON
  5 GO
  6 SET QUOTED_IDENTIFIER ON
  7 GO
  8 
  9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 ALTER PROCEDURE [dbo].[P_PM_SearchPatronByNoIdentifyCriteria]    
 18      @IsActive bit = NULL,
 19      @PatronNo bigint=NULL,
 20      @BirthDate date=NULL,
 21      @PNFirstName nvarchar(50)=NULL,
 22      @PNMiddleName nvarchar(50)=NULL,
 23      @PNLastName nvarchar(50)=NULL,
 24      @PreferredName nvarchar(40)=NULL,
 25      @EXFirstName nvarchar(50)=NULL,
 26      @EXLastName nvarchar(50)=NULL,
 27      @Gender nvarchar(5)=NULL,
 28      @MobileCountryCode nvarchar(5)=NULL,
 29      @Mobile nvarchar(15)=NULL,
 30      @Email nvarchar(30)=NULL,
 31      @MembershipClass nvarchar(30)=Null,    
 32      @DocType nvarchar(5)='',     
 33      @Page int = 1,
 34      @PageSize int = 10,
 35      @OrderSQL nvarchar(max)=null,
 36      @TotalRow int output
 37 
 38     
 39 
 40       -- Add the parameters for the stored procedure here    
 41 as
 42 BEGIN
 43 
 44 SET NOCOUNT ON;
 45 SET FMTONLY OFF  
 46 
 47 
 48 declare @Start nvarchar(20),@End nvarchar(20),@RowNumberSQL nvarchar(200),@BetweenSQL nvarchar(100),
 49 @rowCountSql nvarchar(max)
 50 IF (@Page is null) or (@Page <= 0)
 51     SET @Page = 1
 52 IF (@PageSize is null) or (@PageSize <=0)
 53     SET @PageSize = 10
 54 SET @Start=CONVERT(nvarchar(10),(@Page-1)*@PageSize+1);
 55 SET @End=CONVERT(nvarchar(10),(@Page)*@PageSize);
 56 Set @BetweenSQL = ' ROWNUMVE BETWEEN {Start} AND {End} ';
 57 set @RowNumberSQL = ' ROW_NUMBER() over (order by Patron_No {0} ) AS ROWNUMVE';
 58 
 59 
 60 declare @PatronTableSql nvarchar(max),@IdentificationTableSql nvarchar(max),@sqlwhere nvarchar(max),
 61 @FinalPatronTableSql nvarchar(max)
 62 
 63 set @PatronTableSql = 'SELECT Patron_ID AS PatronID,Patron_No AS PatronNo,PN_First_Name AS PNFirstName,PN_Middle_Name as PNMiddleName,PN_Last_Name AS PNLastName,
 64               Preferred_Name AS PreferredName,EX_First_Name AS EXFirstName,EX_Last_Name AS EXLastName,Birth_Date AS BirthDate,
 65               IsActive,IsMerged,Gender,Membership_Class AS MembershipClass,eMail,Mobile_Country_Code AS MobileCountryCode,
 66               Mobile_No AS MobileNo,Doc_ID as DocID {0} FROM GSMA_Patron ';
 67 if(@PatronNo is not null)
 68     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('Patron_No=',@PatronNo));
 69 if(@BirthDate is not null)
 70     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('Birth_Date=''',@BirthDate,''''));
 71 if(@IsActive is not null)
 72     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('IsActive=',@IsActive));
 73 if(@PNFirstName is not null)
 74     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('PN_First_Name like N''',@PNFirstName,'%'''));
 75 if(@PNMiddleName is not null)
 76     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('PN_Middle_Name like N''',@PNMiddleName,'%'''));
 77 if(@PNLastName is not null)
 78     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('PN_Last_Name like N''',@PNLastName,'%'''));
 79 if(@PreferredName is not null)
 80     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('Preferred_Name like N''',@PreferredName,'%'''));
 81 if(@EXFirstName is not null)
 82     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('EX_First_Name like N''',@EXFirstName+'%'''));
 83 if(@EXLastName is not null)
 84     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('EX_Last_Name like N''',@EXLastName,'%'''));
 85 if(@Gender is not null)
 86     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('Gender = N''',@Gender,''''));
 87 if(@MembershipClass is not null)
 88     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('Membership_Class in (''',Replace(@MembershipClass,',',''','''),''')'));
 89 if(@Email is not null)
 90     set @sqlwhere = dbo.F_CombineWhereSql(@sqlwhere,concat('eMail like N''',@Email+'%'''));
 91 
 92 if(@sqlwhere is not null and @sqlwhere <> '')
 93     set @PatronTableSql = concat(@PatronTableSql,' where ',@sqlwhere);
 94     
 95 -- remove template string and get total account from patron search
 96 set @rowCountSql = concat('Select @rowCount = count(1)  from (',REPLACE(@PatronTableSql,'{0}',''),') WS ');
 97 -- add paging rownumber column when select Patron
 98 set @PatronTableSql = REPLACE(@PatronTableSql,'{0}',concat(',',@RowNumberSQL));    
 99 -- set final patron table search sql
100 set @FinalPatronTableSql = concat('Select PatronID,PatronNo,PNFirstName,PNMiddleName,PNLastName,PreferredName,EXFirstName,
101                 EXLastName,BirthDate,IsActive,IsMerged,Gender,MembershipClass,eMail,MobileCountryCode,MobileNo,DocID 
102                  From (',@PatronTableSql,') P  WHERE ',@BetweenSQL);
103 set @FinalPatronTableSql = concat('(',@FinalPatronTableSql,') A ');
104 
105 
106 set @IdentificationTableSql = '(SELECT ID_Type as IDType,ID_No as IDNo,Patron_ID,
107 Country_ID AS CountryID,City_Name as CityName,ISNULL(Modified_Date,Created_Date) as SortDate FROM GSMA_Patron_Identification WHERE IsActive = 1) B';
108 
109 
110 declare @PISql nvarchar(max)
111 set @PISql = concat('(Select PatronID,PatronNo,PNFirstName,PNMiddleName,PNLastName,PreferredName,EXFirstName,
112 EXLastName,BirthDate,IsActive,IsMerged,Gender,MembershipClass,eMail,MobileCountryCode,MobileNo,DocID,
113 IDType,IDNo,CountryID,CityName From
114 (Select A.*,B.IDType,B.IDNo,B.CountryID,B.CityName,
115 ROW_NUMBER() OVER(PARTITION BY PatronID ORDER BY SortDate DESC) AS RK from '
116 ,char(10),@FinalPatronTableSql,char(10),' Left join ',char(10),@IdentificationTableSql,'
117  ON A.PatronID = B.Patron_ID) PLI WHERE RK=1 ) PI')
118 
119 declare @pageSql nvarchar(max) = concat('SELECT PI.PatronID,PI.PatronNo,PI.PNFirstName,PI.PNMiddleName,PI.PNLastName,PI.PreferredName,PI.EXFirstName,
120 PI.EXLastName,PI.BirthDate,G.Lkp_Value as GenderName,PI.MembershipClass,M.Lkp_Value as MembershipClassName,
121 PI.MobileCountryCode,PI.MobileNo,PI.eMail,PI.IsActive,PI.IsMerged,I.Lkp_Value as IDTypeName,PI.IDNo, C.Country_Name as CountryName,
122 PI.CityName, D.[FileName],T.Tier_ID as TierID, T.Security_level as Securitylevel,PC.Club_Name as ClubName FROM ',@PISql,char(10),
123        'left join (SELECT [File_Name] as [FileName],Doc_ID FROM GSAL_Document_Detail WHERE IsActive = 1 AND Doc_Type = ''DOCPH'') D 
124             on PI.DocID = D.Doc_ID         
125         left join (Select Lkp_Value,Lookup_Cd FROM GSRE_GenLKP) G on PI.Gender =G.Lookup_Cd
126         left join (Select Lkp_Value,Lookup_Cd FROM GSRE_GenLKP) M on PI.MembershipClass =M.Lookup_Cd
127         left join (Select Lkp_Value,Lookup_Cd FROM GSRE_GenLKP) I on PI.IDType =I.Lookup_Cd
128         left join (Select Country_ID,Country_Name FROM GSRE_Country) C on PI.CountryID =C.Country_ID
129         left join (Select Patron_ID,Tier_ID FROM GSPM_Patron_Tier WHERE IsActive=1) PT on PI.PatronID = PT.Patron_ID
130         join  (Select Tier_ID,Club_ID,Security_level FROM GSRE_Tier) T on PT.Tier_ID=T.Tier_ID
131         left join (Select Club_Name,Club_ID FROM GSRE_Club where IsActive=1) PC on T.Club_ID =  PC.Club_ID  ')
132         
133     Execute sp_executesql @rowCountSql, N'@rowCount int output', @TotalRow output;
134     
135     if((@TotalRow/(@PageSize * 2)) >= @Page)
136     begin
137         set @pageSql =  REPLACE(@pageSql,'{0}',' asc ');
138         set @pageSql =  REPLACE(@pageSql,'{Start}',@Start);
139         set @pageSql =  REPLACE(@pageSql,'{End}',@End);
140     end
141     else
142     begin
143         set @pageSql =  REPLACE(@pageSql,'{0}',' desc ');
144         set @pageSql =  REPLACE(@pageSql,'{Start}',@TotalRow - @End + 1);
145         set @pageSql =  REPLACE(@pageSql,'{End}',@TotalRow - @Start + 1);        
146         set @pageSql = concat(' select * from (',@pageSql,') DE order by PatronNo asc');        
147     end
148     exec (@pageSql)
149 END
View Code

顺带最后一提:动态SQL的性能和普通SQL没区别

原文地址:https://www.cnblogs.com/vincentsun1234/p/3487690.html