SQL Server 2005 FOREIGN KEY, который ссылается на «постоянное» значение для второго столбца - PullRequest
0 голосов
/ 03 ноября 2011

У меня есть таблица с внешним ключом для другой таблицы.
Например: Postion.day REFERENCES weekdays.day что нормально. Однако Position.day может содержать рабочие дни, где rdo=true.

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

Я подозреваю, что ответ будет выглядеть примерно так:

ALTER TABLE Postition ADD COLUMN day CHAR(3)
            FOREIGN KEY REFERENCES weekday(shortName) 
            CHECK (weekday.rdo=TRUE);

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

UPDATE
Итак, у меня есть таблица, Name Table (я не назвал ее), у меня есть еще одна таблица дней недели, в которой перечислены все 7 дней недели вместе с другой информацией. Таблица имен имеет 2 внешних ключа rdo и shortDay. Будние дни содержат битовое поле для rdo и битовое поле для короткого дня, в котором указано, подходит ли день для этих дней. Поэтому я хочу, чтобы мое поле RDO было внешним ключом для рабочих дней, но ТОЛЬКО ГДЕ RDO = TRUE

Первичный ключ в будние дни - это короткое имя, 3 буквы (символ (3)), обозначающие день недели, например: понедельник, вторник, среда, четверг и т. Д.

Я думал об этом и вспомнил транзакции SQL (в настоящее время я держу пари, что SQL Server будет достаточно умен, чтобы откатить успешный вызов ALTER TABLE, таблица позиций уже существует.)

BEGIN
ALTER TABLE [Name Table] ADD RDO CHAR(3);
ALTER TABLE [Name Table] ADD FOREIGN KEY (RDO) REFERENCES weekdays(shortName);
ALTER TABLE [Name Table] ADD CHECK (TRUE=(SELECT rdo FROM weekdays 
                                                     WHERE shortName=RDO));
ROLLBACK;

Что возвращается из базы данных:

Код ошибки 102, состояние SQL S0001: неправильный синтаксис рядом с ')'.
Строка 1, столбец 1

Код ошибки 1769, состояние SQL S0001: внешний ключ «RDO» ссылается на недопустимый столбец «RDO» в ссылочной таблице «Таблица имен».
Строка 3, столбец 1

Код ошибки 1046, состояние SQL S0001: подзапросы в этом контексте запрещены. Допускаются только скалярные выражения.
Строка 4, столбец 1

Код ошибки 3903, состояние SQL S0001: у запроса ROLLBACK TRANSACTION нет соответствующей BEGIN TRANSACTION.
Строка 5, столбец 1

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

В идеале синтаксис должен выглядеть так (я знаю, что он недействителен):

ALTER TABLE [Name Table] ADD RDO CHAR(3)
            FOREIGN KEY REFERENCES weekdays(shortName,rdo=true);

это помогает?

Ответы [ 4 ]

1 голос
/ 03 ноября 2011

Ограничения CHECK обычно предназначены для работы с содержимым одной строки. Вот почему операторы SELECT не допускаются. Если вы пытаетесь определить, является ли значение, хранящееся в столбце «день», одним из дней недели, я бы сделал одно из следующих действий.

Если таблица «день недели» содержит только дни недели {«Пн», «Вт», «Ср», «Чт», «Пт»}, то все, что вам нужно, это внешний ключ.

Если в таблице "день недели" есть что-то, кроме дней недели {'Пн', 'Вт', 'Ср', 'Чт', 'Пт'}, я бы рассмотрел создание другой таблицы из пяти фактических дней недели. Используйте любые ограничения целостности, которые вы считаете необходимыми. Таблица «Позиция» может установить ссылку на внешний ключ для этой таблицы из пяти строк.

Я мог бы рассмотреть возможность записи всего ограничения в качестве ограничения CHECK.

ALTER TABLE Postition 
ADD COLUMN day CHAR(3)
CHECK (day IN ('Mon', 'Tue', 'Wed', 'Thu', 'Fri');

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

Позже. , .

Чтобы установить ограничение внешнего ключа только для тех строк, где RDO=TRUE, вам нужно идентифицировать эти дни как строки в таблице. (Внешние ключи предназначены для столбца, а не для столбца, отфильтрованного по значению в другом столбце.) Сейчас у вас есть два логических флага. Попробуйте вместо этого создать две таблицы.

create table rdo (
  day char(3) primary key references weekdays (shortname)
);
insert into rdo values ('Mon');
insert into rdo values ('Tue');
insert into rdo values ('Wed');
insert into rdo values ('Thu');
insert into rdo values ('Fri');

create table shortday (
  day char(3) primary key references weekdays (shortname)
);
insert into shortday values ('Fri');

Вы можете установить ссылки на внешние ключи для любой из этих таблиц.

Ответы получатся лучше, если вы предоставите DDL и примеры данных в виде операторов SQL INSERT.

0 голосов
/ 10 декабря 2013

Способ, которым я сейчас работаю для достижения этой цели:

  • Добавить дополнительный столбец bit к Table, который называется, например, IsRdo.
    • Добавьте check, что Table.IsRdo = true.Да, у нас есть столбец, который всегда 1.
    • Или, как я только что заметил, сделайте его константой: [IsRdo] AS cast(1 as bit) PERSISTED
  • Добавьте составного кандидатаключ на weekdays, состоящий из (shortName, rdo).
  • Сделать внешний ключ из Table до weekdays match (shortName, rdo).

Это гарантирует, что Tableстрока может соответствовать только строке weekdays, где rdo равно true.

Это кажется немного странным (опять же, у нас есть столбец, который всегда 1), но я не нашеллучший способ сделать это.

0 голосов
/ 03 ноября 2011

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

Я думал, что FK на отфильтрованном индексе (SQL 2008) или на материализованном представлении может решить проблему, но, очевидно, ни один из этих подходов не работает: (

0 голосов
/ 03 ноября 2011

Если вы контролируете весь процесс (код, который вызывает базу данных и саму базу данных), я настоятельно рекомендую использовать хранимые процедуры для выполнения вставки / обновления / удаления.

Затем вы выполняете свои проверки в хранимой процедуре перед выполнением действий и raiserror с соответствующими ошибками при обнаружении проблемы.

Основным преимуществом является

1) Ваши бизнес-правила заключены в капсулу и их легко понять, когда их пора менять через 6 месяцев или год

2) Вы можете предоставить своим пользователям гораздо более значимые сообщения об ошибках, чем те, которые возникают при проверке ограничения или другого ограничения. Пользователи часто очень смущаются, когда видят странное исключение.

Фактически, из-за пункта № 2 мы удалили все уникальные ограничения из наших таблиц и заменили их эквивалентными функциями хранимых процедур, которые могут точно сказать пользователю, что не так, и что ему нужно сделать, чтобы решить проблему. В результате значительно сократилось количество обращений в службу поддержки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...