УНИКАЛЬНОЕ ОГРАНИЧЕНИЕ для столбца из внешней таблицы в SQL Server 2008

У меня есть две таблицы:


create table [dbo].[Main]
(
    [ID] [int] identity(1,1) primary key not null,
    [No] [int] not null,
    [Sign] [char](1) not null
)

create table [dbo].[Names]
(
    [ID_Main][int] primary key not null,
    [Name][nvarchar](128) not null,
    constraint [FK_Main_Users] foreign key ([ID_Main]) references [dbo].[Main]([ID]),
    constraint [CK_Name] unique ([Name], [Sign])
)

Проблема со вторым ограничением CK_Name

Есть ли способ сделать целевой столбец ограничения из внешней таблицы?



РЕДАКТИРОВАТЬ:



Объяснение. Я использую эти таблицы в приложении Silverlight с помощью EntityFramework. Сущности создаются таблицей для каждого типа наследования, поэтому код выглядит примерно так:


public abstract class Main
{
    // main properties
}

public class Names : Main
{
    // names properties
}

Это заставляет меня не использовать представления sql.

Пример данных.

---------------------------------------------
|  Main                |  Names             |
---------------------------------------------
|  ID  |  Sign  |  No  |  ID_Main  |  Name  |
---------------------------------------------
|   1  |     A  |   1  |    1      |  'qwe' |
|   2  |     B  |   1  |    2      |  'qwe' |
|   3  |     B  |   1  |    3      |  'qwe' |
|   4  |     C  |   1  |    4      |  'qwe' |
|   5  |     A  |   2  |    5      |  'asd' |
|   6  |     B  |   2  |    6      |  'asd' |
|   7  |     B  |   2  |    7      |  'asd' |
|   8  |     C  |   2  |    8      |  'asd' |

Как видите, есть несколько строк с одинаковым именем, но с разным знаком. Не может быть неуникального Имени с одним и тем же Знаком.

Я хотел бы обеспечить, чтобы было только одно имя со знаком = A и только одно имя со знаком C, но много имен со знаком = B


person bodziec    schedule 12.06.2010    source источник
comment
Почему не [Name, ID_Main] и дополнительные уникальные по [Sign] в [Main] таблице?   -  person LukLed    schedule 13.06.2010
comment
Учитывая дополнительную информацию, которую вы предоставили, я хотел бы узнать больше о том, почему вы хотите это ограничение. Какие объекты представляют Main и Names? Учитывая образец вывода, таблица Names выглядит как средняя таблица, за исключением того, что ID_Main является PK, поэтому на самом деле она применяется как отношение 1: 1. По каким правилам определяется, что один знак может иметь только одно имя, а другой знак может иметь несколько имен?   -  person Thomas    schedule 13.06.2010
comment
Почти каждая таблица в моей системе имеет несколько общих столбцов, поэтому я поместил их в одну основную таблицу, а другие таблицы ссылаются на них. Да, это отношение 1:1, но какая-то другая логика делает его отношением 1:*, но не напрямую   -  person bodziec    schedule 13.06.2010
comment
Это SQL, а не ООП, концепции которого спорны в среде, основанной на SET.   -  person OMG Ponies    schedule 14.06.2010
comment
Как сказал OMG Ponies, реляционная модель — это не то же самое, что ООП. Вы не можете создать одну базовую таблицу, чтобы управлять всеми остальными таблицами. Забудьте на время об атрибутах (столбцах) и начните с основных сущностей, которые будут существовать в вашей системе. Как правило, каждая сущность получает свою собственную таблицу.   -  person Thomas    schedule 14.06.2010


Ответы (2)


Да, вы можете применить такое ограничение, используя индексированное представление и создав уникальное ограничение (индекс) для (Знак, Имя) для отфильтрованного набора результатов.

CREATE VIEW dbo.vwSelectiveUniqueSignName WITH SCHEMABINDING
AS
SELECT  [Sign], Name
FROM    
    dbo.Main INNER JOIN dbo.Names on ID = ID_Main
WHERE
    dbo.Main.Sign IN ('A', 'C')
GO

CREATE UNIQUE CLUSTERED INDEX IDX_vwSelectiveUniqueSignName_Unique_Sign_Name
    ON dbo.vwSelectiveUniqueSignName ( [Sign] ASC, Name ASC )
GO

Контрольная работа:

-- using your sample data:
/* Case 1 Sign = 'A', Name = 'qwe': this will throw error 'cannot insert duplicate key row in object ... with unique index ... */
BEGIN TRAN NotAllowedMoreThan_1
insert dbo.Main ([Sign]) OUTPUT inserted.* values ('A') /* same for 'C' */
insert dbo.Names (ID_Main, Name) OUTPUT inserted.* SELECT SCOPE_IDENTITY(),'qwe'
COMMIT TRAN NotAllowedMoreThan_1
GO

/* Case 2 Sign = 'B', Name = 'qwe': this will pass > 1 times (note GO loop) */
BEGIN TRAN AllowedMoreThan_1
insert dbo.Main ([Sign]) OUTPUT inserted.* values ('B') /* any other than A, C */
insert dbo.Names (ID_Main, Name) OUTPUT inserted.* SELECT SCOPE_IDENTITY(),'qwe'
COMMIT TRAN AllowedMoreThan_1
GO 2
person Hrvoje Piasevoli    schedule 13.06.2010
comment
Я отредактировал свой пост с дополнительным объяснением. Ограничения на представления работают, даже если я не использую представления? - person bodziec; 13.06.2010
comment
Да, ограничение предотвратит вставку неуникальных записей в таблицы. - person Hrvoje Piasevoli; 13.06.2010
comment
Хорошо, ваш ответ и этот пост dbwhisperer.blogspot.com /2008/11/ помог мне найти решение моей проблемы. Если вы будете так любезны и отредактируете свой пост, изменив определение представлений, чтобы он отображал только имена из строк с буквой «А» и добавил к нему уникальный индекс, я отмечу это как ответ - person bodziec; 13.06.2010
comment
Я отредактировал свой ответ с учетом ваших правок и объяснений и включил код для запуска теста на ваших примерных данных. - person Hrvoje Piasevoli; 14.06.2010

Чтобы ответить на ваш вопрос, нет возможности ссылаться на столбец в сторонней таблице в проверочном ограничении (или любом другом виде ограничения в этом отношении). Однако в вашей ситуации вы хотите убедиться, что любая комбинация Names.Name и Main.Sign будет уникальной. Для этого вы можете просто добавить уникальное ограничение для каждого из двух столбцов:

Alter Table dbo.Main Add Constraint UC_Main Unique Nonclustered ( Sign )
GO
Alter Table dbo.Names Add Constraint UC_Names Unique Nonclustered ( Name )

Нет необходимости создавать уникальное ограничение для ID_Main и Name вместе, так как ID_Main уже требуется, чтобы быть уникальным через его ограничение первичного ключа.

person Thomas    schedule 13.06.2010