SQL Server: как ограничить таблицу, чтобы она содержала одну строку?

81

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

Каков самый простой способ применить ограничение одной строки?

Мартин
источник
Почему бы не использовать таблицу со столбцами (Name, Value)с первичным ключом по имени. Тогда вы можете select Value from Table where Name = ?с уверенностью сказать, что ни одна строка не будет возвращена.
a'r
2
Я не уверен, что sql - лучшее решение здесь. Может быть, для настройки больше подходит простой XML-файл. Я привык думать, что конфигурация! = Data и sql была создана для данных.
remi bourgarel
2
@ar - я видел, что это очень плохо, когда вы ожидаете прочитать, скажем, целое число, и вы получаете некорректно отформатированное значение в столбце значений.
Damien_The_Unbeliever
@Damien_The_Unbeliever Почему это произошло? Потому что вы указали несуществующее значение для Name?
Noumenon
1
@Noumenon - обратите внимание, что мой комментарий был ответом на ваш arкомментарий. Проблема в том, что если вы просто храните пары имя / значение, значение вполне должно быть строкой, и у вас нет средств принудительной проверки в базе данных. Когда вы используете однострочную таблицу с отдельными столбцами для каждого параметра (как того требовал OP), вы можете легко обеспечить проверку для каждого параметра конфигурации с помощью проверочных ограничений.
Damien_The_Unbeliever

Ответы:

97

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

CREATE TABLE T1(
    Lock char(1) not null,
    /* Other columns */,
    constraint PK_T1 PRIMARY KEY (Lock),
    constraint CK_T1_Locked CHECK (Lock='X')
)

У меня есть несколько таких таблиц в разных базах данных, в основном для хранения config. Намного приятнее знать, что если элемент конфигурации должен быть int, вы когда-либо будете читать только int из БД.

Damien_The_Unbeliever
источник
2
+1. Это подход, который Celko использует для вспомогательной таблицы констант в "SQL for Smarties"
Мартин Смит
Это очень просто. Благодарю.
Мартин
+1: Интересное решение. Я не видел этого раньше, поэтому спасибо, что поделились. Всегда путь, легко, когда знаешь как ....
Джон Сансом
Вот еще один вопрос, над которым стоит подумать. Какой первичный ключ этой таблицы? :)
nvogel
2
@BZ - cdt - это сокращение comp.databases.theoryот группы usenet (видимой через группы Google), которую я, признаюсь, в последнее время мало читал. Он был больше ориентирован на теорию отношений, чем на SQL, но я знал, что dportas / sqlvogel также часто посещает ту же группу. TTM был ссылкой на «Третий манифест» , хорошую книгу, в которой (опять же) говорится о теории отношений, а не о SQL.
Damien_The_Unbeliever
53

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

CREATE TABLE T1(
    Lock char(1) not null DEFAULT 'X',
    /* Other columns */,
    constraint PK_T1 PRIMARY KEY (Lock),
    constraint CK_T1_Locked CHECK (Lock='X')
)

Добавив «DEFAULT 'X'», вам никогда не придется иметь дело со столбцом Lock, и вам не придется запоминать значение блокировки при загрузке таблицы в первый раз.

ACB
источник
4
Ограничение по умолчанию также должно быть названо, иначе оно получит автоматически сгенерированное сбивающее с толку имя. Lock char(1) not null CONSTRAINT DF_T1_Lock DEFAULT 'X'
Дэвид С.
1
Кроме того, вы должны установить значение по умолчанию, отличное от «X». Я сделал свою строку более длинной, и теперь это мое сообщение об ошибке, если я пытаюсь вставить вторую строку: Нарушение ограничения PRIMARY KEY 'PK_RestrictToOneRow'. Невозможно вставить повторяющийся ключ в объект 'dbo.1ROWTABLE'. Повторяющееся значение ключа (эта таблица привязана к одной строке).
Джефф Грисвальд
14

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

Для этого у вас действительно есть дополнительный столбец creation_date_time(дата / время вставки или обновления) и триггер вставки или вставки / обновления, который правильно заполнит его текущей датой / временем.

