Круглый внешний ключ. Как мне с ними справиться? - PullRequest
2 голосов
/ 19 мая 2010

Большая часть моего кода 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 в прошлом месяце) Итак, как я могу создать эти две таблицы?

Ответы [ 2 ]

6 голосов
/ 19 мая 2010

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

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

ВНИМАНИЕ: следующий фрагмент кода демонстрирует, как создавать круглые 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
1 голос
/ 15 февраля 2013

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

Вы хотите сразу узнать, кто является основным контактом для подписчика. Однако таблица контактов также должна иметь ссылку 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...