Удалите каскад или запустите Fur Multiple Cascade Path с помощью внешних ключей - PullRequest
0 голосов
/ 09 ноября 2018

У меня проблема с действиями удаления в табличной структуре с несколькими каскадными путями.
Я знаю, что тема часто обсуждается здесь, и можно найти хорошее описание / решение, например. в
https://www.mssqltips.com/sqlservertip/2733/solving-the-sql-server-multiple-cascade-path-issue-with-a-trigger с решением использовать триггер INSTEAD OF.

Но моя структура таблицы немного отличается из-за обнуляемого -граничного ключа. Следующее изображение показывает желаемую структуру:

                            +-----------------+                       +----------------+
    +------(fk not null)----| ProductionSteps |<-----(fk not null)----| CUSTOMER-TABLE |
    |     delete cascade    +-----------------+     delete cascade    +----------------+
    |                                ^
    V                                |
+--------------------+          (fk nullable)
|  ProductionOrders  |        on delete set null
+--------------------+               |
    ^                                |
    |                       +-----------------+
    +------(fk not null)----| ProcessingData  |
          delete cascade    +-----------------+

И я не хочу использовать INSTEAD OF Delete-Trigger для ProductionSteps-Table, потому что мои таблицы являются частью поставляемого программного продукта, и клиент может расширить систему дополнительными таблицами, например, ТАБЛИЦА КЛИЕНТОВ с fk to ProductionSteps (удалить каскад). Таким образом, с помощью триггера INSTEAD OF клиент должен расширить поставленный триггер для своей таблицы CUSTOMER-TABLE, и, следовательно, триггер не может быть обновлен. Также клиент не знаком с триггерами.
Поэтому я надеюсь, что есть альтернативное решение.

В конце добавлены sql-команды для создания таблиц и тестовых данных. Внутри также есть подробное описание, включая сообщения об ошибках. Среда: SQLServer 2016

требуемые внешние ключи (как показано на рисунке):

  • ProductionSteps: от fk до ProductionOrders (не ноль, удалить каскад)
  • ProcessingData 1. FK для ProductionOrders (не ноль, удалить каскад) и 2. fk to ProductionSteps ( обнуляется, при удалении набора нуль )

Из-за нескольких каскадных путей «при удалении установлен ноль» не представляется возможным.

==> новая версия с изменениями:

  • ProcessingData: от fk до ProductionSteps сейчас без действия удаления , только обнуляемый
  • ProductionSteps: новый FOR-DELETE-Trigger для установки ФК в обработке данных в NULL

Похоже, что при использовании этой версии триггер не выполняется при удалении записей из ProductionSteps, поэтому возникает ошибка SQL.

Есть ли альтернативное решение, кроме INSTEAD OF delete-trigger?

