Как ограничить таблицу базы данных, чтобы только одна строка могла иметь определенное значение в столбце?

Используя Oracle, если значение столбца может быть «ДА» или «НЕТ», возможно ли ограничить таблицу, чтобы только одна строка могла иметь значение «ДА»?

Я бы предпочел изменить структуру таблицы, но это невозможно.

[UDPATE] К сожалению, нулевые значения в этой таблице недопустимы.


person Trigger    schedule 08.10.2008    source источник
comment
О боже, нулевые значения не допускаются - это немного меняет дело - теперь вам нужно использовать индекс на основе функций (@Tony Andrews). По-прежнему избегайте триггеров и автономных транзакций.   -  person Nick Pierpoint    schedule 10.10.2008


Ответы (7)


Используйте функциональный индекс:

create unique index only_one_yes on mytable
(case when col='YES' then 'YES' end);

Oracle индексирует только те ключи, которые не являются полностью нулевыми, и выражение CASE здесь гарантирует, что все значения 'NO' будут изменены на нулевые и поэтому не будут индексироваться.

person Tony Andrews    schedule 08.10.2008

Это хитрый хак, но если столбец допускает NULL, вы можете использовать NULL вместо «NO» и использовать «YES», как и раньше. Примените уникальное ключевое ограничение к этому столбцу, и вы никогда не получите два значения «ДА», но все равно получите много «НЕТ».

Обновление: @Nick Pierpoint: предложил добавить проверочное ограничение, чтобы значения столбцов были ограничены только «YES» и NULL. Синтаксис полностью разработан в его ответе.

person yukondude    schedule 08.10.2008
comment
Ничего страшного в этом нет - это путь. +1 - person Nick Pierpoint; 08.10.2008
comment
Вам также необходимо добавить контрольное ограничение для таблицы, чтобы она не разрешала ничего, кроме YES или null. - person Nick Pierpoint; 08.10.2008
comment
если вы хотите, чтобы это выглядело красиво, вы, вероятно, могли бы также обернуть представление с помощью NVL, тогда вы получите свой Y/N - person Matthew Watson; 08.10.2008
comment
Что ж, использование NULL неуместно, если вы хотите присоединиться к нему. Это может быть кладжом, который стоит сделать, но это все же компромисс дизайна в отличие от более правильного подхода. - person David Aldridge; 08.10.2008

Вы захотите проверить статью Тома Кайта, в которой задан именно этот вопрос, и его ответ:

http://tkyte.blogspot.com/2008/05/another-of-day.html

Резюме: не используйте триггеры, не используйте автономные транзакции, используйте две таблицы.

Если вы используете базу данных Oracle, вам НЕОБХОДИМО познакомиться с AskTom и получить его книги.

person Guy    schedule 08.10.2008
comment
Заданный там вопрос немного отличается от этого, в вопросе Тома в таблице может быть несколько Y, но только 1 на страну. Насколько я читал, в этом примере таблица может иметь только 1 Да, и в этом случае решение с индексом работает. Но да, AskTom является обязательным для базы данных Oracle. - person Matthew Watson; 09.10.2008
comment
Я согласен. Если бы Тому задали этот вопрос, я бы предположил, что он определенно выбрал бы индексное решение. - person Nick Pierpoint; 09.10.2008

Это не работает с определением таблицы.

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

  1. Установите для всех строк значение «НЕТ».
  2. Установите для строки, которую вы хотите, значение YES
person poezn    schedule 08.10.2008
comment
-1 для решения на основе триггера. Они никогда не работают хорошо для обеспечения соблюдения ограничений на уровне таблицы. - person David Aldridge; 08.10.2008

Следуя моему комментарию к предыдущему ответу yukondude, я бы добавил уникальный индекс и контрольное ограничение:

create table mytest (
    yesorno varchar2(3 char)
);

create unique index uk_mytest_yesorno on mytest(yesorno);

alter table mytest add constraint ck_mytest_yesorno check (yesorno is null or yesorno = 'YES');
person Nick Pierpoint    schedule 08.10.2008
comment
Я думаю, что до тех пор, пока значение NULL подходит в качестве значения not yes, этот метод имеет преимущество перед методом FBI, поскольку оптимизатор может, по крайней мере, использовать ограничения. Я думаю, что запросы с предикатами yesorno='YES' и yesorno is null получат лучшие оценки кардинальности. - person David Aldridge; 08.10.2008
comment
Поскольку всегда будет только 1 строка с ДА и каждая другая строка с нулем, вы получите индекс, используемый для поиска строки ДА, и полное сканирование (совершенно правильно), чтобы получить все остальное (НЕТ). - person Nick Pierpoint; 08.10.2008
comment
Конечно, вопрос лишь в том, является ли замена No на Null достойным компромиссом для достижения этой цели. - person David Aldridge; 08.10.2008

Поддерживает ли Oracle что-то вроде отфильтрованных индексов (на прошлой неделе я слышал, что, например, MSSQL2008 поддерживает)? Возможно, вы можете определить уникальный ключ, который будет применяться только к строкам со значением "Да" в вашем столбце.

person Matthias Meid    schedule 08.10.2008
comment
Нет отфильтрованных индексов, но FBI предлагают более гибкий (хотя, возможно, менее краткий) способ сделать то же самое. - person orbfish; 25.10.2011

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

person Rimas Kudelis    schedule 08.10.2008
comment
трудно поддерживать в постоянной среде - person David Aldridge; 08.10.2008