SQL Server - обновить поле в одной таблице и обновить связанные поля в нескольких других таблицах, триггер, хранимую процедуру или их комбинацию? - PullRequest
0 голосов
/ 26 июня 2018

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

У меня есть следующие таблицы:

CREATE TABLE dbo.Customers 
(
    Cust_ID varchar(12) NOT NULL PRIMARY KEY,
    IsDeleted bit NOT NULL DEFAULT 0,
    Contact_ID varchar(12) NOT NULL FOREIGN KEY,
    Status_ID varchar(12) NOT NULL FOREIGN KEY,
);

CREATE TABLE dbo.CustomerContactDetails 
(
    Contact_ID varchar(12) NOT NULL PRIMARY KEY,
    IsDeleted bit NOT NULL DEFAULT 0,
);

CREATE TABLE dbo.CustomerStatus 
(
    Status_ID varchar(12) NOT NULL PRIMARY KEY, 
    IsDeleted bit NOT NULL DEFAULT 0,
);

CREATE TABLE dbo.AccountDetails 
(
    Account_ID varchar(12) NOT NULL PRIMARY KEY,
    IsDeleted bit NOT NULL DEFAULT 0,       
    Contact_ID varchar(12) NOT NULL FOREIGN KEY,
    Cust_ID varchar(12) NOT NULL FOREIGN KEY,
);

CREATE TABLE dbo.Transactions 
(
    Transaction_ID varchar(12) NOT NULL PRIMARY KEY,
    IsDeleted bit NOT NULL DEFAULT 0,
    Account_ID varchar(12) NOT NULL FOREIGN KEY,
);

CREATE TABLE dbo.Associations  
(
    Association_ID varchar(12) NOT NULL PRIMARY KEY, 
    Company_Name varchar(12) NOT NULL FOREIGN KEY, (uses Cust_ID in dbo.Customers)
    IsDeleted bit DEFAULT 0 NOT NULL,
    AssociatedCompany varchar(12) NOT NULL FOREIGN KEY, (uses Cust_ID in dbo.Customers)
);

Как видите, все эти таблицы имеют столбец IsDeleted с типом данных bit и значением по умолчанию 0 (для мягкого удаления). Я пытаюсь добиться того, чтобы при обновлении dbo.Customers(IsDeleted) до 1-го бита следующие записи автоматически обновлялись до 1-го бита:

  • dbo.CustomerContactDetails (IsDeleted)
  • dbo.CustomerStatus (IsDeleted)
  • dbo.AccountDetails (IsDeleted)
  • dbo.Transactions (IsDeleted)
  • dbo. Ассоциации (IsDeleted)

Моя первоначальная мысль - настроить хранимую процедуру, чтобы конечный пользователь мог легко обновить столбец dbo.Customers(IsDeleted), а затем триггер для обновления связанных записей, но я не уверен, является ли это лучшим подходом, и если так с чего начать.

Мои исследования по решению проблемы до сих пор сбили меня с толку, поскольку мне кажется, что я должен быть очень осторожным, чтобы мой код ограничивал обновление IsDeleted столбцов ONLY в качестве стандартной хранимой процедуры / триггера повлияет на все столбцы подряд.

Любое руководство здесь очень ценится.

Отредактировано с помощью приведенного ниже (работа в процессе).

ALTER PROC [dbo].[DeleteCustomer]
(
    @Cust_ID varchar(12)

) AS

    set nocount on;

    begin transaction 

    begin try

    UPDATE dbo.Customers
    SET IsDeleted = 1
    WHERE Cust_ID = @Cust_ID

    UPDATE ccd
    SET IsDeleted = 1 
    FROM dbo.CustomerContactDetails ccd
    INNER JOIN dbo.Customers C ON ccd.Contact_ID = C.Contact_ID
    WHERE c.Cust_ID = @Cust_ID

    UPDATE cs
    SET IsDeleted = 1 
    FROM dbo.CustomerStatus cs
    INNER JOIN dbo.Customers C ON cs.Status_ID = C.Status_ID
    WHERE c.Cust_ID = @Cust_ID

    UPDATE ad
    SET IsDeleted = 1 
    FROM dbo.AccountDetails ad
    INNER JOIN dbo.Customers C ON ad.Cust_ID = C.Cust_ID
    WHERE c.Cust_ID = @Cust_ID 

    UPDATE T
    SET IsDeleted = 1 
    FROM dbo.Transactions T
    INNER JOIN dbo.AccountDetails AD ON t.Account_ID = AD.Account_ID
    INNER JOIN dbo.Customers C ON ad.Cust_ID = C.Cust_ID
    WHERE c.Cust_ID = @Cust_ID

    UPDATE AA
    SET IsDeleted = 1 
    FROM dbo.Associations AA
    INNER JOIN dbo.Customers C ON AA.Company_Name = C.Cust_ID 
                            OR AA.AssociatedCompany = C.Cust_ID
    WHERE c.Cust_ID = @Cust_ID
    end try

    begin catch
    rollback transaction 
    throw
    end catch

