web-dev-qa-db-fra.com

Déclencheur pour créer une table variable à envoyer au courtier de service

J'ai configuré un courtier de service pour la première fois et je l'ai testé ... a l'air bien. Chaque fois qu'une opération DML arrive sur une table spécifique; Disons que la table de prix, une gâchette appelle une procédure stockée qui lui transmettait les tables insérées et supprimées sous forme de paramètres XML qui enverront le message (contenant les 2 XMLS) à la file d'attente.

La procédure d'activation traite ensuite la file d'attente et insère les modifications apportées au tableau initial dans une table d'audit ... fonctionne bien.

Le problème se produit lorsque la mise à jour sur la table de prix est massive. Dans mon cas, j'ai testé une mise à jour de changement de prix sur 105 000 articles. Les variables xml insérées et supprimées contiennent 1,3 million de lignes chacune ... qui provoque des problèmes majeurs: TEMPDB augmente infiniment et le processeur augmente à 95%, ce qui est compréhensible ... mais ne semble jamais compléter l'opération.

Le code de la gâchette est Price_tab_audit_trig . La gâchette appelle la procédure sendmsgservicebroker pour envoyer le message à la file d'attente. Voir les commentaires ci-dessous pour la procédure d'activation.

J'essaie de comprendre comment je peux décomposer les variables insérées et supprimées (messages) en XML plus petits. Mes questions/pensées sont: puis-je créer des variables de table et passer cela dans le message au lieu de XML? Si oui, comment créer des tables variables dans un déclencheur? Devrais-je créer des tables temporaires uniques et les références dans le message?

Toute autre suggestion, est la bienvenue. Merci d'avance JG

2
JohnG

Vous pouvez envoyer les données XML dans des morceaux prédéfinis en utilisant quelque chose comme la gâchette dans la configuration suivante.

Créez le banc d'essai dans TEMPDB:

USE tempdb;

IF EXISTS (SELECT 1 FROM sys.triggers t WHERE t.name = 'TriggerTest_Chunked')
DROP TRIGGER dbo.TriggerTest_Chunked;


IF EXISTS(SELECT 1 FROM sys.tables t WHERE t.name = 'TriggerTest')
DROP TABLE dbo.TriggerTest;

CREATE TABLE dbo.TriggerTest
(
    ID INT NOT NULL
);

IF EXISTS(SELECT 1 FROM sys.tables t WHERE t.name = 'TriggerTestTarget')
DROP TABLE dbo.TriggerTestTarget;

CREATE TABLE dbo.TriggerTestTarget
(
    OperationType INT NOT NULL
    , DEETS XML NOT NULL
);

La gâchette:

CREATE TRIGGER dbo.TriggerTest_Chunked
ON dbo.TriggerTest
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
    DECLARE @RowStart INT;
    DECLARE @RowEnd INT;
    DECLARE @RowCount INT;
    DECLARE @BatchSize INT;
    SET @BatchSize = 1000;

    SET @RowStart = 0;

    SELECT @RowCount = COUNT(1) 
    FROM inserted;

    IF @RowCount > @BatchSize 
        SET @RowEnd = @BatchSize
    ELSE
        SET @RowEnd = @RowCount;

    WHILE @RowEnd <= @RowCount AND (@RowStart < @RowEnd)
    BEGIN
        INSERT INTO dbo.TriggerTestTarget (OperationType, DEETS)
        SELECT 1, (
            SELECT ID
            FROM (
                    SELECT *
                        , rn = ROW_NUMBER() OVER (ORDER BY ID)
                    FROM inserted
                ) i_rn
            WHERE i_rn.rn > @RowStart
                AND i_rn.rn <= @RowEnd
            FOR XML PATH('')
        );

        SET @RowStart = @RowStart + @BatchSize;
        IF @RowEnd + @BatchSize > @RowCount
            SET @BatchSize = @RowCount - @RowEnd;
        SET @RowEnd = @RowEnd + @BatchSize;
    END

    SET @RowStart = 0;

    SELECT @RowCount = COUNT(1) 
    FROM deleted;

    IF @RowCount > @BatchSize 
        SET @RowEnd = @BatchSize
    ELSE
        SET @RowEnd = @RowCount;

    WHILE @RowEnd <= @RowCount AND (@RowStart < @RowEnd)
    BEGIN
        INSERT INTO dbo.TriggerTestTarget (OperationType, DEETS)
        SELECT 2, (
            SELECT ID
            FROM (
                    SELECT *
                        , rn = ROW_NUMBER() OVER (ORDER BY ID)
                    FROM deleted
                ) i_rn
            WHERE i_rn.rn > @RowStart
                AND i_rn.rn <= @RowEnd
            FOR XML PATH('')
        );

        SET @RowStart = @RowStart + @BatchSize;
        IF @RowEnd + @BatchSize > @RowCount
            SET @BatchSize = @RowCount - @RowEnd;
        SET @RowEnd = @RowEnd + @BatchSize;
    END
END
GO

Insérez quelques données de test pour voir ce que fait la gâchette:

INSERT INTO dbo.TriggerTest (ID)
SELECT ROW_NUMBER() OVER (ORDER BY o1.object_id, o2.object_id) 
FROM sys.objects o1
    , sys.objects o2;

Montrer les résultats:

SELECT *
FROM dbo.TriggerTest;

SELECT *
FROM dbo.TriggerTestTarget;

DELETE TOP(100)
FROM dbo.TriggerTest;

SELECT *
FROM dbo.TriggerTestTarget;
1
Max Vernon