Оптимизация динамической сортировки SQL-запроса - PullRequest
0 голосов
/ 21 февраля 2019

Этот запрос (я имитировал его, чтобы показать вам. В противном случае он длиннее с большим количеством списков столбцов, объединением нескольких таблиц и, наконец, сортировкой по каждому столбцу, как того требует пользователь), занимает слишком много времени, иможет время ожидания из интерфейса иногда.Я пытался оптимизировать хранимую процедуру, чтобы сократить время загрузки, не затрагивая извлеченные списки.Я подумывал выбрать * из временной таблицы, а затем заказать оттуда.Это может как-то помочь.Я также рассмотрел использование динамического 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 голосов
/ 22 февраля 2019

Я не за компьютером, поэтому я не смогу добавить код до тех пор, пока вы не предоставите мне достаточно информации, чтобы дать мне хорошее представление.

Во-первых, оптимизатор никогда не сможетиспользовать индекс для предотвращения сортировки при заказе с помощью оператора CASE.Тем не менее, есть много причин, по которым ваш запрос может быть медленным;Вы должны удалить эту логику, а затем протестировать производительность, чтобы понять, является ли это виновником.

Исходя из ограниченной информации, я предлагаю обновить ваш proc: 1. Использовать динамический SQL для построения вашей временной таблицы, так как высделали, но без заявления CASE.

В этот динамический SQL ^^^ должен быть включен код для построения кластеризованного индекса с @columnName в качестве ключа кластера и использованием @sortDir для определения порядка сортировки индекса.

Теперь у вас естьвременная таблица правильно проиндексирована на основе предоставленных пользователем параметров.

Добавьте второй динамический оператор SQL, который выполняет SELECT * FROM #temp ... Вы снова будете использовать @columnName и @sortOrder для построения SARGable ORDER BY (который будет использовать индекс из шага # 1).

Тестирование с различными параметрами, всегда отслеживая план фактического выполнения для дальнейшей настройки ваших индексов.

Надеюсь, это поможет. Я добавлю демонстрациюкод немного позже, если вам нужно.

Обновлено 22.02.2009, чтобы включить примеры:

Вот хранимый процесс, который делает то, что я описываю (примечаниекомментарии) -

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';

В каждом случае они сортируются, как ожидалось, и в выходном запросе сортировка отсутствует.

...