Sql Server добавление нескольких ограничений внешнего ключа в таблицу в транзакции

#sql-server #tsql #transactions #foreign-keys

Вопрос:

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

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

То, что я пробовал до сих пор:

 ALTER TABLE UoMConversion
ADD CONSTRAINT FK__UoMConversion__UnitOfMEasure__UoMFrom FOREIGN KEY(UoMFrom)
    REFERENCES UnitOfMeasure(UoMID)
    ON DELETE CASCADE;
ALTER TABLE UoMConversion
ADD CONSTRAINT FK__UoMConversion__UnitOfMEasure__UoMTo FOREIGN KEY(UoMTo)
    REFERENCES UnitOfMeasure(UoMID)
    ON DELETE CASCADE;
 

или

 ALTER TABLE UoMConversion
ADD CONSTRAINT FK__UoMConversion__UnitOfMEasure__UoMFrom FOREIGN KEY(UoMFrom)
    REFERENCES UnitOfMeasure(UoMID)
    ON DELETE CASCADE,
    CONSTRAINT FK__UoMConversion__UnitOfMEasure__UoMTo FOREIGN KEY(UoMTo)
    REFERENCES UnitOfMeasure(UoMID)
    ON DELETE CASCADE;
 

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

     BEGIN TRANSACTION CreateTables 
    BEGIN TRY
    USE ProductDB
    
        CREATE TABLE UnitOfMeasure(
            UoMID int not null identity(1,1) primary key,
            UoMDescription varchar(255) not null,
            UoMAbbreviation varchar(10) not null,
            UoMCategoryID int   -- FK__UnitOfMeasure__UnitOfMeasureCategory
        );
        
        
        CREATE TABLE UnitOfMeasureCategory(
            UoMCategoryID int not null identity(1,1) primary key,
            UoMCategory varchar(100) not null
        );
        
        CREATE TABLE UoMConversion (
            UoMConversionID int not null identity(1,1) primary key,
            UoMFrom int not null,   -- FK__UoMConversion__UnitOfMEasure__UoMFrom
            UoMTo int not null,     -- FK__UoMConversion__UnitOfMeasure__UoMTo
            Factor decimal(5),
            UoMCategoryID int       -- FK__UoMConversion__UnitOfMeasureCategory
        );
        ALTER TABLE UoMConversion
          ADD CONSTRAINT FK__UoMConversion__UnitOfMEasure__UoMFrom FOREIGN KEY(UoMFrom)
        REFERENCES UnitOfMeasure(UoMID)
        ON DELETE CASCADE;
        ALTER TABLE UoMConversion
           ADD CONSTRAINT FK__UoMConversion__UnitOfMEasure__UoMTo FOREIGN KEY(UoMTo)
            REFERENCES UnitOfMeasure(UoMID)
            ON DELETE CASCADE;
END TRY
 
BEGIN CATCH  
    SELECT   
        ERROR_NUMBER() AS ErrorNumber  
        ,ERROR_SEVERITY() AS ErrorSeverity  
        ,ERROR_STATE() AS ErrorState  
        ,ERROR_PROCEDURE() AS ErrorProcedure  
        ,ERROR_LINE() AS ErrorLine  
        ,ERROR_MESSAGE() AS ErrorMessage;  
  
 IF @@ERROR > 0  
    ROLLBACK TRANSACTION;  
 END CATCH;  
  
 SELECT @@TRANCOUNT AS OpenTransactions


 COMMIT TRANSACTION;  
 

Также, когда я удаляю один из двух операторов внешнего ключа, ошибка исчезает. Важно отметить, что таблицы не существуют в базе данных. Поэтому при запуске этого скрипта в базе данных нет существующих таблиц, связей и т. Д. И т. Д.

Ошибка, которую я получаю, такова: Сообщение об ошибке

Это означает, что эта ошибка возникает при добавлении ограничения ПЕРВИЧНОГО КЛЮЧА в таблицу, в которой столбец, назначенный в качестве ПЕРВИЧНОГО КЛЮЧА, определен как ОБНУЛЯЕМЫЙ. Это не так, потому что:

Поля таблицы UoMFrom и UoMTo созданы со свойством not null, как вы увидите в приведенном выше сценарии.

Есть ли возможность создать несколько ограничений внешнего ключа в транзакции без возникновения этой ошибки?