Затем, чтобы получить текущую конфигурацию, вы используете что-то вроде:

select * from config_table order by creation_date_time desc fetch first row only

(в зависимости от вашего вкуса СУБД).

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

Paxdiablo
источник
+1: Я слышу, что вы говорите, но предпочитаю записывать историю изменений в отдельные таблицы аудита.
Мартин
+1: За то, что поделился интересной идеей внедрения контрольного журнала.
Джон Сэнсом
Ницца. JustSELECT TOP 1 ... ORDER BY creation_date_time DESC
Баодад
5

Вы можете реализовать триггер INSTEAD OF для принудительного применения бизнес-логики этого типа в базе данных.

Триггер может содержать логику для проверки, существует ли уже запись в таблице, и если да, ОТКАТИТЬ вставку.

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

Джон Сэнсом
источник
+1 - Я вижу, как триггер будет работать, и, возможно, я вернусь к этому подходу, но мне нравится простое ограничение Дэмиена. Предстоит интересное обсуждение того, какие данные конфигурации принадлежат конфигурационному файлу, а какие - БД. В этом случае я думаю, что БД - правильное место. Без сомнения, я буду жить, чтобы пожалеть об этом ... :-)
Мартин
2

Я использую битовое поле для первичного ключа с именем IsActive. Таким образом, может быть не более 2 строк, а sql для получения действительной строки: выберите * из параметров, где IsActive = 1, если таблица называется Параметры.

пользователь3382925
источник
2

Вот решение, которое я придумал для таблицы типа блокировки, которая может содержать только одну строку, содержащую Y или N (например, состояние блокировки приложения).

Создайте таблицу с одним столбцом. Я устанавливаю ограничение проверки для одного столбца, чтобы в него можно было поместить только Y или N. (Или 1, или 0, или что угодно)

Вставить одну строку в таблицу с "нормальным" состоянием (например, N означает не заблокировано)

Затем создайте триггер INSERT для таблицы, которая имеет только SIGNAL (DB2), RAISERROR (SQL Server) или RAISE_APPLICATION_ERROR (Oracle). Благодаря этому код приложения может обновлять таблицу, но любой INSERT не работает.

Пример DB2:

create table PRICE_LIST_LOCK
(
    LOCKED_YN       char(1)   not null  
        constraint PRICE_LIST_LOCK_YN_CK  check (LOCKED_YN in ('Y', 'N') )
);
--- do this insert when creating the table
insert into PRICE_LIST_LOCK
values ('N');

--- once there is one row in the table, create this trigger
CREATE TRIGGER ONLY_ONE_ROW_IN_PRICE_LIST_LOCK
   NO CASCADE 
   BEFORE INSERT ON PRICE_LIST_LOCK
   FOR EACH ROW
   SIGNAL SQLSTATE '81000'  -- arbitrary user-defined value
     SET MESSAGE_TEXT='Only one row is allowed in this table';

Работает на меня.

Дэвид Бимер
источник
2

Старый вопрос, но как насчет использования IDENTITY (MAX, 1) небольшого типа столбца?

CREATE TABLE [dbo].[Config](
[ID] [tinyint] IDENTITY(255,1) NOT NULL,
[Config1] [nvarchar](max) NOT NULL,
[Config2] [nvarchar](max) NOT NULL
NeutronCode
источник
Вы можете добавить еще одну строку, используя SET IDENTITY_INSERT.
Разван Сокол
1

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

Каран
источник
-1
IF NOT EXISTS ( select * from table )
BEGIN
    ///Your insert statement
END
Сачин Шанбхаг
источник
-1

Здесь мы также можем сделать невидимое значение, которое будет таким же после первой записи в базу данных. Пример: Таблица учеников: Id: int firstname: char Здесь, в поле ввода, мы должны указать то же значение для столбца id, которое будет ограничивать как после первой записи, кроме записи lock bla bla из-за ограничения первичного ключа, таким образом, всегда остается только одна строка. Надеюсь это поможет!

Ишагерев
источник