Не раз слышал мнения 1сников, что переиндексации вообще не нужна. Так как мы живем сейчас в век ssd и nvme. И не важно какая у вас фрагментация индекса нужно просто делать обновление статистики и все. И как подтверждение кидают абзац от Microsoft

Но почему – то не читают начало этой статьи, в которой сказано, как влияет фрагментация и плотность страниц на общую производительность. И расход ресурсов

И то что даже на ssd и NMVE последовательная скорость чтения запись всегда выше чем случайная

И про плотность, (если у вас конечно на сервере на 2ТБ памяти на базу 2ТБ)

Подытожим. Обновление статистики и правда даст вам сиюминутный эффект. Но со временем фрагментация сделает свое, и вам не хватает уже мощностей железа что бы перекрыть тормоза.

Так что мое мнение переиндексация нужна! Но не нужно каждый день перестраивать все индексы) Благо всевозможный скриптов хватает в сети.

Идея появилась из-за проблемы, что база в 20ТБ не могла за выделение тех окно пройти «полную» переиндексацию. И через неделю-две начинались дикие тормоза и обновление статистики не давало явного ускорения. Пробовал чужие скрипты с паузой и прочие. Но во всех скриптах перестройка индексов идет в один поток (я говорю не про maxdop). Что пока один индекс не перестроиться, другие не начнут.  Так что вот представляю мой костыль)

Скрипт представляю в немного урезанном виде и на 2 потока.

Шаги в скрипте.

  1. Собрать статистику по всем индексам в базе их фрагментации и размеру. Занести данные в служебную базу.

  2. Выполнить задание перестройки по первой половине данных

  3. Выполнить задание перестройки по второй половине данных

  4. Обновить статистку, по все базе.

Шаги 2 и 3 выполняются параллельно.

Зачем, вы спросите, обновлять статистику по всей базе если переиндексация обновляет статистику? А я отвечу. Мы перестраиваем не все таблицы. А только что перестроенные индексы (при условии, что не было еще ни одной вставки) будут пропущены с сообщение, что нечего там обновлять. Это проще чем делать еще один шаг с условием. Что если переиндексация по таблице не проходила, то обнови статистику.

Можно все сделать разными jobs с привязкой старта, разным логом и прочим. Но так как это lite версия скрипта, пример будет создан на maintenance Plan.

Так как я встречал моветон, имена БД через '–' а не '_', что заставляет городить конструкции с выделением имени БД в несколько кавычек (так как – в скриптах это может быть как действием так и символом).

 Поэтому в скрипте есть 2 разные «переменные» DATA-BASE(Имя базы) и DATA_BASE(Служебная переменная) Во всех файлах необходимо заменить  DATA-BASE имя вашей БД (к примеру SP-UPP), DATA_BASE заменить на имя базы без '-' (SPUPP или SP_UPP)

Скрипт на первом шаге, создает служебную базу profiler (если ее нет). Собираем статистику по индексам и заполняем базу profiler. Сбор статистики достаточно долгий процесс, который не сильно влияет на скорость. То есть 1 шаг лучше запускать заранее. К примеру тех окно у вас с 21:00 часа первый шаг у вас выполняется за 40 мин и не мешает пользователям, то статистику можно начать собирать в 20:20.

 1.

SET QUOTED_IDENTIFIER ON;

if DB_ID('profiler') IS NULL
BEGIN
 PRINT 'Creating Profiler database'
 CREATE DATABASE Profiler
END

declare @astor_name VARCHAR(255)
declare @astor_id INT
SELECT TOP 1 @astor_name = QUOTENAME(name), @astor_id=database_id FROM sys.databases where name like 'DATA-BASE'
DECLARE @rebuildOptions nvarchar(MAX) = N' WITH (maxdop = 10, ONLINE = ON, SORT_IN_TEMPDB = ON)'
DECLARE @partitionnum bigint;
DECLARE @partitions bigint;
DECLARE @objectid int;
DECLARE @indexid int;
DECLARE @partitioncount bigint;

PRINT 'Woring with ' + @astor_name 

DECLARE @indexTable VARCHAR(255) 
SET @indexTable= @astor_name + '.sys.indexes'

