优化动态排序SQL查询

时间:2019-02-21 17:18:33

标签: sql tsql stored-procedures optimization query-optimization

此查询(我模拟给您看了。否则,它会更长,包含更多的列列表,更多的表;最后要根据用户的需要按每列排序)花费的时间太长,并且有时会从UI超时。我正在尝试优化存储过程,以减少加载时间,而不影响检索到的列表。我正在考虑从临时表中选择*,然后从那里订购。这可能会有所帮助。我也考虑过使用动态sql而不指定@sortidr(DESC / ASC),但是用户应该可以灵活地选择两种方式。

我想知道是否有人可以提出更好的优化方法。

CREATE PROCEDURE get_lists   
    @employeeid  int
    @ColumnName varchar(100) = NULL,
    @sortidr varchar(4) = NULL, 
    @start_date datetime = NULL

AS

CREATE #tempemp
(emp_id int, first_name varchar(20), last_name varchar(20), SSN int) 

INSERT INTO #tempemp
    SELECT team_action, rec_id, total_records, cfirst_name, efirst_name, has_desc, category_name
    FROM 
        (SELECT COALESCE(lt.description, lt.action) AS team_action
        ,cl.rec_id
        ,total_records = count(*) Over()
        ,c.cfirst_name
        ,e.efirst_name
        , has_desc = cld.changelog_id IS NOT NULL THEN 1 ELSE 0 END
        , ct.category_name
        FROM claimll cll 
        JOIN itemtype lt ON cll.clLog_id = lt.clLog_id 
        LEFT JOIN categgory ct ON ct.id=clc.cat_id
        LEFT JOIN clients c ON ct.client_id = c.client_id 
        LEFT JOIN employees e ON cll.emp_id = e.emp_id 
        LEFT JOIN detail d ON d.change_id = cll.change_id
        WHERE cll.change_date > @start_date
        AND cll.emp_id = @employeeid OR cll.by_employeeid  = @employeeid 
        ) t 

ORDER BY
      CASE WHEN @sortidr = 'asc' AND @ColumnName='team_action' THEN team_action END, 
      CASE WHEN @sortidr = 'desc' AND @ColumnName = 'team_action' THEN team_action END DESC, 
      CASE WHEN @sortidr = 'asc' AND @ColumnName='rec_id' THEN  rec_id END, 
      CASE WHEN @sortidr = 'desc' AND @ColumnName = 'rec_id' THEN rec_id END DESC, 
      CASE WHEN @sortidr = 'asc' AND @ColumnName='total_records' THEN  total_records END, 
      CASE WHEN @sortidr = 'desc' AND @ColumnName = 'total_records' THEN total_records END DESC, 
      CASE WHEN @sortidr = 'asc' AND @ColumnName='cfirst_name' THEN  cfirst_name END, 
      CASE WHEN @sortidr = 'desc' AND @ColumnName = 'cfirst_name' THEN cfirst_name END DESC, 
      CASE WHEN @ColumnName IS NULL THEN first_name END DESC

1 个答案:

答案 0 :(得分:1)

我不在PC上,因此稍后我将无法添加代码,但是您为我提供了足够的信息,可以提供一些很好的见识。

首先-当您通过CASE语句进行排序时,优化器将永远无法利用索引来防止排序。就是说,您的查询可能很慢的原因很多;您应该删除该逻辑,然后测试性能以了解是否是罪魁祸首。

基于有限的信息,我建议将您的proc更新为: 1.与您一样,使用动态SQL构建临时表,但不使用CASE语句。

  1. 此动态SQL中包括的代码应为使用@columnName作为聚簇键并使用@sortDir指示索引排序顺序的聚簇索引的代码。

现在,您已经有一个基于用户提供的参数正确索引的临时表。

  1. 添加第二个执行SELECT * FROM #temp ...的动态SQL语句。您将再次使用@columnName和@sortOrder构建SARGable ORDER BY(一个将利用步骤1中的索引的语句)。

  2. 使用不同的参数进行测试,请始终查看“实际执行”计划以进一步调整索引。

希望这会有所帮助。如果需要的话,我会稍后添加演示代码。

已于2019/2/22更新,包括示例:

这是一个存储过程,执行我正在描述的操作(请注意注释)-

SET NOCOUNT ON;
USE tempdb
GO

IF OBJECT_ID('dbo.DynamicSortProc','P') IS NOT NULL DROP PROC dbo.DynamicSortProc;
GO
CREATE PROC dbo.DynamicSortProc @sortby NVARCHAR(100), @sortorder NVARCHAR(4)
AS 
BEGIN 
  -- 1. Populate the temp table (sample data)
  IF OBJECT_ID('##tmp','U') IS NOT NULL DROP TABLE ##tmp;

  SELECT   SomeId = IDENTITY(INT,1,1), ColA, ColB
  INTO     ##tmp
  FROM     (VALUES(1,3),(6,5),(2,8),(5,5),(2,6),(5,1)) AS f(ColA,ColB);
  -- NOTE: On SQL 2014+ SELECT INTO is often faster than the standard create+insert
  ;
  -- 2. Setup @sortby
  SET @sortby = QUOTENAME(@sortby)+' '+CASE @sortorder WHEN 'DESC' THEN 'DESC' ELSE '' END;

  -- 3. Set up the Dynamic SQL
  DECLARE @createIndex  NVARCHAR(4000) = 'CREATE CLUSTERED INDEX x ON ##tmp ('+@sortby+') ';
  DECLARE @executeQuery NVARCHAR(4000) = 'SELECT t.* FROM ##tmp AS t ORDER BY '+@sortby;

  -- 4. Execute the queries
  EXEC sys.sp_executesql @createIndex;
  EXEC sys.sp_executesql @executeQuery;
END
GO

您可以这样测试:

EXEC dbo.DynamicSortProc 'ColA', 'ASC';
EXEC dbo.DynamicSortProc 'ColA', 'DESC';
EXEC dbo.DynamicSortProc 'ColB', 'ASC';
EXEC dbo.DynamicSortProc 'ColB', 'DESC';

在每种情况下,它们均按预期进行排序,并且输出查询中没有排序。