mssql-maintenance-deploy/deploy_legacy.txt
2026-05-28 13:27:31 +05:00

1595 lines
45 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

USE [DBA];
GO
/*-----------------------------------------------------------
Удаляем таблицы и процедуры, если они есть
------------------------------------------------------------*/
DROP TABLE IF EXISTS dbo.MaintenanceLog;
GO
DROP TABLE IF EXISTS dbo.IndexMaintenanceTask;
GO
DROP TABLE IF EXISTS dbo.StatsMaintenanceTask;
GO
DROP TABLE IF EXISTS dbo.MaintenanceConfig;
GO
DROP TABLE IF EXISTS dbo.MaintenanceRun;
GO
DROP PROCEDURE IF EXISTS dbo.usp_IndexCandidateSelect;
GO
DROP PROCEDURE IF EXISTS dbo.usp_IndexDefrag_Process;
GO
DROP PROCEDURE IF EXISTS dbo.usp_StatsCandidateSelect;
GO
DROP PROCEDURE IF EXISTS dbo.usp_StatsUpdate_Process;
GO
DROP PROCEDURE IF EXISTS dbo.usp_RunMaintenance;
GO
/*-----------------------------------------------------------
Создаем таблицы
------------------------------------------------------------*/
CREATE TABLE dbo.MaintenanceRun
(
RunId bigint IDENTITY(1,1) NOT NULL CONSTRAINT PK_MaintenanceRun PRIMARY KEY,
ServerName sysname NOT NULL CONSTRAINT DF_MaintenanceRun_ServerName DEFAULT @@SERVERNAME,
DatabaseName sysname NOT NULL,
JobName sysname NULL,
Initiator varchar(20) NOT NULL CONSTRAINT DF_MaintenanceRun_Initiator DEFAULT ('SQLAGENT'),
SessionId int NULL CONSTRAINT DF_MaintenanceRun_SessionId DEFAULT (@@SPID),
StartTime datetime2(0) NOT NULL CONSTRAINT DF_MaintenanceRun_StartTime DEFAULT (SYSDATETIME()),
EndTime datetime2(0) NULL,
Status varchar(20) NOT NULL CONSTRAINT DF_MaintenanceRun_Status DEFAULT ('RUNNING'),
MinPages int NULL,
MinFrag decimal(10,2) NULL,
MaxDop int NULL,
MinRows bigint NULL,
MinModPct decimal(10,2) NULL,
MinModAbs bigint NULL,
SamplePercent int NULL,
LockTimeoutMs int NULL,
CutoffTime datetime2(0) NULL,
IndexCandidates int NULL,
IndexProcessed int NULL,
IndexErrors int NULL,
IndexLockTimeouts int NULL,
IndexDeadlocks int NULL,
IndexTimeLimitSkipped int NULL,
StatsCandidates int NULL,
StatsProcessed int NULL,
StatsErrors int NULL,
ErrorMessage nvarchar(max) NULL
);
GO
CREATE INDEX IX_MaintenanceRun_DatabaseName_StartTime ON dbo.MaintenanceRun(DatabaseName, StartTime DESC);
GO
CREATE INDEX IX_MaintenanceRun_Status_StartTime ON dbo.MaintenanceRun(Status, StartTime DESC);
GO
CREATE TABLE dbo.IndexMaintenanceTask
(
TaskId bigint IDENTITY(1,1) NOT NULL CONSTRAINT PK_IndexMaintenanceTask PRIMARY KEY,
RunId bigint NOT NULL,
ServerName sysname NOT NULL CONSTRAINT DF_IndexMaintenanceTask_ServerName DEFAULT @@SERVERNAME,
DatabaseName sysname NOT NULL,
SchemaName sysname NOT NULL,
TableName sysname NOT NULL,
IndexName sysname NOT NULL,
ObjectId int NOT NULL,
IndexId int NOT NULL,
IndexType nvarchar(60) NULL,
PageCount bigint NULL,
FragPercent decimal(12,3) NULL,
ScanMode varchar(20) NOT NULL,
OperationType varchar(20) NOT NULL CONSTRAINT DF_IndexMaintenanceTask_OperationType DEFAULT ('REBUILD'),
AttemptNo int NOT NULL CONSTRAINT DF_IndexMaintenanceTask_AttemptNo DEFAULT (1),
CommandText nvarchar(max) NULL,
Status varchar(20) NOT NULL CONSTRAINT DF_IndexMaintenanceTask_Status DEFAULT ('PENDING'),
SkipReason varchar(30) NULL,
QueueTime datetime2(0) NOT NULL CONSTRAINT DF_IndexMaintenanceTask_QueueTime DEFAULT (SYSDATETIME()),
StartTime datetime2(0) NULL,
EndTime datetime2(0) NULL,
DurationMin decimal(10,2) NULL,
ErrorNumber int NULL,
ErrorMessage nvarchar(max) NULL
);
GO
ALTER TABLE dbo.IndexMaintenanceTask
ADD CONSTRAINT FK_IndexMaintenanceTask_MaintenanceRun
FOREIGN KEY (RunId)
REFERENCES dbo.MaintenanceRun(RunId);
GO
CREATE INDEX IX_IndexMaintenanceTask_RunId_Status ON dbo.IndexMaintenanceTask(RunId, Status);
GO
CREATE INDEX IX_IndexMaintenanceTask_Db_Object ON dbo.IndexMaintenanceTask(DatabaseName, ObjectId, IndexId);
GO
CREATE INDEX IX_IndexMaintenanceTask_Status_QueueTime ON dbo.IndexMaintenanceTask(Status, QueueTime);
GO
CREATE TABLE dbo.StatsMaintenanceTask
(
TaskId bigint IDENTITY(1,1) NOT NULL CONSTRAINT PK_StatsMaintenanceTask PRIMARY KEY,
RunId bigint NOT NULL,
ServerName sysname NOT NULL CONSTRAINT DF_StatsMaintenanceTask_ServerName DEFAULT @@SERVERNAME,
DatabaseName sysname NOT NULL,
SchemaName sysname NOT NULL,
TableName sysname NOT NULL,
ObjectId int NULL,
StatsName sysname NULL,
RowsCount bigint NULL,
ModificationCounter bigint NULL,
ModPct decimal(8,3) NULL,
UpdateMode varchar(20) NOT NULL,
SamplePercent int NULL,
CommandText nvarchar(max) NULL,
Status varchar(20) NOT NULL CONSTRAINT DF_StatsMaintenanceTask_Status DEFAULT ('PENDING'),
QueueTime datetime2(0) NOT NULL CONSTRAINT DF_StatsMaintenanceTask_QueueTime DEFAULT (SYSDATETIME()),
StartTime datetime2(0) NULL,
EndTime datetime2(0) NULL,
DurationMin decimal(10,2) NULL,
ErrorNumber int NULL,
ErrorMessage nvarchar(max) NULL
);
GO
ALTER TABLE dbo.StatsMaintenanceTask
ADD CONSTRAINT FK_StatsMaintenanceTask_MaintenanceRun
FOREIGN KEY (RunId)
REFERENCES dbo.MaintenanceRun(RunId);
GO
CREATE INDEX IX_StatsMaintenanceTask_RunId_Status ON dbo.StatsMaintenanceTask(RunId, Status);
GO
CREATE INDEX IX_StatsMaintenanceTask_Db_Object ON dbo.StatsMaintenanceTask(DatabaseName, ObjectId);
GO
CREATE INDEX IX_StatsMaintenanceTask_Status_QueueTime ON dbo.StatsMaintenanceTask(Status, QueueTime);
GO
CREATE TABLE dbo.MaintenanceLog
(
LogId bigint IDENTITY(1,1) NOT NULL CONSTRAINT PK_MaintenanceLog PRIMARY KEY,
RunId bigint NOT NULL,
TaskType varchar(20) NULL,
TaskId bigint NULL,
LogTime datetime2(0) NOT NULL CONSTRAINT DF_MaintenanceLog_LogTime DEFAULT (SYSDATETIME()),
LogLevel varchar(10) NOT NULL,
StepName varchar(100) NULL,
Message nvarchar(max) NOT NULL
);
GO
ALTER TABLE dbo.MaintenanceLog
ADD CONSTRAINT FK_MaintenanceLog_MaintenanceRun
FOREIGN KEY (RunId)
REFERENCES dbo.MaintenanceRun(RunId);
GO
CREATE INDEX IX_MaintenanceLog_RunId_LogTime ON dbo.MaintenanceLog(RunId, LogTime);
GO
CREATE INDEX IX_MaintenanceLog_TaskType_TaskId ON dbo.MaintenanceLog(TaskType, TaskId);
GO
CREATE INDEX IX_MaintenanceLog_LogLevel_LogTime ON dbo.MaintenanceLog(LogLevel, LogTime DESC);
GO
CREATE TABLE dbo.MaintenanceConfig
(
ConfigId int IDENTITY(1,1) NOT NULL CONSTRAINT PK_MaintenanceConfig PRIMARY KEY,
ServerName sysname NOT NULL CONSTRAINT DF_MaintenanceConfig_ServerName DEFAULT @@SERVERNAME,
DatabaseName sysname NOT NULL,
IsEnabled bit NOT NULL CONSTRAINT DF_MaintenanceConfig_IsEnabled DEFAULT (1),
MinPages int NOT NULL CONSTRAINT DF_MaintenanceConfig_MinPages DEFAULT (1000),
MinFrag decimal(5,2) NOT NULL CONSTRAINT DF_MaintenanceConfig_MinFrag DEFAULT (5.00),
MaxDop int NOT NULL CONSTRAINT DF_MaintenanceConfig_MaxDop DEFAULT (4),
MinRows bigint NOT NULL CONSTRAINT DF_MaintenanceConfig_MinRows DEFAULT (5000),
MinModPct decimal(5,2) NOT NULL CONSTRAINT DF_MaintenanceConfig_MinModPct DEFAULT (10.00),
MinModAbs bigint NOT NULL CONSTRAINT DF_MaintenanceConfig_MinModAbs DEFAULT (5000),
LockTimeoutMs int NOT NULL CONSTRAINT DF_MaintenanceConfig_LockTimeoutMs DEFAULT (5000),
RebuildCutoffTime time(0) NULL,
ExcludeSchemas nvarchar(max) NULL,
ExcludeTables nvarchar(max) NULL,
Notes nvarchar(1000) NULL,
ModifiedAt datetime2(0) NOT NULL CONSTRAINT DF_MaintenanceConfig_ModifiedAt DEFAULT (SYSDATETIME())
);
GO
CREATE UNIQUE INDEX UX_MaintenanceConfig_DatabaseName ON dbo.MaintenanceConfig(DatabaseName);
GO
/*-----------------------------------------------------------
Записываем начальные конфиги для всех баз
------------------------------------------------------------*/
SET NOCOUNT ON;
SET XACT_ABORT ON;
GO
DECLARE @IsEnabled bit = 1;
DECLARE @MinPages int = 1000;
DECLARE @MinFrag decimal(5,2) = 5.00;
DECLARE @MaxDop int = 4;
DECLARE @MinRows bigint = 5000;
DECLARE @MinModPct decimal(5,2) = 5.00;
DECLARE @MinModAbs bigint = 40000;
DECLARE @LockTimeoutMs int = 5000;
DECLARE @RebuildCutoffTime time(0) = '06:00:00';
DECLARE @Notes nvarchar(1000) = N'Инициализация';
INSERT INTO dbo.MaintenanceConfig
(
DatabaseName,
IsEnabled,
MinPages,
MinFrag,
MaxDop,
MinRows,
MinModPct,
MinModAbs,
LockTimeoutMs,
RebuildCutoffTime,
Notes,
ModifiedAt
)
SELECT
d.name,
@IsEnabled,
@MinPages,
@MinFrag,
@MaxDop,
@MinRows,
@MinModPct,
@MinModAbs,
@LockTimeoutMs,
@RebuildCutoffTime,
@Notes,
SYSDATETIME()
FROM sys.databases AS d
WHERE d.database_id > 4
AND d.name != N'DBA'
AND d.state_desc = 'ONLINE'
AND d.source_database_id IS NULL
AND NOT EXISTS
(
SELECT 1
FROM dbo.MaintenanceConfig AS c
WHERE c.DatabaseName = d.name
);
GO
/*-----------------------------------------------------------
Определяем хранимые процедуры
------------------------------------------------------------*/
CREATE OR ALTER PROCEDURE dbo.usp_Index_SelectCandidates
(
@DatabaseName sysname,
@RunId bigint,
@PhaseName varchar(20)
)
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE
@MinPages int,
@MinFrag decimal(5,2),
@IsEnabled bit,
@Cmd nvarchar(max),
@Inserted int = 0,
@AdminDb sysname = DB_NAME();
SET @PhaseName = UPPER(@PhaseName);
IF @PhaseName NOT IN ('LIMITED', 'SAMPLED')
THROW 50015, N'@PhaseName должен быть LIMITED или SAMPLED', 1;
IF @DatabaseName IS NULL OR LTRIM(RTRIM(@DatabaseName)) = N''
THROW 50001, N'usp_Index_SelectCandidates: не задан параметр @DatabaseName', 1;
IF @RunId IS NULL
THROW 50002, N'usp_Index_SelectCandidates: не задан параметр @RunId', 1;
IF DB_ID(@DatabaseName) IS NULL
THROW 50003, N'usp_Index_SelectCandidates: указанная база не существует', 1;
SELECT
@IsEnabled = IsEnabled,
@MinPages = MinPages,
@MinFrag = MinFrag
FROM dbo.MaintenanceConfig
WHERE DatabaseName = @DatabaseName;
IF @IsEnabled IS NULL
THROW 50004, N'usp_Index_SelectCandidates: для базы отсутствует запись в dbo.MaintenanceConfig', 1;
IF @IsEnabled = 0
BEGIN
INSERT INTO dbo.MaintenanceLog
(
RunId,
TaskType,
TaskId,
LogLevel,
StepName,
Message
)
VALUES
(
@RunId,
'RUN',
NULL,
'WARN',
'INDEX_SELECT_' + @PhaseName,
N'Этап пропущен: обслуживание для базы отключено в MaintenanceConfig.'
);
RETURN;
END;
INSERT INTO dbo.MaintenanceLog
(
RunId,
TaskType,
TaskId,
LogLevel,
StepName,
Message
)
VALUES
(
@RunId,
'RUN',
NULL,
'INFO',
'INDEX_SELECT_' + @PhaseName,
N'Начат отбор кандидатов в режиме ' + @PhaseName
);
SET @Cmd = N'
INSERT INTO ' + QUOTENAME(@AdminDb) + N'.dbo.IndexMaintenanceTask
(
RunId,
DatabaseName,
SchemaName,
TableName,
IndexName,
ObjectId,
IndexId,
IndexType,
PageCount,
FragPercent,
ScanMode,
OperationType,
Status,
CommandText
)
SELECT
@p_RunId,
@p_DatabaseName,
s.name,
o.name,
i.name,
i.object_id,
i.index_id,
i.type_desc,
ips.page_count,
ips.avg_fragmentation_in_percent,
@p_ScanMode,
''REBUILD'',
''PENDING'',
NULL
FROM sys.dm_db_index_physical_stats(DB_ID(@p_DatabaseName), NULL, NULL, NULL, @p_ScanMode) AS ips
JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.indexes AS i
ON i.object_id = ips.object_id
AND i.index_id = ips.index_id
JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.objects AS o
ON o.object_id = i.object_id
JOIN ' + QUOTENAME(@DatabaseName) + N'.sys.schemas AS s
ON s.schema_id = o.schema_id
WHERE
o.type = ''U''
AND ips.alloc_unit_type_desc = ''IN_ROW_DATA''
AND ips.index_id > 0
AND ips.page_count >= @p_MinPages
AND ips.avg_fragmentation_in_percent >= @p_MinFrag
AND i.is_disabled = 0
AND i.is_hypothetical = 0
AND i.type_desc IN (''CLUSTERED'', ''NONCLUSTERED'')
AND NOT EXISTS
(
SELECT 1
FROM ' + QUOTENAME(@AdminDb) + N'.dbo.IndexMaintenanceTask AS t
WHERE t.RunId = @p_RunId
AND t.DatabaseName = @p_DatabaseName
AND t.ObjectId = i.object_id
AND t.IndexId = i.index_id
);
SET @p_Inserted = @@ROWCOUNT;
';
EXEC sys.sp_executesql
@Cmd,
N'@p_RunId bigint,
@p_DatabaseName sysname,
@p_ScanMode varchar(20),
@p_MinPages int,
@p_MinFrag decimal(5,2),
@p_Inserted int OUTPUT',
@p_RunId = @RunId,
@p_DatabaseName = @DatabaseName,
@p_ScanMode = @PhaseName,
@p_MinPages = @MinPages,
@p_MinFrag = @MinFrag,
@p_Inserted = @Inserted OUTPUT;
INSERT INTO dbo.MaintenanceLog
(
RunId,
TaskType,
TaskId,
LogLevel,
StepName,
Message
)
VALUES
(
@RunId,
'RUN',
NULL,
'INFO',
'INDEX_SELECT_' + @PhaseName,
N'Отбор завершен. Добавлено кандидатов: ' + CAST(@Inserted AS nvarchar(20))
);
END;
GO
CREATE OR ALTER PROCEDURE dbo.usp_UpdateStatistics_SelectCandidates
(
@DatabaseName sysname,
@RunId bigint
)
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE
@IsEnabled bit,
@MinRows bigint,
@MinModPct decimal(5,2),
@MinModAbs bigint,
@Cmd nvarchar(max),
@Inserted int = 0,
@AdminDb sysname = DB_NAME();
IF @DatabaseName IS NULL OR LTRIM(RTRIM(@DatabaseName)) = N''
THROW 50031, N'usp_UpdateStatistics_SelectCandidates: не задан параметр @DatabaseName.', 1;
IF @RunId IS NULL
THROW 50032, N'usp_UpdateStatistics_SelectCandidates: не задан параметр @RunId.', 1;
IF DB_ID(@DatabaseName) IS NULL
THROW 50033, N'usp_UpdateStatistics_SelectCandidates: указанная база не существует.', 1;
SELECT
@IsEnabled = IsEnabled,
@MinRows = MinRows,
@MinModPct = MinModPct,
@MinModAbs = MinModAbs
FROM dbo.MaintenanceConfig
WHERE DatabaseName = @DatabaseName;
IF @IsEnabled IS NULL
THROW 50034, N'usp_UpdateStatistics_SelectCandidates: для базы отсутствует запись в dbo.MaintenanceConfig.', 1;
IF @IsEnabled = 0
BEGIN
INSERT INTO dbo.MaintenanceLog
(
RunId,
TaskType,
TaskId,
LogLevel,
StepName,
Message
)
VALUES
(
@RunId,
'RUN',
NULL,
'WARN',
'STATS_SELECT',
N'Этап пропущен: обслуживание для базы отключено в dbo.MaintenanceConfig.'
);
RETURN;
END;
INSERT INTO dbo.MaintenanceLog
(
RunId,
TaskType,
TaskId,
LogLevel,
StepName,
Message
)
VALUES
(
@RunId,
'RUN',
NULL,
'INFO',
'STATS_SELECT',
N'Начат отбор кандидатов на обновление статистики для базы ' + QUOTENAME(@DatabaseName)
);
SET @Cmd = N'
USE ' + QUOTENAME(@DatabaseName) + N';
;WITH StatsData AS
(
SELECT
o.object_id,
s.name AS SchemaName,
o.name AS TableName,
CASE
WHEN o.name LIKE ''[_]AccRg%''
OR o.name LIKE ''[_]AccumRg%''
OR o.name LIKE ''[_]InfoRg%''
OR o.name LIKE ''[_]CRg%''
THEN 1
ELSE 0
END AS Is1CRegister,
ISNULL(sp.[rows], 0) AS [rows],
ISNULL(sp.modification_counter, 0) AS modification_counter,
CASE
WHEN ISNULL(sp.[rows], 0) = 0 THEN 0
ELSE
CASE
WHEN (100.0 * ISNULL(sp.modification_counter, 0) / sp.[rows]) > 99999
THEN 99999
ELSE (100.0 * ISNULL(sp.modification_counter, 0) / sp.[rows])
END
END AS ModPct,
sp.last_updated
FROM sys.objects AS o
JOIN sys.schemas AS s
ON s.schema_id = o.schema_id
JOIN sys.stats AS st
ON st.object_id = o.object_id
OUTER APPLY sys.dm_db_stats_properties(st.object_id, st.stats_id) AS sp
WHERE
o.type = ''U''
AND o.is_ms_shipped = 0
),
Agg AS
(
SELECT
sd.object_id,
sd.SchemaName,
sd.TableName,
MAX(sd.Is1CRegister) AS Is1CRegister,
MAX(sd.[rows]) AS RowsCount,
MAX(sd.modification_counter) AS ModificationCounter,
MAX(sd.ModPct) AS ModPct,
MAX(CASE WHEN sd.last_updated IS NULL THEN 1 ELSE 0 END) AS HasNeverUpdatedStats,
SUM(CASE WHEN sd.modification_counter >= @p_MinModAbs THEN 1 ELSE 0 END) AS StatsWithHighModCounter
FROM StatsData AS sd
WHERE sd.[rows] >= @p_MinRows
GROUP BY
sd.object_id,
sd.SchemaName,
sd.TableName
),
Prepared AS
(
SELECT
a.object_id,
a.SchemaName,
a.TableName,
a.Is1CRegister,
a.RowsCount,
a.ModificationCounter,
a.ModPct,
CASE
WHEN a.Is1CRegister = 1 THEN ''FULLSCAN''
WHEN a.RowsCount < 5000000 THEN ''FULLSCAN''
ELSE ''SAMPLE''
END AS UpdateMode,
CASE
WHEN a.Is1CRegister = 1 THEN NULL
WHEN a.RowsCount < 5000000 THEN NULL
WHEN a.RowsCount < 10000000 THEN 30
WHEN a.RowsCount < 30000000 THEN 20
WHEN a.RowsCount < 100000000 THEN 10
WHEN a.RowsCount < 300000000 THEN 7
WHEN a.RowsCount < 1000000000 THEN 5
WHEN a.RowsCount < 3000000000 THEN 4
ELSE 3
END AS SamplePercent
FROM Agg AS a
WHERE
a.HasNeverUpdatedStats = 1
OR a.ModPct >= @p_MinModPct
OR a.StatsWithHighModCounter >= 1
)
INSERT INTO ' + QUOTENAME(@AdminDb) + N'.dbo.StatsMaintenanceTask
(
RunId,
DatabaseName,
SchemaName,
TableName,
ObjectId,
StatsName,
RowsCount,
ModificationCounter,
ModPct,
UpdateMode,
SamplePercent,
Status,
CommandText
)
SELECT
@p_RunId,
@p_DatabaseName,
p.SchemaName,
p.TableName,
p.object_id,
NULL,
p.RowsCount,
p.ModificationCounter,
p.ModPct,
p.UpdateMode,
p.SamplePercent,
''PENDING'',
NULL
FROM Prepared AS p
WHERE NOT EXISTS
(
SELECT 1
FROM ' + QUOTENAME(@AdminDb) + N'.dbo.StatsMaintenanceTask AS t
WHERE t.RunId = @p_RunId
AND t.DatabaseName = @p_DatabaseName
AND t.ObjectId = p.object_id
);
SET @p_Inserted = @@ROWCOUNT;
';
EXEC sys.sp_executesql
@Cmd,
N'@p_RunId bigint,
@p_DatabaseName sysname,
@p_MinRows bigint,
@p_MinModPct decimal(5,2),
@p_MinModAbs bigint,
@p_Inserted int OUTPUT',
@p_RunId = @RunId,
@p_DatabaseName = @DatabaseName,
@p_MinRows = @MinRows,
@p_MinModPct = @MinModPct,
@p_MinModAbs = @MinModAbs,
@p_Inserted = @Inserted OUTPUT;
INSERT INTO dbo.MaintenanceLog
(
RunId, TaskType, TaskId, LogLevel, StepName, Message
)
VALUES
(
@RunId, 'RUN', NULL, 'INFO', 'STATS_SELECT',
N'Отбор кандидатов на обновление статистики завершен. Добавлено задач: ' + CAST(@Inserted AS nvarchar(20)) + N'.'
);
END;
GO
CREATE OR ALTER PROCEDURE dbo.usp_Index_RebuildProcess
(
@DatabaseName sysname,
@RunId bigint,
@PhaseName varchar(20)
)
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT OFF;
DECLARE
@MaxDop int,
@LockTimeoutMs int,
@RebuildCutoffTime time(0),
@IsEnabled bit,
@TaskId bigint,
@SchemaName sysname,
@TableName sysname,
@IndexName sysname,
@ObjectId int,
@IndexId int,
@PageCount bigint,
@FragPercent decimal(8,3),
@Cmd nvarchar(max),
@StartTime datetime2(0),
@EndTime datetime2(0),
@DurationMin decimal(10,2),
@CutoffDateTime datetime2(0),
@SkippedByCutoff int = 0;
IF @DatabaseName IS NULL OR LTRIM(RTRIM(@DatabaseName)) = N''
THROW 50011, N'usp_Index_RebuildProcess: не задан параметр @DatabaseName.', 1;
IF @RunId IS NULL
THROW 50012, N'usp_Index_RebuildProcess: не задан параметр @RunId.', 1;
IF DB_ID(@DatabaseName) IS NULL
THROW 50013, N'usp_Index_RebuildProcess: указанная база не существует.', 1;
IF @PhaseName IS NULL OR UPPER(@PhaseName) NOT IN ('LIMITED', 'SAMPLED')
THROW 50014, N'usp_Index_RebuildProcess: @PhaseName должен быть LIMITED или SAMPLED.', 1;
SET @PhaseName = UPPER(@PhaseName);
SELECT
@IsEnabled = IsEnabled,
@MaxDop = MaxDop,
@LockTimeoutMs = LockTimeoutMs,
@RebuildCutoffTime = RebuildCutoffTime
FROM dbo.MaintenanceConfig
WHERE DatabaseName = @DatabaseName;
IF @IsEnabled IS NULL
THROW 50015, N'usp_Index_RebuildProcess: для базы отсутствует запись в dbo.MaintenanceConfig.', 1;
IF @IsEnabled = 0
BEGIN
INSERT INTO dbo.MaintenanceLog
(
RunId, TaskType, TaskId, LogLevel, StepName, Message
)
VALUES
(
@RunId, 'RUN', NULL, 'WARN',
'INDEX_REBUILD_' + @PhaseName,
N'Этап пропущен: обслуживание для базы отключено в dbo.MaintenanceConfig.'
);
RETURN;
END;
IF @RebuildCutoffTime IS NOT NULL
BEGIN
SET @CutoffDateTime =
DATEADD
(
SECOND,
DATEDIFF(SECOND, CONVERT(time(0), '00:00:00'), @RebuildCutoffTime),
CONVERT(datetime2(0), CONVERT(date, SYSDATETIME()))
);
END;
DECLARE @SqlLockTimeout nvarchar(100);
SET @SqlLockTimeout = N'SET LOCK_TIMEOUT ' + CONVERT(nvarchar(20), @LockTimeoutMs) + N';';
EXEC sys.sp_executesql @SqlLockTimeout;
INSERT INTO dbo.MaintenanceLog
(
RunId,
TaskType,
TaskId,
LogLevel,
StepName,
Message
)
VALUES
(
@RunId,
'RUN',
NULL,
'INFO',
'INDEX_REBUILD_' + @PhaseName,
N'Начата обработка очереди индексов для фазы ' + @PhaseName + N' в базе ' + QUOTENAME(@DatabaseName)
);
WHILE EXISTS
(
SELECT 1
FROM dbo.IndexMaintenanceTask
WHERE RunId = @RunId
AND DatabaseName = @DatabaseName
AND ScanMode = @PhaseName
AND Status = 'PENDING'
)
BEGIN
IF @CutoffDateTime IS NOT NULL AND SYSDATETIME() >= @CutoffDateTime
BEGIN
UPDATE dbo.IndexMaintenanceTask
SET
Status = 'TIME_LIMIT',
SkipReason = 'TIME_LIMIT',
EndTime = SYSDATETIME(),
ErrorMessage = N'Операция не начата: достигнуто ограничение RebuildCutoffTime.'
WHERE RunId = @RunId
AND DatabaseName = @DatabaseName
AND ScanMode = @PhaseName
AND Status = 'PENDING';
SET @SkippedByCutoff = @@ROWCOUNT;
INSERT INTO dbo.MaintenanceLog
(
RunId,
TaskType,
TaskId,
LogLevel,
StepName,
Message
)
VALUES
(
@RunId,
'RUN',
NULL,
'WARN',
'INDEX_REBUILD_' + @PhaseName,
N'Обработка очереди остановлена по RebuildCutoffTime. Пропущено задач: ' + CAST(@SkippedByCutoff AS nvarchar(20))
);
BREAK;
END;
SELECT TOP (1)
@TaskId = TaskId,
@SchemaName = SchemaName,
@TableName = TableName,
@IndexName = IndexName,
@ObjectId = ObjectId,
@IndexId = IndexId,
@PageCount = PageCount,
@FragPercent = FragPercent
FROM dbo.IndexMaintenanceTask
WHERE RunId = @RunId
AND DatabaseName = @DatabaseName
AND ScanMode = @PhaseName
AND Status = 'PENDING'
ORDER BY FragPercent DESC, PageCount DESC, TaskId ASC;
SET @StartTime = SYSDATETIME();
UPDATE dbo.IndexMaintenanceTask
SET
Status = 'RUNNING',
StartTime = @StartTime,
CommandText =
N'USE ' + QUOTENAME(@DatabaseName) + N'; ' +
N'ALTER INDEX ' + QUOTENAME(@IndexName) +
N' ON ' + QUOTENAME(@SchemaName) + N'.' + QUOTENAME(@TableName) +
N' REBUILD WITH (SORT_IN_TEMPDB = OFF, MAXDOP = ' + CAST(@MaxDop AS nvarchar(10)) + N', ONLINE = OFF);'
WHERE TaskId = @TaskId;
SELECT @Cmd = CommandText
FROM dbo.IndexMaintenanceTask
WHERE TaskId = @TaskId;
BEGIN TRY
EXEC sys.sp_executesql @Cmd;
SET @EndTime = SYSDATETIME();
SET @DurationMin = CAST(DATEDIFF(MILLISECOND, @StartTime, @EndTime) / 60000.0 AS decimal(10,2));
UPDATE dbo.IndexMaintenanceTask
SET
Status = 'DONE',
SkipReason = NULL,
EndTime = @EndTime,
DurationMin = @DurationMin,
ErrorNumber = NULL,
ErrorMessage = NULL
WHERE TaskId = @TaskId;
END TRY
BEGIN CATCH
SET @EndTime = SYSDATETIME();
SET @DurationMin = CAST(DATEDIFF(MILLISECOND, @StartTime, @EndTime) / 60000.0 AS decimal(10,2));
IF ERROR_NUMBER() = 1222
BEGIN
UPDATE dbo.IndexMaintenanceTask
SET
Status = 'LOCK_TIMEOUT',
SkipReason = 'LOCK_TIMEOUT',
EndTime = @EndTime,
DurationMin = @DurationMin,
ErrorNumber = ERROR_NUMBER(),
ErrorMessage = ERROR_MESSAGE()
WHERE TaskId = @TaskId;
INSERT INTO dbo.MaintenanceLog
(
RunId,
TaskType,
TaskId,
LogLevel,
StepName,
Message
)
VALUES
(
@RunId,
'INDEX',
@TaskId,
'WARN',
'INDEX_REBUILD_' + @PhaseName,
N'LOCK_TIMEOUT при REBUILD индекса ' + QUOTENAME(@SchemaName) + N'.' + QUOTENAME(@TableName) + N' / ' + QUOTENAME(@IndexName)
);
END
ELSE IF ERROR_NUMBER() = 1205
BEGIN
UPDATE dbo.IndexMaintenanceTask
SET
Status = 'DEADLOCK',
SkipReason = 'DEADLOCK',
EndTime = @EndTime,
DurationMin = @DurationMin,
ErrorNumber = ERROR_NUMBER(),
ErrorMessage = ERROR_MESSAGE()
WHERE TaskId = @TaskId;
INSERT INTO dbo.MaintenanceLog
(
RunId,
TaskType,
TaskId,
LogLevel,
StepName,
Message
)
VALUES
(
@RunId,
'INDEX',
@TaskId,
'WARN',
'INDEX_REBUILD_' + @PhaseName, N'DEADLOCK при REBUILD индекса ' + QUOTENAME(@SchemaName) + N'.' + QUOTENAME(@TableName) + N' / ' + QUOTENAME(@IndexName)
);
END
ELSE
BEGIN
UPDATE dbo.IndexMaintenanceTask
SET
Status = 'ERROR',
SkipReason = NULL,
EndTime = @EndTime,
DurationMin = @DurationMin,
ErrorNumber = ERROR_NUMBER(),
ErrorMessage = ERROR_MESSAGE()
WHERE TaskId = @TaskId;
INSERT INTO dbo.MaintenanceLog
(
RunId,
TaskType,
TaskId,
LogLevel,
StepName,
Message
)
VALUES
(
@RunId,
'INDEX',
@TaskId,
'ERROR',
'INDEX_REBUILD_' + @PhaseName,
N'Ошибка при REBUILD индекса ' + QUOTENAME(@SchemaName) + N'.' + QUOTENAME(@TableName) + N' / ' + QUOTENAME(@IndexName) + N'. Ошибка: ' + ERROR_MESSAGE()
);
END
END CATCH;
END;
INSERT INTO dbo.MaintenanceLog
(
RunId,
TaskType,
TaskId,
LogLevel,
StepName,
Message
)
VALUES
(
@RunId,
'RUN',
NULL,
'INFO',
'INDEX_REBUILD_' + @PhaseName,
N'Обработка очереди индексов для фазы ' + @PhaseName + N' завершена.'
);
END;
GO
CREATE OR ALTER PROCEDURE dbo.usp_UpdateStatistics_Process
(
@DatabaseName sysname,
@RunId bigint
)
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT OFF;
DECLARE
@IsEnabled bit,
@TaskId bigint,
@SchemaName sysname,
@TableName sysname,
@StatsName sysname,
@UpdateMode varchar(20),
@SamplePercent int,
@Cmd nvarchar(max),
@StartTime datetime2(0),
@EndTime datetime2(0),
@RebuildCutoffTime time(0),
@CutoffDateTime datetime2(0),
@DurationMin decimal(10,2),
@SkippedByCutoff int = 0;
IF @DatabaseName IS NULL OR LTRIM(RTRIM(@DatabaseName)) = N''
THROW 50041, N'usp_UpdateStatistics_Process: не задан параметр @DatabaseName.', 1;
IF @RunId IS NULL
THROW 50042, N'usp_UpdateStatistics_Process: не задан параметр @RunId.', 1;
IF DB_ID(@DatabaseName) IS NULL
THROW 50043, N'usp_UpdateStatistics_Process: указанная база не существует.', 1;
SELECT
@IsEnabled = IsEnabled,
@RebuildCutoffTime = RebuildCutoffTime
FROM dbo.MaintenanceConfig
WHERE DatabaseName = @DatabaseName;
IF @IsEnabled IS NULL
THROW 50044, N'usp_UpdateStatistics_Process: для базы отсутствует запись в dbo.MaintenanceConfig.', 1;
IF @IsEnabled = 0
BEGIN
INSERT INTO dbo.MaintenanceLog
(
RunId, TaskType, TaskId, LogLevel, StepName, Message
)
VALUES
(
@RunId, 'RUN', NULL, 'WARN', 'STATS_UPDATE',
N'Этап пропущен: обслуживание для базы отключено в dbo.MaintenanceConfig.'
);
RETURN;
END;
IF @RebuildCutoffTime IS NOT NULL
BEGIN
SET @CutoffDateTime =
DATEADD
(
SECOND,
DATEDIFF(SECOND, CONVERT(time(0), '00:00:00'), @RebuildCutoffTime),
CONVERT(datetime2(0), CONVERT(date, SYSDATETIME()))
);
END;
INSERT INTO dbo.MaintenanceLog
(
RunId,
TaskType,
TaskId,
LogLevel,
StepName,
Message
)
VALUES
(
@RunId,
'RUN',
NULL,
'INFO',
'STATS_UPDATE',
N'Начато обновление статистики для базы ' + QUOTENAME(@DatabaseName)
);
WHILE EXISTS
(
SELECT 1
FROM dbo.StatsMaintenanceTask
WHERE RunId = @RunId
AND DatabaseName = @DatabaseName
AND Status = 'PENDING'
)
BEGIN
IF @CutoffDateTime IS NOT NULL AND SYSDATETIME() >= @CutoffDateTime
BEGIN
UPDATE dbo.StatsMaintenanceTask
SET
Status = 'TIME_LIMIT',
EndTime = SYSDATETIME(),
ErrorMessage = N'Операция не начата: достигнуто ограничение RebuildCutoffTime.'
WHERE RunId = @RunId
AND DatabaseName = @DatabaseName
AND Status = 'PENDING';
SET @SkippedByCutoff = @@ROWCOUNT;
INSERT INTO dbo.MaintenanceLog
(
RunId,
TaskType,
TaskId,
LogLevel,
StepName,
Message
)
VALUES
(
@RunId,
'RUN',
NULL,
'WARN',
'STATS_UPDATE',
N'Обработка очереди остановлена по RebuildCutoffTime. Пропущено задач: ' + CAST(@SkippedByCutoff AS nvarchar(20))
);
BREAK;
END;
SELECT TOP (1)
@TaskId = TaskId,
@SchemaName = SchemaName,
@TableName = TableName,
@StatsName = StatsName,
@UpdateMode = UpdateMode,
@SamplePercent = SamplePercent
FROM dbo.StatsMaintenanceTask
WHERE RunId = @RunId
AND DatabaseName = @DatabaseName
AND Status = 'PENDING'
ORDER BY TaskId;
SET @StartTime = SYSDATETIME();
UPDATE dbo.StatsMaintenanceTask
SET Status = 'RUNNING',
StartTime = @StartTime
WHERE TaskId = @TaskId;
SET @Cmd =
N'USE ' + QUOTENAME(@DatabaseName) + N'; ' +
N'UPDATE STATISTICS ' + QUOTENAME(@SchemaName) + N'.' + QUOTENAME(@TableName) +
CASE
WHEN @StatsName IS NOT NULL
THEN N' (' + QUOTENAME(@StatsName) + N')'
ELSE N''
END +
CASE
WHEN @UpdateMode = 'FULLSCAN'
THEN N' WITH FULLSCAN;'
WHEN @UpdateMode = 'SAMPLE'
THEN N' WITH SAMPLE ' + CAST(@SamplePercent AS nvarchar(10)) + N' PERCENT;'
ELSE N';'
END;
UPDATE dbo.StatsMaintenanceTask
SET CommandText = @Cmd
WHERE TaskId = @TaskId;
BEGIN TRY
EXEC sys.sp_executesql @Cmd;
SET @EndTime = SYSDATETIME();
SET @DurationMin = CAST(DATEDIFF(MILLISECOND, @StartTime, @EndTime) / 60000.0 AS decimal(10,2));
UPDATE dbo.StatsMaintenanceTask
SET
Status = 'DONE',
EndTime = @EndTime,
DurationMin = @DurationMin,
ErrorNumber = NULL,
ErrorMessage = NULL
WHERE TaskId = @TaskId;
END TRY
BEGIN CATCH
SET @EndTime = SYSDATETIME();
SET @DurationMin = CAST(DATEDIFF(MILLISECOND, @StartTime, @EndTime) / 60000.0 AS decimal(10,2));
UPDATE dbo.StatsMaintenanceTask
SET
Status = 'ERROR',
EndTime = @EndTime,
DurationMin = @DurationMin,
ErrorNumber = ERROR_NUMBER(),
ErrorMessage = ERROR_MESSAGE()
WHERE TaskId = @TaskId;
INSERT INTO dbo.MaintenanceLog
(
RunId,
TaskType,
TaskId,
LogLevel,
StepName,
Message
)
VALUES
(
@RunId,
'STATS',
@TaskId,
'ERROR',
'STATS_UPDATE',
N'Ошибка обновления статистики для ' + QUOTENAME(@SchemaName) + N'.' + QUOTENAME(@TableName) + N'. Ошибка: ' + ERROR_MESSAGE()
);
END CATCH;
END;
INSERT INTO dbo.MaintenanceLog
(
RunId,
TaskType,
TaskId,
LogLevel,
StepName,
Message
)
VALUES
(
@RunId,
'RUN',
NULL,
'INFO',
'STATS_UPDATE',
N'Обновление статистики для базы ' + QUOTENAME(@DatabaseName) + N' завершено'
);
END;
GO
CREATE OR ALTER PROCEDURE dbo.usp_RunMaintenance
(
@DatabaseName sysname,
@RunId bigint OUTPUT
)
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE
@IsEnabled bit,
@MinPages int,
@MinFrag decimal(5,2),
@MaxDop int,
@MinRows bigint,
@MinModPct decimal(5,2),
@MinModAbs bigint,
@LockTimeoutMs int,
@RebuildCutoffTime time(0),
@CutoffDateTime datetime2(0),
@ErrorMessage nvarchar(max),
@FinalStatus varchar(20),
@IndexCandidates int,
@IndexProcessed int,
@IndexErrors int,
@IndexLockTimeouts int,
@IndexDeadlocks int,
@IndexTimeLimitSkipped int,
@StatsCandidates int,
@StatsProcessed int,
@StatsErrors int;
IF @DatabaseName IS NULL OR LTRIM(RTRIM(@DatabaseName)) = N''
THROW 50101, N'usp_RunMaintenance: не задан параметр @DatabaseName.', 1;
IF DB_ID(@DatabaseName) IS NULL
THROW 50102, N'usp_RunMaintenance: указанная база не существует.', 1;
SELECT
@IsEnabled = IsEnabled,
@MinPages = MinPages,
@MinFrag = MinFrag,
@MaxDop = MaxDop,
@MinRows = MinRows,
@MinModPct = MinModPct,
@MinModAbs = MinModAbs,
@LockTimeoutMs = LockTimeoutMs,
@RebuildCutoffTime = RebuildCutoffTime
FROM dbo.MaintenanceConfig
WHERE DatabaseName = @DatabaseName;
IF @IsEnabled IS NULL
THROW 50103, N'usp_RunMaintenance: для базы отсутствует запись в dbo.MaintenanceConfig.', 1;
IF @RebuildCutoffTime IS NOT NULL
BEGIN
SET @CutoffDateTime =
DATEADD
(
SECOND,
DATEDIFF(SECOND, CONVERT(time(0), '00:00:00'), @RebuildCutoffTime),
CONVERT(datetime2(0), CONVERT(date, SYSDATETIME()))
);
END;
INSERT INTO dbo.MaintenanceRun
(
ServerName,
DatabaseName,
JobName,
Initiator,
SessionId,
StartTime,
Status,
MinPages,
MinFrag,
MaxDop,
MinRows,
MinModPct,
MinModAbs,
LockTimeoutMs,
CutoffTime
)
VALUES
(
@@SERVERNAME,
@DatabaseName,
NULL,
'SQLAGENT',
@@SPID,
SYSDATETIME(),
'RUNNING',
@MinPages,
@MinFrag,
@MaxDop,
@MinRows,
@MinModPct,
@MinModAbs,
@LockTimeoutMs,
@CutoffDateTime
);
SET @RunId = SCOPE_IDENTITY();
INSERT INTO dbo.MaintenanceLog
(
RunId,
TaskType,
TaskId,
LogLevel,
StepName,
Message
)
VALUES
(
@RunId,
'RUN',
NULL,
'INFO',
'START_RUN',
N'Старт обслуживания базы ' + QUOTENAME(@DatabaseName)
);
IF @IsEnabled = 0
BEGIN
INSERT INTO dbo.MaintenanceLog
(
RunId, TaskType, TaskId, LogLevel, StepName, Message
)
VALUES
(
@RunId, 'RUN', NULL, 'WARN', 'CANCELLED_RUN',
N'Обслуживание базы отключено в dbo.MaintenanceConfig. Отмена'
);
UPDATE dbo.MaintenanceRun
SET
EndTime = SYSDATETIME(),
Status = 'CANCELLED',
ErrorMessage = NULL,
IndexCandidates = 0,
IndexProcessed = 0,
IndexErrors = 0,
IndexLockTimeouts = 0,
IndexDeadlocks = 0,
IndexTimeLimitSkipped = 0,
StatsCandidates = 0,
StatsProcessed = 0,
StatsErrors = 0
WHERE RunId = @RunId;
RETURN;
END;
BEGIN TRY
EXEC dbo.usp_Index_SelectCandidates
@DatabaseName = @DatabaseName,
@RunId = @RunId,
@PhaseName = 'LIMITED';
EXEC dbo.usp_Index_RebuildProcess
@DatabaseName = @DatabaseName,
@RunId = @RunId,
@PhaseName = 'LIMITED';
EXEC dbo.usp_Index_SelectCandidates
@DatabaseName = @DatabaseName,
@RunId = @RunId,
@PhaseName = 'SAMPLED';
EXEC dbo.usp_Index_RebuildProcess
@DatabaseName = @DatabaseName,
@RunId = @RunId,
@PhaseName = 'SAMPLED';
EXEC dbo.usp_UpdateStatistics_SelectCandidates
@DatabaseName = @DatabaseName,
@RunId = @RunId;
EXEC dbo.usp_UpdateStatistics_Process
@DatabaseName = @DatabaseName,
@RunId = @RunId;
SELECT
@IndexCandidates = COUNT(*),
@IndexProcessed = SUM(CASE WHEN Status = 'DONE' THEN 1 ELSE 0 END),
@IndexErrors = SUM(CASE WHEN Status = 'ERROR' THEN 1 ELSE 0 END),
@IndexLockTimeouts = SUM(CASE WHEN Status = 'LOCK_TIMEOUT' THEN 1 ELSE 0 END),
@IndexDeadlocks = SUM(CASE WHEN Status = 'DEADLOCK' THEN 1 ELSE 0 END),
@IndexTimeLimitSkipped = SUM(CASE WHEN Status = 'TIME_LIMIT' THEN 1 ELSE 0 END)
FROM dbo.IndexMaintenanceTask
WHERE RunId = @RunId
AND DatabaseName = @DatabaseName;
SELECT
@StatsCandidates = COUNT(*),
@StatsProcessed = SUM(CASE WHEN Status = 'DONE' THEN 1 ELSE 0 END),
@StatsErrors = SUM(CASE WHEN Status = 'ERROR' THEN 1 ELSE 0 END)
FROM dbo.StatsMaintenanceTask
WHERE RunId = @RunId
AND DatabaseName = @DatabaseName;
SELECT
@IndexCandidates = ISNULL(@IndexCandidates, 0),
@IndexProcessed = ISNULL(@IndexProcessed, 0),
@IndexErrors = ISNULL(@IndexErrors, 0),
@IndexLockTimeouts = ISNULL(@IndexLockTimeouts, 0),
@IndexDeadlocks = ISNULL(@IndexDeadlocks, 0),
@IndexTimeLimitSkipped = ISNULL(@IndexTimeLimitSkipped, 0),
@StatsCandidates = ISNULL(@StatsCandidates, 0),
@StatsProcessed = ISNULL(@StatsProcessed, 0),
@StatsErrors = ISNULL(@StatsErrors, 0);
IF @IndexErrors > 0 OR @StatsErrors > 0
SET @FinalStatus = 'ERROR';
ELSE IF @IndexLockTimeouts > 0 OR @IndexDeadlocks > 0 OR @IndexTimeLimitSkipped > 0
SET @FinalStatus = 'WARNING';
ELSE
SET @FinalStatus = 'SUCCESS';
UPDATE dbo.MaintenanceRun
SET
EndTime = SYSDATETIME(),
Status = @FinalStatus,
ErrorMessage = NULL,
IndexCandidates = @IndexCandidates,
IndexProcessed = @IndexProcessed,
IndexErrors = @IndexErrors,
IndexLockTimeouts = @IndexLockTimeouts,
IndexDeadlocks = @IndexDeadlocks,
IndexTimeLimitSkipped = @IndexTimeLimitSkipped,
StatsCandidates = @StatsCandidates,
StatsProcessed = @StatsProcessed,
StatsErrors = @StatsErrors
WHERE RunId = @RunId;
INSERT INTO dbo.MaintenanceLog
(
RunId, TaskType, TaskId, LogLevel, StepName, Message
)
VALUES
(
@RunId, 'RUN', NULL, 'INFO', 'FINISH_RUN',
N'Запуск обслуживания базы ' + QUOTENAME(@DatabaseName) +
N' завершен. Итоговый статус: ' + @FinalStatus + N'.'
);
END TRY
BEGIN CATCH
SET @ErrorMessage = ERROR_MESSAGE();
SELECT
@IndexCandidates = COUNT(*),
@IndexProcessed = SUM(CASE WHEN Status = 'DONE' THEN 1 ELSE 0 END),
@IndexErrors = SUM(CASE WHEN Status = 'ERROR' THEN 1 ELSE 0 END),
@IndexLockTimeouts = SUM(CASE WHEN Status = 'LOCK_TIMEOUT' THEN 1 ELSE 0 END),
@IndexDeadlocks = SUM(CASE WHEN Status = 'DEADLOCK' THEN 1 ELSE 0 END),
@IndexTimeLimitSkipped = SUM(CASE WHEN Status = 'TIME_LIMIT' THEN 1 ELSE 0 END)
FROM dbo.IndexMaintenanceTask
WHERE RunId = @RunId
AND DatabaseName = @DatabaseName;
SELECT
@StatsCandidates = COUNT(*),
@StatsProcessed = SUM(CASE WHEN Status = 'DONE' THEN 1 ELSE 0 END),
@StatsErrors = SUM(CASE WHEN Status = 'ERROR' THEN 1 ELSE 0 END)
FROM dbo.StatsMaintenanceTask
WHERE RunId = @RunId
AND DatabaseName = @DatabaseName;
SELECT
@IndexCandidates = ISNULL(@IndexCandidates, 0),
@IndexProcessed = ISNULL(@IndexProcessed, 0),
@IndexErrors = ISNULL(@IndexErrors, 0),
@IndexLockTimeouts = ISNULL(@IndexLockTimeouts, 0),
@IndexDeadlocks = ISNULL(@IndexDeadlocks, 0),
@IndexTimeLimitSkipped = ISNULL(@IndexTimeLimitSkipped, 0),
@StatsCandidates = ISNULL(@StatsCandidates, 0),
@StatsProcessed = ISNULL(@StatsProcessed, 0),
@StatsErrors = ISNULL(@StatsErrors, 0);
UPDATE dbo.MaintenanceRun
SET
EndTime = SYSDATETIME(),
Status = 'ERROR',
ErrorMessage = @ErrorMessage,
IndexCandidates = @IndexCandidates,
IndexProcessed = @IndexProcessed,
IndexErrors = @IndexErrors,
IndexLockTimeouts = @IndexLockTimeouts,
IndexDeadlocks = @IndexDeadlocks,
IndexTimeLimitSkipped = @IndexTimeLimitSkipped,
StatsCandidates = @StatsCandidates,
StatsProcessed = @StatsProcessed,
StatsErrors = @StatsErrors
WHERE RunId = @RunId;
INSERT INTO dbo.MaintenanceLog
(
RunId, TaskType, TaskId, LogLevel, StepName, Message
)
VALUES
(
@RunId, 'RUN', NULL, 'ERROR', 'RUN_ERROR',
N'Запуск обслуживания базы ' + QUOTENAME(@DatabaseName) +
N' завершился с ошибкой: ' + @ErrorMessage
);
THROW;
END CATCH;
END;
GO
/*-----------------------------------------------------------
Отображаем конфиг
------------------------------------------------------------*/
SELECT *
FROM dbo.MaintenanceConfig
ORDER BY DatabaseName;
GO