-- -----------------------------------------------------------------------------------------------------
--      Step 1:     create table ProductionOrders
-- -----------------------------------------------------------------------------------------------------
CREATE TABLE [ProductionOrders](
    [Sequence] [bigint] IDENTITY(1,1) NOT NULL,
    [Code] [nvarchar](32) NOT NULL,
 CONSTRAINT [PK_ProductionOrders] PRIMARY KEY CLUSTERED 
(
    [Code] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

-- -----------------------------------------------------------------------------------------------------
--      Step 2a:    create table ProductionSteps
-- -----------------------------------------------------------------------------------------------------
CREATE TABLE [ProductionSteps](
    [Sequence] [bigint] IDENTITY(1,1) NOT NULL,
    [ProductionOrderCode] [nvarchar](32) NOT NULL,
    [Code] [nvarchar](32) NOT NULL,
 CONSTRAINT [PK_ProductionSteps] PRIMARY KEY CLUSTERED 
(
    [ProductionOrderCode] ASC,
    [Code] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

-- -----------------------------------------------------------------------------------------------------
--      Step 2b:    fk  ProductionSteps ==> ProductionOrders  (not null / delete cascade)
-- -----------------------------------------------------------------------------------------------------
ALTER TABLE [ProductionSteps]  WITH CHECK ADD  CONSTRAINT [FK_ProductionSteps_ProductionOrder] FOREIGN KEY([ProductionOrderCode])
REFERENCES [ProductionOrders] ([Code])
ON DELETE CASCADE
GO

ALTER TABLE [ProductionSteps] CHECK CONSTRAINT [FK_ProductionSteps_ProductionOrder]
GO

-- -----------------------------------------------------------------------------------------------------
--      Step 3a:    create table ProcessingData
-- -----------------------------------------------------------------------------------------------------
CREATE TABLE [ProcessingData](
    [Sequence] [bigint] IDENTITY(1,1) NOT NULL,
    [ProductionOrderCode] [nvarchar](32) NOT NULL,
    [WorkCenterCode] [nvarchar](32) NOT NULL,
    [ProductionOrderStepCode] [nvarchar](32) NULL,
    [ProductionStepCode] [nvarchar](32) NULL,
    [Order] [int] NOT NULL,
 CONSTRAINT [PK_ProcessingData] PRIMARY KEY CLUSTERED 
(
    [ProductionOrderCode] ASC,
    [WorkCenterCode] ASC,
    [Order] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

-- -----------------------------------------------------------------------------------------------------
--      Step 3b:    fk  ProcessingData ==> ProductionOrders  (not null / delete cascade)
-- -----------------------------------------------------------------------------------------------------
ALTER TABLE [ProcessingData]  WITH NOCHECK ADD  CONSTRAINT [FK_ProcessingData_ProductionOrders] FOREIGN KEY([ProductionOrderCode])
REFERENCES [ProductionOrders] ([Code])
ON DELETE CASCADE
GO

ALTER TABLE [ProcessingData] CHECK CONSTRAINT [FK_ProcessingData_ProductionOrders]
GO

-- -----------------------------------------------------------------------------------------------------
--      Step 3c:    fk  ProcessingData ==> ProductionSteps  (nullable)
--
--              PROBLEM:    "on delete set null"  in the next SQL-command isn't feasible !!!
--                          executing the SQL-command __WITH__ including "on delete set null" causes an ERROR:
--
--                  Msg 1785, Level 16, State 0, Line 2 
--                  Introducing FOREIGN KEY constraint 'FK_ProcessingData_ProductionSteps' on table 'ProcessingData' may cause cycles or multiple cascade paths. 
--                  Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints. 
--                  Msg 1750, Level 16, State 1, Line 2 
--                  Could not create constraint or index. See previous errors. 
--
-- -----------------------------------------------------------------------------------------------------
ALTER TABLE [ProcessingData]  WITH CHECK ADD  CONSTRAINT [FK_ProcessingData_ProductionSteps] FOREIGN KEY([ProductionOrderStepCode], [ProductionStepCode])
-- REFERENCES [ProductionSteps] ([ProductionOrderCode], [Code])  on delete set null
   REFERENCES [ProductionSteps] ([ProductionOrderCode], [Code])
GO

ALTER TABLE [ProcessingData] CHECK CONSTRAINT [FK_ProcessingData_ProductionSteps]
GO

-- -----------------------------------------------------------------------------------------------------
--      Step 4:     create new trigger for table  ProductionSteps  instead of "on delete set null"
-- -----------------------------------------------------------------------------------------------------
CREATE TRIGGER [TRG_ProcessingData_ProductionStepsDelete]
ON [ProductionSteps]
FOR DELETE
AS
BEGIN
    SET NOCOUNT ON
    UPDATE [ProcessingData]
    SET [ProductionOrderStepCode] = NULL, [ProductionStepCode] = NULL
    FROM [ProcessingData] PD
    INNER JOIN DELETED D
    ON PD.ProductionOrderStepCode = D.ProductionOrderCode
    AND PD.ProductionStepCode = D.Code
END
GO

ALTER TABLE [ProductionSteps] ENABLE TRIGGER [TRG_ProcessingData_ProductionStepsDelete]
GO

-- -----------------------------------------------------------------------------------------------------
--      Step 5:     create some test-records in tables
-- -----------------------------------------------------------------------------------------------------
INSERT INTO [ProductionOrders] ([Code])   VALUES   ('po1')
GO

INSERT INTO [ProductionSteps] ([ProductionOrderCode] ,[Code]) VALUES ('po1', 'ps1')
INSERT INTO [ProductionSteps] ([ProductionOrderCode] ,[Code]) VALUES ('po1', 'ps2')
GO

INSERT INTO [ProcessingData] ([ProductionOrderCode], [WorkCenterCode], [ProductionOrderStepCode],[ProductionStepCode],[Order])
     VALUES ('po1', 'wc1', 'po1', 'ps1', 1)
INSERT INTO [ProcessingData] ([ProductionOrderCode], [WorkCenterCode], [ProductionOrderStepCode],[ProductionStepCode],[Order])
     VALUES ('po1', 'wc1', 'po1', 'ps2', 2)
GO

-- -----------------------------------------------------------------------------------------------------
--      Step 6:     Test: delete all records in table  ProductionSteps
--
--          Executing the next SQL-Command causes an ERROR!!!   It seems, the trigger isn't executed
--
--          ERROR:
--              Msg 547, Level 16, State 0, Line 1
--              The DELETE statement conflicted with the REFERENCE constraint "FK_ProcessingData_ProductionSteps". 
--              The conflict occurred in database "TEST_DeleteSetNull", table "dbo.ProcessingData".
--
-- -----------------------------------------------------------------------------------------------------
DELETE FROM [ProductionSteps] 
GO
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...