Загрузка данных


USE [replTransVas];
SET NOCOUNT ON;

IF CONVERT(sysname, SERVERPROPERTY('ServerName'))
    <> N'WIN-5ODCFIGQRP3\SQL2016SQL'
BEGIN
    ;THROW 50001, N'Неверный экземпляр SQL Server.', 1;
END;

IF @@TRANCOUNT <> 0
BEGIN
    ;THROW 50002, N'Запустите блок вне открытой транзакции.', 1;
END;

DECLARE @jobName sysname =
    N'WIN-5ODCFIGQRP3\SQL20-myNewVasya-V3Trans-WIN-5ODCFIGQRP3\SQL20-3';

DECLARE @program nvarchar(128) =
    N'WIN-5ODCFIGQRP3_myNewVasya_V3Trans';

DECLARE @jobId uniqueidentifier;
DECLARE @obj int = OBJECT_ID(N'dbo.sb_answer', N'U');
DECLARE @deadline datetime2;
DECLARE @running bit;
DECLARE @sessions bit;

SELECT @jobId = job_id
FROM msdb.dbo.sysjobs
WHERE name = @jobName;

IF @jobId IS NULL
BEGIN
    ;THROW 50003, N'Задание Distribution Agent не найдено.', 1;
END;

IF NOT EXISTS
(
    SELECT 1
    FROM distribution.dbo.MSdistribution_agents
    WHERE name = @jobName
      AND publisher_db = N'myNewVasya'
      AND publication = N'V3Trans'
      AND subscriber_db = N'replTransVas'
)
BEGIN
    ;THROW 50004, N'Не подтверждена нужная подписка. Остановка отменена.', 1;
END;

-- До остановки проверяем наличие и тип обоих индексов.
IF
(
    SELECT COUNT(*)
    FROM sys.indexes
    WHERE object_id = @obj
      AND name IN
      (
          N'IX_sb_answer_MtsMemberLatest',
          N'IX_sb_answer_MtsParticipantLatest'
      )
      AND type = 2
      AND is_unique = 0
      AND is_primary_key = 0
      AND is_unique_constraint = 0
      AND is_hypothetical = 0
) <> 2
BEGIN
    ;THROW 50005, N'Индексы отличаются от ожидаемых. Остановка отменена.', 1;
END;

-- 1. Запрашиваем штатную остановку только нужного задания.
IF EXISTS
(
    SELECT 1
    FROM msdb.dbo.sysjobactivity
    WHERE job_id = @jobId
      AND session_id =
          (SELECT MAX(session_id) FROM msdb.dbo.syssessions)
      AND start_execution_date IS NOT NULL
      AND stop_execution_date IS NULL
)
BEGIN
    BEGIN TRY
        EXEC msdb.dbo.sp_stop_job @job_id = @jobId;
    END TRY
    BEGIN CATCH
        -- Задание могло завершиться между проверкой и остановкой.
        IF EXISTS
        (
            SELECT 1
            FROM msdb.dbo.sysjobactivity
            WHERE job_id = @jobId
              AND session_id =
                  (SELECT MAX(session_id) FROM msdb.dbo.syssessions)
              AND start_execution_date IS NOT NULL
              AND stop_execution_date IS NULL
        )
        BEGIN
            THROW;
        END;
    END CATCH;
END;

-- 2. Ждем завершения задания и его SQL-сессий.
SET @deadline = DATEADD(SECOND, 60, SYSDATETIME());