Я успешно создал свою хранимую процедуру, но при выполнении ничего не происходит. На экране просто написано «выполняется», и так будет до тех пор, пока я не остановлю его. Есть идеи?

USE [db]
GO

DECLARE @return_value int

EXEC    @return_value = [dbo].[DeleteCustomer]
        @Cust_ID = N'C1'

SELECT  'Return Value' = @return_value

GO

Сообщение 266, Уровень 16, Состояние 2, Процедура DeleteCustomer, Строка 2 Число транзакций после EXECUTE указывает на несовпадающее количество операторов BEGIN и COMMIT. Предыдущий счет = 0, текущий счет = 1.

Ответы [ 3 ]

0 голосов
/ 26 июня 2018

Скорее всего, это сработает, вам, конечно, потребуется предоставить входные данные для остальных таблиц с соответствующими ключами отношений.

CREATE TRIGGER trg_Update_IsDeleted
ON dbo.Customers
AFTER UPDATE
AS
IF UPDATE(dbo.Customers)
    UPDATE  CustomerContactDetails
    SET     IsDeleted = Inserted.IsDeleted
    FROM    Inserted
    WHERE   CustomerContactDetails.Contact_ID = Inserted.Contact_ID

Это - достойное объяснение Inserted Логических таблиц, если интересно.

0 голосов
/ 27 июня 2018

Что-то в этом роде должно быть довольно близко. Я хотел бы предупредить вас, однако, ваши структуры таблиц являются проблемными в нескольких отношениях. Одна из самых больших проблем для меня заключается в том, что у вас есть Cust_ID в качестве varchar (12), но у вас нет столбца для имени клиента. Затем вы указали этот Cust_ID как внешний ключ в других таблицах. Я предполагаю, что эта колонка действительно имя клиента. И проблема в том, что вы создали свои таблицы таким образом, что вы не сможете изменить имя клиента после его создания. Как правило, это не очень хороший подход, поскольку имена людей и компаний могут меняться. Во всяком случае, это тема для другого дня.

CREATE PROC DeleteCustomer
(
    @Cust_ID varchar(12)
) AS

    set nocount on;

    begin transaction 

    begin try --we want ALL data to be updated or NONE of it so we use a single try/catch block

        UPDATE dbo.Customers
        SET IsDeleted = 1
        WHERE Cust_ID = @Cust_ID

        --we don't want to update this table. It looks like a lookup table.
        --UPDATE dbo.CustomerStatus
        --SET DeactivationDate = GETDATE()
        --WHERE @IsDeleted=IsDeleted

        UPDATE ccd
        SET IsDeleted = 1 
        FROM dbo.CustomerContactDetails ccd
        INNER JOIN dbo.Customers C ON ccd.Cust_ID = C.Cust_ID
        WHERE c.Cust_ID = @Cust_ID

        UPDATE cs
        SET IsDeleted = 1 
        FROM dbo.CustomerStatus cs
        INNER JOIN dbo.Customers C ON cs.Status_ID = C.Status_ID
        WHERE c.Cust_ID = @Cust_ID

        UPDATE ad
        SET IsDeleted = 1 
        FROM dbo.AccountDetails ad
        INNER JOIN dbo.Customers C ON ad.Cust_ID = C.Cust_ID
        WHERE c.Cust_ID = @Cust_ID

        UPDATE T
        SET IsDeleted = 1 
        FROM dbo.Transactions T
        INNER JOIN dbo.AccountDetails D ON T.Account_ID = D.Account_ID
        INN
        WHERE c.Cust_ID = @Cust_ID

        --you can do this with a single update instead of two by using a conditional OR in your join predicate
        --I would STRONGLY suggest that you don't interchange Company_Name and Cust_ID. A column name should not change between tables like that.
        UPDATE AA
        SET IsDeleted = 1 
        FROM dbo.Associations AA
        INNER JOIN dbo.Customers C ON AA.Company_Name = C.Cust_ID 
                                OR AA.AssociatedCompany = C.Cust_ID
        WHERE c.Cust_ID = @Cust_ID
    end try

    begin catch
        rollback transaction 
        throw --this will rethrow the exception so that you can handle it in your calling application
    end catch
0 голосов
/ 26 июня 2018

Что ж, вы можете использовать триггер на вашей таблице Customers для обновления связанных таблиц при каждом изменении Customers.is_deleted. SQL Server предоставляет виртуальные таблицы inserted и deleted в триггерах, так что вы можете сравнить, чтобы увидеть, изменился ли is_deleted, и обновить другие таблицы при необходимости.

Однако мне интересно, нужны ли вам дополнительные is_deleted поля для ваших "дочерних" таблиц. Например, если CustomerContactDetails.is_deleted всегда будет соответствовать Customers.is_deleted, вы можете просто создать представление, содержащее все контактные данные, и извлечь значение is_deleted из базовой таблицы Customers.

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

...