Использование триггера DML «AFTER INSERT, UPDATE, DELETE».Как ссылка запускает строку? - PullRequest
0 голосов
/ 31 декабря 2018

У меня есть главная таблица, в которой я хочу суммировать значения из строк в дополнительной таблице.Одна или несколько строк Sub будут ссылаться на одну главную строку.Я хочу, чтобы Main.Total был суммой всех соответствующих значений Sub.Amount.Я пытаюсь реализовать триггер AFTER INSERT, UPDATE, DELETE для достижения этой цели.Я открыт для альтернативных решений помимо триггера, таких как использование представления, если это лучший способ приблизиться к этому.Даже если есть лучшее решение, мне все равно интересно узнать, как бы я решил это с помощью триггера, хотя бы для академических целей.Вот упрощенный пример:

CREATE DATABASE TEST;
GO
USE TEST;
GO

CREATE TABLE Main (
    Id INT
    ,Total INT DEFAULT(0)
    );
GO

CREATE TABLE Sub (
    Main_fk INT
    ,Sub_Id INT
    ,Amount INT
    );
GO

CREATE TRIGGER Update_Main_Total
    ON Sub
    AFTER INSERT, UPDATE, DELETE
    AS BEGIN
        DECLARE @Main_Id INT = (
            --  The Main_fk value for the inserted/updated/deleted
            --  Sub row that caused the trigger to fire
            );
        UPDATE Main
        SET Total = (
            SELECT SUM(Amount)
            FROM Sub
            WHERE Main_fk = @Main_Id
            )
        WHERE Id = @Main_Id;
    END;
GO

Ответы [ 2 ]

0 голосов
/ 31 декабря 2018

В SQL Server нет триггеров для каждой строки.Вместо этого в триггере вы можете получить доступ к псевдотаблицам inserted и deleted.inserted содержит вставленные строки или измененную версию строки для обновления и deleted удаленные строки или версию строки перед обновлением.

Так что вам нужно использоватьчто-то вроде следующего, чтобы обновить сумму.Сначала он получает сумму для идентификатора для inserted и аналог для deleted, затем полное объединение результатов - здесь требуется полное объединение, поскольку не все идентификаторы обязательно находятся в обоих наборах, а затем обновляется main.

UPDATE m
       SET m.total = m.total + z.total
       FROM main m
            INNER JOIN (SELECT coalesce(x.main_fk, y.main_fk) main_fk,
                               coalesce(x.total, 0) - coalesce(y.total, 0) total
                               FROM (SELECT i.main_fk,
                                            sum(i.amount) total
                                            FROM inserted i
                                            GROUP BY i.main_fk) x
                                    FULL JOIN (SELECT d.main_fk,
                                                      sum(d.amount) total
                                                      FROM delete d
                                                      GROUP BY d.main_fk) y
                                              ON y.main_fk = x.main_fk) z
                       ON z.main_fk = m.id;

(Возможно, вы хотите установить total в main на NULL, если не осталось ни одной записи sub. В приведенном выше запросе этого не делается, для этого потребуетсянекоторая дополнительная работа.)

Но физическое хранение таких цифр, которые могут быть вычислены из других, несет риск несоответствий.Если триггер отключен или не работает (должным образом) по другим причинам в течение определенного периода времени, изменения в таблице sub (не правильно) не отражаются в таблице main.У вас там будут ложные цифры, и, возможно, вы их даже не узнаете.

Я бы избегал таких вещей и выбирал бы вид вместо этого, если это возможно.(Писать тоже проще.;))

CREATE VIEW main_with_total
AS
SELECT m.id,
       sum(s.amount) total
       FROM main m
            LEFT JOIN sub s
                      ON s.main_fk = m.id
       GROUP BY m.id;

(Здесь вы получите NULL как total для идентификатора, в котором нет записей sub. Если вы хотите 0, вместо этого изменитевыражение для total до coalesce(sum(s.amount), 0).)

0 голосов
/ 31 декабря 2018

Я смог понять это довольно просто.Спасибо @Dale_Burrell за руководство в комментарии.Я нашел эту ссылку более прямым ответом на информацию, которую я получил после: https://www.mssqltips.com/sqlservertip/2342/understanding-sql-server-inserted-and-deleted-tables-for-dml-triggers/

Короче говоря, мне нужно было ссылаться на встроенные таблицы inserted и deleted для ссылки на запускстрока.Мой вопрос был сразу «а как же обновление?».Обновление будет использовать обе эти таблицы (подумайте об этом или воспользуйтесь приведенной выше ссылкой для объяснения).Вот исправленный триггер:

CREATE TRIGGER Update_Main_Total
    ON Sub
    AFTER INSERT, UPDATE, DELETE
    AS BEGIN
        DECLARE @main_id INT;

        IF EXISTS (SELECT * FROM inserted)
            SELECT @main_id = Main_fk
            FROM inserted;

        ELSE
            SELECT @main_id = Main_fk
            FROM deleted;

        UPDATE Main
        SET Total = (
            SELECT SUM(Amount)
            FROM Sub
            WHERE Main_fk = @Main_Id
            )
        WHERE Id = @Main_Id;
    END;
GO
...