WHILE 1 = 1
BEGIN
    SET @running = 0;
    SET @sessions = 0;

    IF EXISTS
    (
        SELECT 1
        FROM msdb.dbo.sysjobactivity
        WHERE job_id = @jobId
          AND session_id =
              (SELECT MAX(session_id) FROM msdb.dbo.syssessions)
          AND start_execution_date IS NOT NULL
          AND stop_execution_date IS NULL
    )
        SET @running = 1;

    IF EXISTS
    (
        SELECT 1
        FROM sys.dm_exec_sessions
        WHERE program_name IN (@program, @jobName)
    )
        SET @sessions = 1;

    IF @running = 0 AND @sessions = 0
        BREAK;

    IF SYSDATETIME() >= @deadline
    BEGIN
        SELECT
            N'WAITING_FOR_STOP' AS result,
            @running AS job_still_running,
            @sessions AS agent_sessions_exist,
            N'Индексы не изменены этим запуском. Повторите блок позже.'
                AS next_action;

        SELECT
            s.session_id,
            s.program_name,
            s.status AS session_status,
            r.command,
            r.status AS request_status,
            r.wait_type,
            r.blocking_session_id,
            r.percent_complete,
            r.estimated_completion_time AS estimated_remaining_ms
        FROM sys.dm_exec_sessions AS s
        LEFT JOIN sys.dm_exec_requests AS r
            ON r.session_id = s.session_id
        WHERE s.program_name IN (@program, @jobName);

        RETURN;
    END;

    WAITFOR DELAY '00:00:02';
END;

-- 3. Отключаем оба индекса в одной транзакции.
-- При ошибке изменения этого этапа откатываются.
BEGIN TRY
    EXEC sys.sp_executesql N'
        SET LOCK_TIMEOUT 5000;
        SET XACT_ABORT ON;

        BEGIN TRY
            BEGIN TRANSACTION;

            IF EXISTS
            (
                SELECT 1 FROM sys.indexes
                WHERE object_id = OBJECT_ID(N''dbo.sb_answer'')
                  AND name = N''IX_sb_answer_MtsMemberLatest''
                  AND is_disabled = 0
            )
                ALTER INDEX [IX_sb_answer_MtsMemberLatest]
                    ON dbo.sb_answer DISABLE;

            IF EXISTS
            (
                SELECT 1 FROM sys.indexes
                WHERE object_id = OBJECT_ID(N''dbo.sb_answer'')
                  AND name = N''IX_sb_answer_MtsParticipantLatest''
                  AND is_disabled = 0
            )
                ALTER INDEX [IX_sb_answer_MtsParticipantLatest]
                    ON dbo.sb_answer DISABLE;

            COMMIT TRANSACTION;
        END TRY
        BEGIN CATCH
            IF XACT_STATE() <> 0
                ROLLBACK TRANSACTION;
            THROW;
        END CATCH;
    ';
END TRY
BEGIN CATCH
    SELECT
        N'INDEX_CHANGE_FAILED' AS result,
        ERROR_NUMBER() AS error_number,
        ERROR_MESSAGE() AS error_message;

    -- Возобновляем доставку, даже если эксперимент не удался.
    BEGIN TRY
        EXEC msdb.dbo.sp_start_job @job_id = @jobId;

        SELECT
            N'AGENT_RESTART_REQUESTED_AFTER_FAILURE' AS result;
    END TRY
    BEGIN CATCH
        SELECT
            N'AGENT_START_FAILED' AS result,
            ERROR_MESSAGE() AS error_message,
            @jobName AS job_to_start;
    END CATCH;

    SELECT name, type_desc, is_disabled
    FROM sys.indexes
    WHERE object_id = @obj;

    RETURN;
END CATCH;

-- 4. Запускаем доставку с отключенными индексами.
BEGIN TRY
    EXEC msdb.dbo.sp_start_job @job_id = @jobId;
END TRY
BEGIN CATCH
    SELECT
        N'INDEXES_DISABLED_BUT_AGENT_START_FAILED' AS result,
        ERROR_MESSAGE() AS error_message,
        @jobName AS job_to_start;

    RETURN;
END CATCH;

SELECT
    N'INDEXES_DISABLED_AGENT_START_REQUESTED' AS result,
    SYSDATETIME() AS experiment_started_at;

SELECT name, type_desc, is_disabled
FROM sys.indexes
WHERE object_id = @obj
ORDER BY index_id;