【问题标题】:how to perform sorting and filtering in stored procedure with performance optimization?如何通过性能优化在存储过程中执行排序和过滤?
【发布时间】:2015-06-26 19:05:22
【问题描述】:

我想在我的存储过程中执行排序和过滤。

我为假期表创建表:

CREATE TABLE [dbo].[Holiday](
    [HolidaysId] [int] IDENTITY(1,1) NOT NULL,
    [HolidayDate] [date] NULL,
    [HolidayDiscription] [nvarchar](500) NULL,
    [Name] [nvarchar](max) NULL,
 CONSTRAINT [PK_Holiday] PRIMARY KEY CLUSTERED 
(
    [HolidaysId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

我的过滤条件如下:

  1. 开始于
  2. 等于
  3. 不等于。

注意:过滤器比较时请忽略HolidayId。

我的餐桌:假期

HolidaysId int,Name nvarchar(500),HolidayDate date.

示例输入:

HolidayId       Name       Date
 1               abc       1/1/2015
 2               pqr       1/2/2015
 3               xyz       1/3/2015

输出:

Case 1:Starts with(This is just for name column only.likewise i want to do for HolidayDate column too)
Input:ab(filtering parameter)
Query:where Name like '%ab%' order by Name(when sort column name is  passed as parameter in stored procedure for column to sort(for eg:Name))
output:1,abc,1/1/2015

Case 2:Is Equal to(Same as above)
Input:prr(filtering parameter)
output:2,pqr,1/2/2015

Case 3:Not Equal to(Same as above)
Input:bbb(filtering parameter)
output:All Records

这是我目前的存储过程:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sp_PagedItems]
    (
     @Page int,
     @RecsPerPage int
    )
AS

-- We don't want to return the # of rows inserted
-- into our temporary table, so turn NOCOUNT ON
SET NOCOUNT ON


--Create a temporary table
CREATE TABLE #TempItems
(
    ID int,
    Name varchar(50),
    HolidayDate date
)


-- Insert the rows from tblItems into the temp. table
INSERT INTO #TempItems (ID, Name,HolidayDate)
SELECT HolidaysId,HolidayDiscription,HolidayDate FROM holiday 

-- Find out the first and last record we want
DECLARE @FirstRec int, @LastRec int
SELECT @FirstRec = (@Page - 1) * @RecsPerPage
SELECT @LastRec = (@Page * @RecsPerPage + 1)

-- Now, return the set of paged records, plus, an indiciation of we
-- have more records or not!
SELECT *,
       MoreRecords = 
    (
     SELECT COUNT(*) 
     FROM #TempItems TI
     WHERE TI.ID >= @LastRec
    ) 
FROM #TempItems
WHERE ID > @FirstRec AND ID < @LastRec


-- Turn NOCOUNT back OFF
SET NOCOUNT OFF

现在我要发送到我的存储过程的 4 件事是:

  1. 页码
  2. PageSize(要检索的记录数)
  3. 排序列名称(名称或假日日期)
  4. 我的过滤器列名(假日日期的名称)和运算符,如 StartWith 或等于或不等于。(列名和运算符)

谁能帮我执行排序和过滤,如果有任何与性能优化相关的更改,请给我建议。

【问题讨论】:

  • 我不确定您要在这里实现什么,我认为一些示例数据和预期结果可能会有所帮助。将整个表放入临时表然后使用它似乎也很奇怪。为什么不只使用原始表?最后,you should not prefix your stored procedures with sp_
  • 请编辑您的问题,以使预期的输出更加清晰。
  • 您是否在数据库上设置了任何索引?
  • @GarethD:请查看我更新的问题
  • 但是您的存储过程不采用任何与您描述的过滤或排序相关的参数...如果没有它,您希望如何过滤数据?顺便提一句。将整个 Holiday 表插入 temp 表只是浪费内存和电力 - 只有在插入前过滤数据时,这样的操作才有意义。

标签: sql sql-server select stored-procedures sql-server-2008-r2


【解决方案1】:

我没有对此进行测试,但是您可以将类似的东西用作启动器并进行修改以使其稳定:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[sp_PagedItems]
    (
    @ID int = NULL,
    @Name varchar(50) = NULL,
    @HolidayDate date = NULL,
    @SortCol varchar(20) = '',
     @Page int=1,
     @RecsPerPage int=10 -- default size, you can change it or apply while executing the SP
    )
AS
BEGIN
-- We don't want to return the # of rows inserted
-- into our temporary table, so turn NOCOUNT ON
SET NOCOUNT ON


--Create a temporary table
CREATE TABLE #TempItems
(
    ID int,
    Name varchar(50),
    HolidayDate date
)


-- Insert the rows from tblItems into the temp. table
INSERT INTO #TempItems (ID, Name,HolidayDate)
SELECT HolidaysId, HolidayDiscription, HolidayDate 
FROM holiday 

-- Find out the first and last record we want
DECLARE @FirstRec int, @LastRec int
SELECT @FirstRec = (@Page - 1) * @RecsPerPage
SELECT @LastRec = (@Page * @RecsPerPage + 1)

-- Now, return the set of paged records, plus, an indiciation of we
-- have more records or not!
; WITH CTE_Results
AS (
    SELECT ROW_NUMBER() OVER (ORDER BY
        CASE WHEN @SortCol = 'ID_Asc' THEN ID
            END ASC,
        CASE WHEN @SortCol = 'ID_Desc' THEN ID
            END DESC, 

        CASE WHEN @SortCol = 'Name_Asc' THEN Name
            END ASC,
        CASE WHEN @SortCol = 'Name_Desc' THEN Name
            END DESC, 

        CASE WHEN @SortCol = 'HolidayDate_Asc' THEN HolidayDate
            END ASC,
        CASE WHEN @SortCol = 'HolidayDate_Desc' THEN HolidayDate
            END DESC
        ) AS ROWNUM,
        ID,
        Name,
        HolidayDate
    FROM #TempItems
    WHERE 
            (@ID IS NULL OR ID = @ID)
        AND (@Name IS NULL OR Name LIKE '%' + @Name + '%')
        AND (@HolidayDate IS NULL OR HolidayDate = @HolidayDate)
    )
SELECT 
        ID,
        Name,
        HolidayDate
    FROM CTE_Results
    WHERE
        ROWNUM > @FirstRec 
    AND ROWNUM < @LastRec
    ORDER BY ROWNUM ASC

-- Turn NOCOUNT back OFF
SET NOCOUNT OFF
END
GO

你可以查看我写过的博文:

  1. Creating Stored Procedures with Dynamic Search (filter)

  2. Creating Stored Procedures with Dynamic Search & Paging (Pagination)

  3. Creating Stored Procedure with Dynamic Search, Paging and Sorting

  4. 如果您使用 SQL 2012 或更高版本,也可以使用 FETCH-OFFSET 子句进行分页,link

【讨论】:

  • 能否请您为我现有的存储过程提供任何解决方案,并尽可能考虑性能
  • 添加了 SP 的代码,检查一下,但你需要稍微调整一下,因为我无法测试它。
  • 但是您认为这里需要 CTE 吗,并且 cmets 中的一位成员建议不需要使用临时表和浪费内存,但由于您为我所做的努力,我仍然会支持您的回答跨度>
  • 这里你已经修复了列名,如果假设我将来会在我的假期表中添加 1 列并想对该列执行排序和过滤然后我必须再次更改这个 sp (表示你的 sp)。
  • 可以,不需要使用#temp Table,可以直接从holiday table中获取。
【解决方案2】:

这就是我所做的,我得到了预期的输出,但如果有任何改进建议,我仍然想向大家提出改进建议。

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[HolidayFetchList]
@pageno int,
@pagesize int,
@sortorder varchar(10),
@sortcolumn varchar(100),
@filter varchar(max),
@count int OUTPUT
AS
BEGIN
    declare @Start int=(@pageno)*@pagesize;
    declare @End int=@Start+@pagesize;
    SET NOCOUNT ON;

    DECLARE @tblHoliday AS TABLE
        (HolidaysId int,HolidayDate date,HolidayDiscription nvarchar(500),HolidayName nvarchar(max),RN int)

    declare @sql varchar(max)= '
    select HolidaysId,HolidayDate,HolidayDiscription,HolidayDiscription as HolidayName,ROW_NUMBER() OVER 
                     (ORDER BY '+@sortcolumn + ' '+@sortorder+' ) AS RN from Holiday
     WHERE 1=1 '+@filter
     print @sql
INSERT INTO @tblHoliday
exec (@sql)
select  @count=COUNT(*) from @tblHoliday
print @count
select * from @tblHoliday where RN>@Start and RN<=@End order by RN 
END

如果你有任何建议,请给我任何建议。

【讨论】:

    【解决方案3】:

    不建议使用 #temp 表,因为您会影响服务器上的 RAM。但是,坏消息:(,你也不应该使用 exec 命令......现在你很容易在你的应用程序中受到 SQL 注入的影响。所以,我认为至少有两个选择:1)使用视图(包括标志值),表值函数和其他组件; 2)WHERE语句内部过滤如下图:

    SELECT * FROM Holiday
    WHERE
    CASE WHEN @paramStartDate Is Not Null THEN HolidayDate ELSE '' END
    >= CASE WHEN @paramStartDate Is Not Null THEN @paramStartDate ELSE '' END
    AND
    CASE WHEN @paramEndDate Is Not Null THEN HolidayDate ELSE '' END
    <= CASE WHEN @paramEndDate Is Not Null THEN @paramEndDate ELSE '' END
    AND
    CASE WHEN @paramName Is Not Null THEN [Name] ELSE '' END
    LIKE CASE WHEN @paramName Is Not Null THEN '%' + @paramName + '%' ELSE ''  END
    

    您应该记住,这种方法可以增加处理时间。如果是这样,您可以创建多个存储过程,一个用于 HolidayDate 搜索,另一个用于名称搜索,另一个用于组合过滤器。您的应用程序必须能够根据输入参数决定使用哪一个。

    【讨论】:

      【解决方案4】:

      对于分页(临时报告),我会使用 OFFSET 和 FETCH。使用 T-SQL 的一些优势,那么您将不需要临时表和任何混乱。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多