2010-05-19 1 views
2

Большая часть моего кода sql генерируется (из POD). Теперь у меня есть проблема, когда таблица user_data имеет FK для ref_status, которая указывает на две разные user_data. Мой код выполняет следующиекруговой иностранный ключ. Как я их обрабатываю?

  1. начинает транзакцию
  2. смотрит на user_data (и добавляет его в список)
  3. видит ref_status затем повторяет # 2 с ним
  4. выполняет создание кода ref_status таблицы

Тогда я получаю исключение

Foreign key 'FK__...__0F975522' references invalid table 'user_data'. 

Как создать две таблицы, если они используют оба источника в качестве ссылки? Я думал, так как это было в той же транзакции, это сработало бы. Я также отмечу, что этот код отлично работает в sqlite с поддержкой поддержки FK (поддерживается с выпуском System.Data.SQLite в прошлом месяце). Итак, как я могу создать эти две таблицы?

+4

Вы уверены, что данные моделируются правильно? – munissor

ответ

6

Циркулярные внешние ключи на SQL Server не поддерживаются. Это можно сделать, если вы действительно этого хотите, но это не очень полезно, поскольку у вас нет возможности вставлять какие-либо данные - вы не можете вставлять в таблицу A, потому что требуемые ссылки в таблице B не существуют и наоборот. Единственный способ - создать одну из таблиц без FK, а затем добавить ее после создания второй таблицы. Затем, чтобы вставить данные, вам нужно отключить один из FK, а затем снова включить его, но это очень ресурсоемкая операция, если у вас много данных, так как все это нужно будет перепроверять, когда FK снова включен.

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

ВНИМАНИЕ: следующий код smaple демонстрирует, как создавать круговые FK, но это действительно очень плохо для вашего здоровья! Я уверен, что в долгосрочной перспективе вы не захотите этого делать. Например, просто попытка сбросить любую из этих таблиц после этого очень сложно, вы не можете просто DROP TABLE!

CREATE TABLE [A] 
(
    [AId] int 
     NOT NULL 
     PRIMARY KEY, 
    [BId] int 
     NULL 
     -- You can't create the reference to B here since it doesn't yet exist! 
) 

CREATE TABLE [B] 
(
    [BId] int 
     NOT NULL 
     PRIMARY KEY, 
    [AId] int 
     NOT NULL 
     FOREIGN KEY 
      REFERENCES [A] 
) 

-- Now that B is created, add the FK to A 
ALTER TABLE [A] 
    ADD 
     FOREIGN KEY ([BId]) 
     REFERENCES [B] 

ALTER TABLE [A] 
    ALTER COLUMN [BId] 
     int 
     NOT NULL 
+0

В этом случае user_data имеет nullable ref для ref_status. Я всегда могу вставить обновление user_data.Но мой вопрос в том, как я даже начинаю с тех пор, как мне нужно создать две таблицы со ссылками на eachother. – 2010-05-19 10:04:21

+0

Я добавил пример, показывающий, как это сделать, но на самом деле вы не хотите этого делать! –

+0

То есть ... безумный. Также спасибо за вашу помощь сегодня и вчера. – 2010-05-19 10:49:12

1

В сценарии с несколькими арендаторами, предположим, у вас есть 2 таблицы: подписчиков & Контакт.

Вы хотите знать, кто является Первичным контактом для подписчика. Однако в таблице Contact также должна быть ссылка FKEY на SubscriberId для секционирования/для использования для ключа объединения и т. Д. (SQL Azure).

======================================================================= 
Subscriber.sql 
======================================================================= 
-- One who has subscribed to Rhipheus 
CREATE TABLE [rhipheus].[Subscriber] 
(
    [Id] UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() CONSTRAINT [PKEY_Subscriber_Id] PRIMARY KEY CLUSTERED, 
    [ShortName] NVARCHAR(50) NOT NULL, 
    [LegalName] NVARCHAR(255) NOT NULL, 
    [SmallLogoPath] NVARCHAR(MAX) NOT NULL, 
    [LargeLogoPath] NVARCHAR(MAX) NOT NULL, 
    [PrimaryContactId] UNIQUEIDENTIFIER NULL REFERENCES [rhipheus].[Contact]([Id]), 
) 

==================================================================== 
Contact.sql 
==================================================================== 
CREATE TABLE [rhipheus].[Contact] 
(
    [Id] UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() CONSTRAINT [PKEY_Contact_Id] PRIMARY KEY CLUSTERED, 
    [SubscriberId] UNIQUEIDENTIFIER NOT NULL CONSTRAINT [FKEY_Contact_SubscriberId_Subscriber_Id] REFERENCES [rhipheus].[Subscriber]([Id]), 
    [FirstName] NVARCHAR(50) NOT NULL, 
    [LastName] NVARCHAR(50) NOT NULL, 
) 

Это работало 2010 проект базы данных, поскольку она используется для стирают все ограничения на уровне столбцов и создавать их с помощью отдельных сценариев ALTER.

Способ, которым я решил это в VS.Net 2012, - объявить столбец внешнего ключа как NULLable и добавить внешние ключи для подписчика, используя отдельный оператор ALTER. Конечно, проект SQL Server в VS 2012 не позволил бы мне выполнить декларацию на уровне столбца, поскольку он не может определить, какую таблицу создать сначала (даже если HINT находится там, в форме объявления NULLable) ,

======================================================================= 
Subscriber.sql 
======================================================================= 
-- One who has subscribed to Rhipheus 
CREATE TABLE [rhipheus].[Subscriber] 
(
    [Id] UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() CONSTRAINT [PKEY_Subscriber_Id] PRIMARY KEY CLUSTERED, 
    [ShortName] NVARCHAR(50) NOT NULL, 
    [LegalName] NVARCHAR(255) NOT NULL, 
    [SmallLogoPath] NVARCHAR(MAX) NOT NULL, 
    [LargeLogoPath] NVARCHAR(MAX) NOT NULL, 
    [PrimaryContactId] UNIQUEIDENTIFIER NULL 
) 

==================================================================== 
Contact.sql 
==================================================================== 
CREATE TABLE [rhipheus].[Contact] 
(
    [Id] UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() CONSTRAINT [PKEY_Contact_Id] PRIMARY KEY CLUSTERED, 
    [SubscriberId] UNIQUEIDENTIFIER NOT NULL CONSTRAINT [FKEY_Contact_SubscriberId_Subscriber_Id] REFERENCES [rhipheus].[Subscriber]([Id]), 
    [FirstName] NVARCHAR(50) NOT NULL, 
    [LastName] NVARCHAR(50) NOT NULL 
) 

==================================================================== 
Subscriber.ForeignKeys.sql 
==================================================================== 
ALTER TABLE [rhipheus].[Subscriber] ADD CONSTRAINT [FKEY_Subscriber_PrimaryContactId_Contact_Id] FOREIGN KEY([PrimaryContactId]) REFERENCES [rhipheus].[Contact]([Id]) 
GO