if OBJECT_ID('temporary_indexes_DATA_BASE') IS NOT NULL 
 DROP VIEW temporary_indexes_DATA_BASE;

EXEC('CREATE VIEW temporary_indexes_DATA_BASE AS 
  SELECT 
    idx.object_id as object_id, 
    idx.index_id as index_id,
    sch.name as schema_name,
    obj.name as table_name,
    idx.name as index_name,
    idx.type_desc as type_desc 
   FROM ' + @astor_name + '.sys.indexes as idx
   JOIN '  + @astor_name + '.sys.objects as obj ON obj.object_id = idx.object_id
   JOIN '  + @astor_name + '.sys.schemas as sch ON sch.schema_id = obj.schema_id')

DECLARE @FramentationReportTable VARCHAR(255) 
SET @FramentationReportTable = 'Fragmentation_DATA_BASE_'+REPLACE(convert(varchar, getdate(), 102), '.', '_')

IF OBJECT_ID('tempdb..##fragmentation_DATA_BASE') IS NOT NULL
 DROP TABLE ##fragmentation_DATA_BASE

DECLARE @reportNum INT 
SET @reportNum = 1

WHILE OBJECT_ID('[Profiler].dbo.' + @FramentationReportTable+'_' + CAST(@reportNum AS nvarchar(255))) IS NOT NULL  
 SELECT @reportNum = @reportNum + 1;

SELECT @FramentationReportTable = @FramentationReportTable + '_' + CAST(@reportNum AS nvarchar(255))

DROP SEQUENCE IF EXISTS Sequence;
CREATE SEQUENCE Sequence
    START WITH 1  
    INCREMENT BY 1  
    MINVALUE 1  
    MAXVALUE 2  
    CYCLE  

RAISERROR( N'Analyzing indexes',0,1) WITH NOWAIT
SELECT
 DB_NAME(stats.database_id) as db_name,
 idx.schema_name as schema_name,
 idx.table_name AS table_name,
    idx.index_name AS index_name,
 idx.type_desc as index_type,
    stats.partition_number AS partition_num,
    stats.avg_fragmentation_in_percent AS fragmentation,
 stats.avg_page_space_used_in_percent as page_fullness,
 stats.avg_record_size_in_bytes as record_size,
 stats.record_count as rows_count,
 stats.page_count as page_count
 --- ,next value for Sequence over (order by [record_count] desc) as num
INTO ##fragmentation_DATA_BASE
FROM sys.dm_db_index_physical_stats (@astor_id, NULL, NULL , NULL, 'SAMPLED') as stats
JOIN temporary_indexes_DATA_BASE idx ON idx.object_id = stats.object_id and idx.index_id = stats.index_id
DROP VIEW temporary_indexes_DATA_BASE

EXEC ('SELECT [db_name]
      ,[schema_name]
      ,[table_name]
      ,[index_name]
      ,[index_type]
      ,[partition_num]
      ,[fragmentation]
      ,[page_fullness]
      ,[record_size]
      ,[rows_count]
      ,[page_count]
   ,next value for Sequence over (order by ROUND ([fragmentation],0 ) desc, [rows_count] desc) as num INTO profiler.dbo.'+ @FramentationReportTable +' FROM ##fragmentation_DATA_BASE')

2 и 3 шаги почти идентичные кроме условия условий выбора и названия курсора.

2.    

DECLARE @FramentationReportTable_1 VARCHAR(255) 
SET @FramentationReportTable_1 = (SELECT top 1 table_name FROM Profiler.INFORMATION_SCHEMA.TABLES  WHERE table_Name LIKE 'Fragmentation_DATA_BASE_'+REPLACE(convert(varchar, getdate(), 102), '.', '_') + '%')
print @FramentationReportTable_1

exec ('
DECLARE bad_indexes_1 CURSOR FOR 
 
 select
 frag.db_name,
 frag.schema_name,
 frag.table_name,
 frag.index_name,
 frag.partition_num,
 case
  when frag.record_size*16 <= 403 then 95
  when frag.record_size*16 <= 806 then 90
  when frag.record_size*16 <= 1209 then 85
  else 80
 end as suggested_fillfactor
 
 
 from Profiler.dbo.'+ @FramentationReportTable_1 +' frag
 where frag.page_count > 24 and frag.fragmentation >= 5  and frag.num = 1  and frag.index_type <>''HEAP''
 order by ROUND ([fragmentation],0 ) desc, [rows_count] desc')


-- Open the cursor.
OPEN bad_indexes_1

DECLARE @db_name nvarchar(130);
DECLARE @schema_name nvarchar(130);
DECLARE @table_name nvarchar(130);
DECLARE @index_name nvarchar(130);
DECLARE @fragmentation bigint;
DECLARE @suggested_fillfactor int;
DECLARE @partition_num bigint;
DECLARE @partitionOption nvarchar(130);
DECLARE @fillfactorOption nvarchar(130);
DECLARE @object_name nvarchar(1000);
DECLARE @command nvarchar(1000);
DECLARE @time nvarchar(130)

WHILE (1=1) 
 BEGIN
 FETCH NEXT
           FROM bad_indexes_1
           INTO @db_name, @schema_name, @table_name, @index_name, @partition_num, @suggested_fillfactor;

 IF @@FETCH_STATUS < 0 BREAK
 
 

   IF @partition_num > 1
   begin
    SET @partitionOption = N' PARTITION=' + CAST(@partition_num AS nvarchar(10));
    set @fillfactorOption = N''
   end
  else 
   begin
    SET @partitionOption = N''
    set @fillfactorOption = N' FILLFACTOR=' + CAST(@suggested_fillfactor as nvarchar(10)) + N', '
   end

 SET @object_name = QUOTENAME(@db_name) + N'.' + QUOTENAME(@schema_name) + N'.' + QUOTENAME(@table_name)
 BEGIN TRY

    SET @command = N'ALTER INDEX ' + QUOTENAME(@index_name) + N' ON ' + @object_name + ' REBUILD ' + @partitionOption +' WITH(' + @fillfactorOption +  N'maxdop = 0, ONLINE = ON, SORT_IN_TEMPDB = ON, MAX_DURATION = 10 minutes, ABORT_AFTER_WAIT = BLOCKERS)'
  set @time = CURRENT_TIMESTAMP;
  print @time
  RAISERROR(@command, 0, 1)
  EXEC(@command)
  set @time = CURRENT_TIMESTAMP;
  RAISERROR(N'DONE', 0, 1) 
 END TRY
 BEGIN CATCH
     SET @command = N'ALTER INDEX ' + QUOTENAME(@index_name) + N' ON ' + @object_name + ' REBUILD ' + @partitionOption +' WITH(' + @fillfactorOption +  N'maxdop = 0, SORT_IN_TEMPDB = ON)'
 set @time = CURRENT_TIMESTAMP;
  print @time
  RAISERROR(@command, 0, 1)
  EXEC(@command)
  set @time = CURRENT_TIMESTAMP;
  RAISERROR(N'DONE', 0, 1) 
  print @time
 END CATCH
END

close bad_indexes_1
deallocate bad_indexes_1

3.    

DECLARE @FramentationReportTable_2 VARCHAR(255) 
SET @FramentationReportTable_2 = (SELECT top 1 table_name FROM Profiler.INFORMATION_SCHEMA.TABLES  WHERE table_Name LIKE 'Fragmentation_DATA_BASE_'+REPLACE(convert(varchar, getdate(), 102), '.', '_') + '%')
print @FramentationReportTable_2

exec ('
DECLARE bad_indexes_2 CURSOR FOR 
 
 select
 frag.db_name,
 frag.schema_name,
 frag.table_name,
 frag.index_name,
 frag.partition_num,
 case
  when frag.record_size*16 <= 403 then 95
  when frag.record_size*16 <= 806 then 90
  when frag.record_size*16 <= 1209 then 85
  else 80
 end as suggested_fillfactor
 
 
 from Profiler.dbo.'+ @FramentationReportTable_2 +' frag
 where frag.page_count > 24 and frag.fragmentation >= 5  and frag.num = 2  and frag.index_type <>''HEAP''
 order by ROUND ([fragmentation],0 ) desc, [rows_count] desc')


-- Open the cursor.
OPEN bad_indexes_2

DECLARE @db_name nvarchar(130);
DECLARE @schema_name nvarchar(130);
DECLARE @table_name nvarchar(130);
DECLARE @index_name nvarchar(130);
DECLARE @fragmentation bigint;
DECLARE @suggested_fillfactor int;
DECLARE @partition_num bigint;
DECLARE @partitionOption nvarchar(130);
DECLARE @fillfactorOption nvarchar(130);
DECLARE @object_name nvarchar(1000);
DECLARE @command nvarchar(1000);
DECLARE @time nvarchar(130)

WHILE (1=1) 
 BEGIN
 FETCH NEXT
           FROM bad_indexes_2
           INTO @db_name, @schema_name, @table_name, @index_name, @partition_num, @suggested_fillfactor;

 IF @@FETCH_STATUS < 0 BREAK
 
 

   IF @partition_num > 1
   begin
    SET @partitionOption = N' PARTITION=' + CAST(@partition_num AS nvarchar(10));
    set @fillfactorOption = N''
   end
  else 
   begin
    SET @partitionOption = N''
    set @fillfactorOption = N' FILLFACTOR=' + CAST(@suggested_fillfactor as nvarchar(10)) + N', '
   end

 SET @object_name = QUOTENAME(@db_name) + N'.' + QUOTENAME(@schema_name) + N'.' + QUOTENAME(@table_name)
 BEGIN TRY
 
    SET @command = N'ALTER INDEX ' + QUOTENAME(@index_name) + N' ON ' + @object_name + ' REBUILD ' + @partitionOption +' WITH(' + @fillfactorOption +  N'maxdop = 0, ONLINE = ON, SORT_IN_TEMPDB = ON, MAX_DURATION = 10 minutes, ABORT_AFTER_WAIT = BLOCKERS)'
  set @time = CURRENT_TIMESTAMP;
  print @time
  RAISERROR(@command, 0, 1)
  EXEC(@command)
  set @time = CURRENT_TIMESTAMP;
  RAISERROR(N'DONE', 0, 1) 
 END TRY
 BEGIN CATCH
   SET @command = N'ALTER INDEX ' + QUOTENAME(@index_name) + N' ON ' + @object_name + ' REBUILD ' + @partitionOption +' WITH(' + @fillfactorOption +  N'maxdop = 0, SORT_IN_TEMPDB = ON)'
 set @time = CURRENT_TIMESTAMP;
  print @time
  RAISERROR(@command, 0, 1)
  EXEC(@command)
  set @time = CURRENT_TIMESTAMP;
  RAISERROR(N'DONE', 0, 1) 
  print @time
 END CATCH
END

close bad_indexes_2
deallocate bad_indexes_2

В скриптах 2 и 3. Есть пара моментов, которые я хочу прояснить.

  • есть проверка на партиции, и в зависимости от этого будет меняться запрос.

  • есть изменение fillfactor в зависимости от record_size

  • идет 2 условия. Пробует перестроить индекс online и с ожидание если не удается, то перестраивает индекс просто.

  • RAISERROR со временем было сделано для того узнать время начала переиндексации по таблице и конца (Здесь осталось как отладочная команда). Эту инфу можно передать в другую базу для истории или диагностики.

4. Обновление статистики.

USE [DATA-BASE] 
        GO  
        EXEC sp_updatestats;

 

Если вдруг вы хотите добавить потоков, то необходимо будет в скрипте

  • Изменить параметр в SEQUENCE

  • Установить MAXVALUE на значение желаемых потоков

  • И создать шаг наподобие 2 или 3

Но тут нужно хорошо думать. Так как переиндексация достаточно сильно нагружаемый процесс. И можно поставить сервер колом если делать несколько alter index сразу.

После пары запусков с разными настройками удалось добиться 30% выигрыша по времени. И задание успевает выполниться за тех окно.

Комментарии (33)


  1. katamoto
    06.12.2024 13:52

    А исходная проблема-то разрешилась? Т.е. обновление статистики не помогало, а теперь всё летает?


    1. roma_mef Автор
      06.12.2024 13:52

      Обновление статистики помогало только если день - два переиндексация не проходила. Если дольше то начинались тормоза. Сейчас (уже 3 месяца) переиндексация проходит каждую ночь и тормозов больше не наблюдается. Так что да, помогло решить исходную проблему.


      1. katamoto
        06.12.2024 13:52

        Обновление статистики так же каждую ночь делали?


        1. roma_mef Автор
          06.12.2024 13:52

          Да, каждую ночь. Но из-за фрагментация таблицы, самых больших таблиц уже не хватало железа что бы переварить запросы (типо всяких select'ов от начало времен). Был еще вариант заставить программистов 1с переделать их обработки, но они не захотели)


  1. generalx
    06.12.2024 13:52

    хотел бы вас попросить сопровождение, на двух бд прошло успешно
    на одной словил ошибку

    Executing query "SET QUOTED_IDENTIFIER ON; if DB_ID('profiler') ...".: 100% complete End Progress Error: 2024-12-07 08:57:34.52 Code: 0xC002F210 Source: Execute T-SQL Statement Task 1 Execute SQL Task Description: Executing the query "DECLARE @FramentationReportTable_1 VARCHAR(255) ..." failed with the following error: "Incorrect syntax near 'bad_indexes_1'.".


    1. roma_mef Автор
      06.12.2024 13:52

      Можно имя БД?


      1. generalx
        06.12.2024 13:52

        GoldenHome_Treasure_NEW_2012


        1. roma_mef Автор
          06.12.2024 13:52

          В базе профайлер таблицы с именами такого вида Fragmentation_GoldenHome_Treasure_NEW_2012дата ?

          Вот этот запрос выдает результат или ошибку?

          DECLARE @FramentationReportTable_1 VARCHAR(255)
          SET @FramentationReportTable_1 = (SELECT top 1 table_name FROM Profiler.INFORMATION_SCHEMA.TABLES WHERE table_Name LIKE 'Fragmentation_GoldenHome_Treasure_NEW_2012'+REPLACE(convert(varchar, getdate(), 102), '.', '_') + '%')
          print @FramentationReportTable_1

          exec ('

          select
          frag.db_name,
          frag.schema_name,
          frag.table_name,
          frag.index_name,
          frag.partition_num,
          case
          when frag.record_size16 <= 403 then 95 when frag.record_size16 <= 806 then 90
          when frag.record_size*16 <= 1209 then 85
          else 80
          end as suggested_fillfactor

          from Profiler.dbo.'+ @FramentationReportTable_1 +' frag
          where frag.page_count > 24 and frag.fragmentation >= 5 and frag.num = 1 and frag.index_type <>''HEAP''
          order by frag.fragmentation desc')


          1. roma_mef Автор
            06.12.2024 13:52

            Пока как предположение, без полного запроса. Что дата в таблице в базе profiler идет с датой от вчера. и по этому курсор не смог задаться (так как ищет от сегодня данные). Если это так то запрос должен выдать ошибку, и получить данные если будет SET @FramentationReportTable_1 = (SELECT top 1 table_name FROM Profiler.INFORMATION_SCHEMA.TABLES WHERE table_Name LIKE 'Fragmentation_GoldenHome_Treasure_NEW_2012'+REPLACE(convert(varchar, getdate()-1, 102), '.', '_') + '%')


            1. generalx
              06.12.2024 13:52

              Fragmentation_GoldenHome_Treasure_NEW_2012_2024_12_07_1

              Msg 208, Level 16, State 1, Line 3
              Invalid object name 'Profiler.dbo.frag'.


              1. roma_mef Автор
                06.12.2024 13:52

                Ой я тогда в запросе, который кинул _ забыл после имени БД 'Fragmentation_GoldenHome_Treasure_NEW_2012_' нужно


                1. generalx
                  06.12.2024 13:52

                  база проблемная
                  Microsoft SQL Server 2017 (RTM) - 14.0.1000.169 (X64) Aug 22 2017 17:04:49 Copyright (C) 2017 Microsoft Corporation Standard Edition (64-bit) on Windows Server 2016 Standard 10.0 (Build 14393: ) (Hypervisor)

                  обе где успешно
                  Microsoft SQL Server 2022 (RTM-CU11) (KB5032679) - 16.0.4105.2 (X64) Nov 14 2023 18:33:19 Copyright (C) 2022 Microsoft Corporation Standard Edition (64-bit) on Windows Server 2022 Standard 10.0 (Build 20348: ) (Hypervisor)

                  все три разных хоста


                1. generalx
                  06.12.2024 13:52

                  ща проверим


                1. generalx
                  06.12.2024 13:52

                  Fragmentation_GoldenHome_Treasure_NEW_2012_2024_12_07_1
                  Msg 207, Level 16, State 1, Line 3
                  Invalid column name 'record_size16'.
                  Msg 207, Level 16, State 1, Line 3
                  Invalid column name 'record_size16'.

                  Completion time: 2024-12-07T10:29:30.5579878+03:00


                  1. roma_mef Автор
                    06.12.2024 13:52

                    Кажется в запросе где знаки потерялись при копирование у меня

                    DECLARE @FramentationReportTable_1 VARCHAR(255)
                    SET @FramentationReportTable_1 = (SELECT top 1 table_name FROM Profiler.INFORMATION_SCHEMA.TABLES WHERE table_Name LIKE 'Fragmentation_GoldenHome_Treasure_NEW_2012_'+REPLACE(convert(varchar, getdate(), 102), '.', '_') + '%')
                    print @FramentationReportTable_1

                    exec ('

                    select
                    frag.db_name,
                    frag.schema_name,
                    frag.table_name,
                    frag.index_name,
                    frag.partition_num,
                    case
                    when frag.record_size*16 <= 403 then 95

                    when frag.record_size*16 <= 806 then 90
                    when frag.record_size*16 <= 1209 then 85
                    else 80
                    end as suggested_fillfactor

                    from Profiler.dbo.'+ @FramentationReportTable_1 +' frag
                    where frag.page_count > 24 and frag.fragmentation >= 5 and frag.num = 1 and frag.index_type <>''HEAP''
                    order by frag.fragmentation desc')


                    1. generalx
                      06.12.2024 13:52

                      успешно выполнено есть 31 строка
                      куда мне дальше


                  1. roma_mef Автор
                    06.12.2024 13:52

                    А пришли, весь запрос который у тебя получился в скрипте, который не работает (насколько я понимаю это номер 2 должен быть что у меня в статье)


                    1. generalx
                      06.12.2024 13:52

                      DECLARE @FramentationReportTable_1 VARCHAR(255)
                      SET @FramentationReportTable_1 = (SELECT top 1 table_name FROM Profiler.INFORMATION_SCHEMA.TABLES WHERE table_Name LIKE 'Fragmentation_GoldenHome_Treasure_NEW_2012_'+REPLACE(convert(varchar, getdate(), 102), '.', '_') + '%')
                      print @FramentationReportTable_1

                      exec ('
                      DECLARE bad_indexes_1 CURSOR FOR

                      select
                      frag.db_name,
                      frag.schema_name,
                      frag.table_name,
                      frag.index_name,
                      frag.partition_num,
                      case
                      when frag.record_size16 <= 403 then 95 when frag.record_size16 <= 806 then 90
                      when frag.record_size*16 <= 1209 then 85
                      else 80
                      end as suggested_fillfactor

                      from Profiler.dbo.'+ @FramentationReportTable_1 +' frag
                      where frag.page_count > 24 and frag.fragmentation >= 5 and frag.num = 1 and frag.index_type <>''HEAP''
                      order by ROUND ([fragmentation],0 ) desc, [rows_count] desc')

                      -- Open the cursor.
                      OPEN bad_indexes_1

                      DECLARE @db_name nvarchar(130);
                      DECLARE @schema_name nvarchar(130);
                      DECLARE @table_name nvarchar(130);
                      DECLARE @index_name nvarchar(130);
                      DECLARE @fragmentation bigint;
                      DECLARE @suggested_fillfactor int;
                      DECLARE @partition_num bigint;
                      DECLARE @partitionOption nvarchar(130);
                      DECLARE @fillfactorOption nvarchar(130);
                      DECLARE @object_name nvarchar(1000);
                      DECLARE @command nvarchar(1000);
                      DECLARE @time nvarchar(130)

                      WHILE (1=1)
                      BEGIN
                      FETCH NEXT
                      FROM bad_indexes_1
                      INTO @db_name, @schema_name, @table_name, @index_name, @partition_num, @suggested_fillfactor;

                      IF @FETCH_STATUSS < 0 BREAK

                      IF @partition_num > 1
                      begin
                      SET @partitionOption = N' PARTITION=' + CAST@partition_numm AS nvarchar(10));
                      set @fillfactorOption = N''
                      end
                      else
                      begin
                      SET @partitionOption = N''
                      set @fillfactorOption = N' FILLFACTOR=' + CAST@suggested_fillfactorr as nvarchar(10)) + N', '
                      end

                      SET @object_name = QUOTENAME@db_namee) + N'.' + QUOTENAME@schema_namee) + N'.' + QUOTENAME@table_namee)
                      BEGIN TRY

                      SET @command = N'ALTER INDEX ' + QUOTENAME(@index_name) + N' ON ' + @object_name + ' REBUILD ' + @partitionOption +' WITH(' + @fillfactorOption +  N'maxdop = 0, ONLINE = ON, SORT_IN_TEMPDB = ON, MAX_DURATION = 10 minutes, ABORT_AFTER_WAIT = BLOCKERS)'
                      

                      set @time = CURRENT_TIMESTAMP;
                      print @time
                      RAISERROR@commandd, 0, 1)
                      EXEC@commandd)
                      set @time = CURRENT_TIMESTAMP;
                      RAISERROR(N'DONE', 0, 1)
                      END TRY
                      BEGIN CATCH
                      SET @command = N'ALTER INDEX ' + QUOTENAME@index_namee) + N' ON ' + @object_name + ' REBUILD ' + @partitionOption +' WITH(' + @fillfactorOption + N'maxdop = 0, SORT_IN_TEMPDB = ON)'
                      set @time = CURRENT_TIMESTAMP;
                      print @time
                      RAISERROR@commandd, 0, 1)
                      EXEC@commandd)
                      set @time = CURRENT_TIMESTAMP;
                      RAISERROR(N'DONE', 0, 1)
                      print @time
                      END CATCH
                      END

                      close bad_indexes_1
                      deal


                      1. roma_mef Автор
                        06.12.2024 13:52

                        Тоже смотрю у тебя часть запроса потерялась при копирование.

                        должно быть IF @@FETCH_STATUS < 0 BREAK

                        а у тебя IF @FETCH_STATUSS < 0 BREAK

                        Кстати можешь попробовать на сервере выполнить запрос (корректный) просто закоментив обе строки с переиндексацией вот эти SET @command = N'ALTER INDEX '  .... и тогда будет проще увидеть где ошибка


                      1. generalx
                        06.12.2024 13:52

                        ща попробуем запрос проверить
                        и за коменнтить


                      1. generalx
                        06.12.2024 13:52

                        Executed as user: NT Service\SQLSERVERAGENT. Microsoft (R) SQL Server Execute Package Utility Version 14.0.1000.169 for 64-bit Copyright (C) 2017 Microsoft. All rights reserved. Started: 12:32:42 PM Progress: 2024-12-07 12:32:43.29 Source: {3F75105D-FFED-468F-8903-4844FFB5DE7E} Executing query "DECLARE @Guid UNIQUEIDENTIFIER EXECUTE msdb..sp...".: 100% complete End Progress Progress: 2024-12-07 12:32:50.71 Source: Execute T-SQL Statement Task Executing query "SET QUOTED_IDENTIFIER ON; if DB_ID('profiler') ...".: 100% complete End Progress Error: 2024-12-07 12:32:50.76 Code: 0xC002F210 Source: Execute T-SQL Statement Task 2 Execute SQL Task Description: Executing the query "DECLARE @FramentationReportTable_2 VARCHAR(255) ..." failed with the following error: "Incorrect syntax near 'bad_indexes_2'.". Possible failure reasons: Problems with the query, "ResultSet" property not set correctly, parameters not set correctly, or connection not established correctly. End Error Error: 2024-12-07 12:32:50.76 Code: 0xC002F210 Source: Execute T-SQL Statement Task 1 Execute SQL Task Description: Executing the query "DECLARE @FramentationReportTable_1 VARCHAR(255) ..." failed with the following error: "Incorrect syntax near 'bad_indexes_1'.". Possible failure reasons: Problems with the query, "ResultSet" property not set correctly, parameters not set correctly, or connection not established correctly. End Error Warning: 2024-12-07 12:32:50.76 Code: 0x80019002 Source: Subplan_1 Description: SSIS Warning Code DTS_W_MAXIMUMERRORCOUNTREACHED. The Execution method succeeded, but the number of errors raised (2) reached the maximum allowed (1); resulting in failure. This occurs when the number of errors reaches the number specified in MaximumErrorCount. Change the MaximumErrorCount or fix the errors. End Warning DTExec: The package execution returned DTSER_FAILURE (1). Started: 12:32:42 PM Finished: 12:32:50 PM Elapsed: 8.078 seconds. The package execution failed. The step failed.


                      1. roma_mef Автор
                        06.12.2024 13:52

                        Это больше похоже на ошибку из лога. Я имел ввиду new quary весь 2ой или 3й шаг из статьи на сервере выполнить. закометив строку с переиндексации.


                      1. generalx
                        06.12.2024 13:52

                        Так ошибка в логах и не исполняет
                        я за коментил в скрипте2 ща во втором тоже сделаю


                      1. generalx
                        06.12.2024 13:52

                        или мне в отношение базы profile нужно исполнить запросы 2 и 3?


                      1. generalx
                        06.12.2024 13:52

                        Msg 102, Level 15, State 1, Line 87
                        Incorrect syntax near 'bad_indexes_1'.

                        Msg 102, Level 15, State 1, Line 87
                        Incorrect syntax near 'bad_indexes_2'.


                      1. roma_mef Автор
                        06.12.2024 13:52

                        вот тут как раз написано в какой строке ошибка. нужно смотреть может где-то данные не передаются, или потерялся какой ни будь символ


                      1. generalx
                        06.12.2024 13:52

                        Line 87 это конец скрипта. далее deal
                        я см. в notepad++ с отображением символов


                      1. generalx
                        06.12.2024 13:52

                        все точно упущено (((
                        close bad_indexes_1
                        deallocate bad_indexes_1


      1. generalx
        06.12.2024 13:52

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


  1. generalx
    06.12.2024 13:52

    промах ---


  1. Isiirk
    06.12.2024 13:52

    А зачем вам успевать в техокно?, у меня самая большая база в терабайт сейчас, работает 2 скрипта, один занимается индексированием, второй сбором статистики, всё выполняется online, в скриптах для снижения нагрузки реализован механизм проверки, не выполняется ли сейчас бэкап в базе... Работает уже пол года, полет отличный, пару таблиц есть в базе, которые нельзя инлексировать online, но практика показывает, что проблем не возникает из за этого.

    Индексация выполняется только тех, которые действительно надо, то же самое со статистикой, а не всё подряд


  1. unfilled
    06.12.2024 13:52

    А как ваш скрипт учитывает плотность страниц, о которой вы пишете в тексте? Ну, не считая того, что уменьшает её, уменьшая для всех таблиц fill factor?


    1. roma_mef Автор
      06.12.2024 13:52

      А зачем мне учитывать плотность страниц при перестройке индекса в общем случае без точной специфики работы базы. Ведь априори, если уменьшая фрагментацию, мы увеличиваем плотность данных на страницах. Вопрос изначальный в статье о том, что переиндексация нужна. А оставлять fill factor 0 чревато тем, что при любой вставке будет проходить разбиение и фрагментация будет расти слишком быстро. Но при этом и не желательно делать слишком много пустого пространства, из за которого плотность будет меньше. а общее условие suggested_fillfactor позволяет найти компромисс от полного заполнения станиц и как следствие каждодневное повышение фрагментации и сохранить плотность на должном уровне.