1

Я использую SQL Server 2008 и выполнив следующую хранимую процедуру, которая должна «чистой» таблицы 70 мельницы от приблизительно 50 мельничных строк в другой таблице, id_col является integer (первичный ключ идентификации)Efficient SQL Server хранимые процедуры

Согласно последнему бега я сделал это работает хорошо, но, как ожидается, продлится около 200 дней:

SET NOCOUNT ON 

    -- define the last ID handled 
    DECLARE @LastID integer 
    SET @LastID = 0 
    declare @tempDate datetime 
    set @tempDate = dateadd(dd,-20,getdate()) 
    -- define the ID to be handled now 
    DECLARE @IDToHandle integer 
    DECLARE @iCounter integer 
    DECLARE @watch1 nvarchar(50) 
    DECLARE @watch2 nvarchar(50) 
    set @iCounter = 0 
    -- select the next to handle  
    SELECT TOP 1 @IDToHandle = id_col 
    FROM MAIN_TABLE 
    WHERE id_col> @LastID and DATEDIFF(DD,someDateCol,otherDateCol) < 1 
     and datediff(dd,someDateCol,@tempDate) > 0 and (some_other_int_col = 1745 or some_other_int_col = 1548 or some_other_int_col = 4785) 
    ORDER BY id_col 

    -- as long as we have s......  
    WHILE @IDToHandle IS NOT NULL 
    BEGIN 
     IF ((select count(1) from SOME_OTHER_TABLE_THAT_CONTAINS_20k_ROWS where some_int_col = @IDToHandle) = 0 and (select count(1) from A_70k_rows_table where some_int_col [email protected])=0) 
     BEGIN 
      INSERT INTO SECONDERY_TABLE 
      SELECT col1,col2,col3..... 
      FROM MAIN_TABLE WHERE id_col = @IDToHandle 

      EXEC [dbo].[DeleteByID] @ID = @IDToHandle --deletes the row from 2 other tables that is related to the MAIN_TABLE and than from the MAIN_TABLE 
      set @iCounter = @iCounter +1 
     END 
     IF (@iCounter % 1000 = 0) 
     begin 
      set @watch1 = 'iCounter - ' + CAST(@iCounter AS VARCHAR) 
      set @watch2 = 'IDToHandle - '+ CAST(@IDToHandle AS VARCHAR) 
      raiserror (@watch1, 10,1) with nowait 
      raiserror (@watch2, 10,1) with nowait 
     end 
     -- set the last handled to the one we just handled 
     SET @LastID = @IDToHandle 
     SET @IDToHandle = NULL 

     -- select the next to handle  
     SELECT TOP 1 @IDToHandle = id_col 
     FROM MAIN_TABLE 
     WHERE id_col> @LastID and DATEDIFF(DD,someDateCol,otherDateCol) < 1 
      and datediff(dd,someDateCol,@tempDate) > 0 and (some_other_int_col = 1745 or some_other_int_col = 1548 or some_other_int_col = 4785) 
     ORDER BY id_col 
    END 

Любые идеи или направления для улучшения этой процедуры во время выполнения будет приветствоваться

+5

Хммм ... это похоже на очень процедурный подход к тому, что должно быть проблемой на основе набора. Вам нужно перестать думать в терминах по очереди, а вместо этого использовать способность Sql работать очень эффективно против наборов данных. –

+0

У вас есть триггеры для задействованных таблиц? Если вы это сделаете, эти триггеры могут заставить все заняться гораздо дольше. –

+5

Похоже, это единственное, что заставляет вас делать это [row-by-agonizing-row] (https://www.simple-talk.com/sql/t-sql-programming/rbar--row-by -agonizing-row /) является хранимой процедурой 'DeleteByID' ... можете ли вы включить определение этой хранимой процедуры, чтобы затем ее можно было включить в решение на основе набора? –

ответ

3

Да , попробуйте thi с:

Declare @Ids Table (id int Primary Key not Null) 
Insert @Ids(id) 
Select id_col 
From MAIN_TABLE m 
Where someDateCol >= otherDateCol 
    And someDateCol < @tempDate -- If there are times in these datetime fields, 
           -- then you may need to modify this condition. 
    And some_other_int_col In (1745, 1548, 4785) 
    And Not exists (Select * from SOME_OTHER_TABLE_THAT_CONTAINS_20k_ROWS 
        Where some_int_col = m.id_col) 
    And Not Exists (Select * From A_70k_rows_table 
        Where some_int_col = m.id_col) 
Select id from @Ids -- this to confirm above code generates the correct list of Ids 
return -- this line to stop (Not do insert/deletes) until you have verified @Ids is correct 
-- Once you have verified that above @Ids is correctly populated, 
-- then delete or comment out the select and return lines above so insert runs. 

     Begin Transaction 
     Delete OT  -- eliminate row-by-row call to second stored proc 
     From OtherTable ot 
     Join MAIN_TABLE m On m.id_col = ot.FKCol 
     Join @Ids i On i.Id = m.id_col 

     Insert SECONDERY_TABLE(col1, col2, etc.) 
     Select col1,col2,col3..... 
     FROM MAIN_TABLE m Join @Ids i On i.Id = m.id_col 

     Delete m -- eliminate row-by-row call to second stored proc 
     FROM MAIN_TABLE m 
     Join @Ids i On i.Id = m.id_col 

     Commit Transaction 

Объяснение.

  1. Вы имели многочисленные условия фильтрации, которые не были SARGable, то есть, они заставили бы полное сканирование таблицы для каждой итерации цикла вашего, вместо того, чтобы быть в состоянии использовать любой существующий индекс. Всегда старайтесь избегать условий фильтра, которые применяют логику обработки к значению столбца таблицы, прежде чем сравнивать его с каким-либо другим значением. Это устраняет возможность использования оптимизатором запросов индекса.

  2. Вы выполняли вставки по одному ... Лучше сгенерировать список идентификаторов PK, которые необходимо обработать (все сразу), а затем делать все вставки сразу, в одном выражении.

+0

ну, кажется, хорошо, я запускаю проверки и отправлю обратно, если все работает нормально, в любом случае спасибо много –

+0

Ну, я попытался запустить это для небольшого объема информации сначала (внутри цикла, где я прошел процесс за каждые 100000 но это сделало мой db недоступным и после того, как я попытался за каждые 1000 строк, и все же он сделал это за несколько секунд недоступным для доступа SECONDERY_TABLE до и других связанных таблиц. Только перезапуск службы очистил его ... –

+0

Возможно, мне нужно выполните что-то отсюда: http://stackoverflow.com/questions/15480699/running-large-queries-in-the-backgroud-ms-sql –

Смежные вопросы