2015-03-19 4 views
1

У меня есть сценарий вроде этого:MSSQL Constraint может вызвать циклы или несколько путей каскадных

User может владеть мультипликатор Accounts. User также имеет адреса биткойнов (которые он сам вводит), и они являются «адресами вывода». Каждый Account также может иметь несколько адресов биткойнов (которые являются «адресами депозитов»).

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

Я хотел бы создать сценарий, при котором удаление User вызовет весь вывод BitcoinAddresses он владеет, чтобы быть удален, а также все Accounts он владеет будут удалены. Но удаление Account должно привести к тому, что ссылка BitcoinAddresses будет установлена ​​на NULL.

Я пытался что-то вроде этого:

CREATE TABLE [dbo].[Users] (
    [Id]     NVARCHAR (128) NOT NULL, 
    [UserName]   NVARCHAR (64) NULL, 
    CONSTRAINT [PK_dbo.Users] PRIMARY KEY CLUSTERED ([Id] ASC), 
); 

CREATE TABLE [dbo].[Accounts] (
    [Id]  BIGINT   IDENTITY (1, 1) NOT NULL, 
    [UserId] NVARCHAR (128) NOT NULL, 
    [Number] BIGINT   NOT NULL, 
    CONSTRAINT [PK_dbo.Accounts] PRIMARY KEY CLUSTERED ([Id] ASC), 
    CONSTRAINT [FK_dbo.Accounts.Users_UserId] FOREIGN KEY ([UserId]) REFERENCES [dbo].[Users] ([Id]) ON DELETE CASCADE 
); 

CREATE TABLE [dbo].[BitcoinAddresses] (
    [BitcoinAddressId] INT   IDENTITY (1, 1) NOT NULL, 
    [Address]   NVARCHAR (MAX) NOT NULL, 
    [AccountId]  BIGINT   NULL, 
    [UserId]   NVARCHAR (128) NULL, 
    [Type]   NVARCHAR (MAX) NULL, 
    CONSTRAINT [PK_dbo.BitcoinAddresses] PRIMARY KEY CLUSTERED ([BitcoinAddressId] ASC), 
    CONSTRAINT [FK_dbo.BitcoinAddresses_dbo.Accounts_AccountId] FOREIGN KEY ([AccountId]) REFERENCES [dbo].[Accounts] ([Id]) ON DELETE SET NULL, 
    CONSTRAINT [FK_dbo.BitcoinAddresses_dbo.Users_UserId] FOREIGN KEY ([UserId]) REFERENCES [dbo].[Users] ([Id]) ON DELETE CASCADE 
); 

Это решение вызывает ошибку:

Introducing FOREIGN KEY constraint 'FK_dbo.BitcoinAddresses_dbo.Users_UserId' on table 'BitcoinAddresses' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints. 

Но, очевидно, я не вижу никаких циклов в этом подходе. Можете ли вы объяснить мне, почему такое поведение происходит? Как я могу это решить? Я хотел бы отметить, что я не хочу разбивать адреса Wihtdrawal и Deposit на две разные таблицы (так как это было бы правильным решением для этого сценария, но я хочу знать, почему я не могу создавать такие ссылки)

Вот моя скрипка, чтобы играть с: http://sqlfiddle.com/#!6/5d9cd

+0

У вашего дизайна есть еще одна проблема. Строка в «биткойнАдрессах» может ссылаться на «Пользователь» и «Учетная запись» с разными 'UserId'. Это намеренно? Если нет, вы можете удалить 2 FK и использовать только один, с: 'FOREIGN KEY (UserId, AccountId) ССЫЛКИ [dbo]. [Учетные записи] (UserId, Id)', вы сможете решить обе свои проблемы;) О, и вам нужно будет добавить ограничения 'UNIQUE' в' Accounts' для определения указанного FK. –

+0

Разделение таблицы на два кажется лучшим решением для этого. Для пользователей нужно было бы только FK (и никакой столбец AccountID), а другой - только для учетных записей. Нет нескольких каскадных путей и столбцов с нулевым значением. –

+0

Спасибо за ваши предложения. Я знаю, что могу разбить его на две таблицы, хотя в моем случае это не так просто, потому что весь этот сценарий управляется Entity Framework. Таким образом, структура гарантирует, что у меня нет ситуации, когда у меня есть разные «учетные записи» и «пользователь» на одном «биткойн-аддресе». Но спасибо, что заметили, хороший улов! –

ответ

3

при удалении пользователя, его учетные записи будут удалены, и получить его bitcoinaddresses будет удаляется. Но когда его account будет удален, bitcoinaddresses учетной записи также будет обновлен до NULL. Это слишком сложно для SQL Server. Каскадные цепи должны быть простыми и аккуратными, а не ветвями и/или сходиться. Как SQL-Server может знать, что вы хотите, если удаление пользователя приведет к удалению биткойна и настройке его учетной записи на NULL?

+1

Вы правы, но даже если бы я установил его для CASCADE delete на каждой ССЫЛКЕ, то это дало бы мне ту же ошибку. –

+0

Да и по той же причине. Два каскадных пути сходятся. Это основная причина. SQL не запрограммирован, чтобы слишком глубоко думать о том, действительно ли сходящиеся каскады вызовут проблему. Если он видит потенциальную конвергенцию, возникает ошибка. –

+0

@TabAlleman Пожалуйста, отредактируйте свой naswer, от «SQL» до «SQL-Server». Существуют другие продукты SQL (например, Postgres), которые отлично работают с несколькими каскадными путями.Архитекторы SQL-Server решили запретить это (по каким-то веским причинам нет никаких аргументов). –

Смежные вопросы