Любая помощь или предложения будут очень признательны! Заранее спасибо.

Используемое программное обеспечение:

 Microsoft SQL Server Management Studio                      14.0.17289.0
Microsoft Analysis Services Client Tools                    14.0.1016.283
Microsoft Data Access Components (MDAC)                     10.0.19041.1
Microsoft MSXML                                             3.0 6.0 
Microsoft Internet Explorer                                 9.11.19041.0
Microsoft .NET Framework                                    4.0.30319.42000
Operating System                                            6.3.19043
 

Комментарии:

1. Вы говорите, что получили ошибку, однако вы не включили указанную ошибку в свой вопрос… Мы не можем сказать вам, почему ваш код не работает, если вы не скажете нам, почему он не работает.

2. Остановитесь и не идите дальше. Что вам сообщило это сообщение об ошибке? Вы видели «предыдущие ошибки»? Перестаньте предполагать и начните реальную отладку. И обратите внимание, что ваша версия SSMS здесь не имеет значения — это информация о версии, которую вы включили. Если вы возьмете свой скрипт в блоке TRY и запустите его непосредственно в окне запроса SSMS, вы увидите фактическую ошибку.

3. И передача ошибок в tsql «сложна» — прочитайте обсуждение Эрланда здесь для более подробной информации.

4. Кроме того, почему SELECT ошибка, а не THROW это?

5. @larnu Я новичок в SQL Server. Так что я все еще учусь. Сообщение об ошибке или, по крайней мере, последняя обнаруженная ошибка была опубликована с изображением в ссылке. Я попробовал ваше второе предложение, которое привело меня к фактической ошибке. Msg 1785, Уровень 16, Состояние 0, Строка 234 Введение ограничения ВНЕШНЕГО КЛЮЧА «FK__UoMConversion__UnitOfMeasure__UoMTo «в таблице» UoMConversion » может привести к циклам или нескольким каскадным путям. Укажите ПРИ УДАЛЕНИИ БЕЗ ДЕЙСТВИЯ или ПРИ ОБНОВЛЕНИИ БЕЗ ДЕЙСТВИЯ или измените другие ограничения ВНЕШНЕГО КЛЮЧА. Я перестроил КАСКАДЫ, и это сработало! Спасибо!

Ответ №1:

Здесь нет необходимости использовать TRY/CATCH. Просто установите XACT_ABORT включенным и запустите пакет в транзакции.

 USE ProductDB

SET XACT_ABORT ON 
BEGIN TRANSACTION 
    
CREATE TABLE UnitOfMeasure(
    UoMID int not null identity(1,1) primary key,
    UoMDescription varchar(255) not null,
    UoMAbbreviation varchar(10) not null,
    UoMCategoryID int   -- FK__UnitOfMeasure__UnitOfMeasureCategory
);
        
        
CREATE TABLE UnitOfMeasureCategory(
    UoMCategoryID int not null identity(1,1) primary key,
    UoMCategory varchar(100) not null
);
        
CREATE TABLE UoMConversion (
    UoMConversionID int not null identity(1,1) primary key,
    UoMFrom int not null,   -- FK__UoMConversion__UnitOfMEasure__UoMFrom
    UoMTo int not null,     -- FK__UoMConversion__UnitOfMeasure__UoMTo
    Factor decimal(5),
    UoMCategoryID int       -- FK__UoMConversion__UnitOfMeasureCategory
);
ALTER TABLE UoMConversion
    ADD CONSTRAINT FK__UoMConversion__UnitOfMEasure__UoMFrom FOREIGN KEY(UoMFrom)
REFERENCES UnitOfMeasure(UoMID) ON DELETE CASCADE;

ALTER TABLE UoMConversion
    ADD CONSTRAINT FK__UoMConversion__UnitOfMEasure__UoMTo FOREIGN KEY(UoMTo)
    REFERENCES UnitOfMeasure(UoMID) ON DELETE CASCADE;

COMMIT TRANSACTION;  
 

Вы увидите все возвращенные сообщения об ошибках:

 Msg 1785, Level 16, State 0, Line 30
Introducing FOREIGN KEY constraint 'FK__UoMConversion__UnitOfMEasure__UoMTo' on table 'UoMConversion' 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 30
Could not create constraint or index. See previous errors.
 

Которые вы решаете, удаляя ON DELETE CASCADE по крайней мере из одного из ваших внешних ключей.