SQL Server: як обмежити таблицю, щоб вона містила один рядок?


81

Я хочу зберегти один рядок у таблиці конфігурації для моєї програми. Я хотів би забезпечити, щоб ця таблиця містила лише один рядок.

Який найпростіший спосіб застосувати обмеження для одного рядка?


Чому б не використовувати таблицю зі стовпцями (Name, Value)з первинним ключем на Name. Тоді ви можете select Value from Table where Name = ?з упевненістю сказати, що не буде повернуто жодного рядка або один рядок.
a'r

2
Я не впевнений, що sql є найкращим рішенням тут. Можливо, простий файл xml більше підходить для конфігурації. Я вважаю, що конфігурація! = Data і sql була створена для даних.
remi bourgarel

2
@ar - Я бачив, що це робиться дуже неправильно, коли ви очікуєте прочитати, скажімо, ціле число, і ви отримаєте якесь погано відформатоване значення у стовпці значень.
Damien_The_Unbeliever

@Damien_The_Unbeliever Чому так сталося? Оскільки ви вказали неіснуюче значення для Name?
Ноуменон

1
@Noumenon - зауважте, що мій коментар був відповіддю на arкоментар s. Проблема полягає в тому, що якщо ви просто зберігаєте пари ім’я / значення, значення досить добре має бути строковим, і у вас немає засобів для забезпечення перевірки в базі даних. Коли ви використовуєте однорядкову таблицю з окремими стовпцями для кожного параметра (як вимагав 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')
)

У мене є ряд цих таблиць у різних базах даних, в основному для зберігання конфігурації. Набагато приємніше знати, що якщо елемент конфігурації повинен бути int, ви завжди прочитаєте int з БД.


2
+1. Це підхід, який Celko використовує для таблиці допоміжних констант в "SQL для розумників"
Мартін Сміт,

Це приблизно так просто. Дякую.
Мартін,

+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')
)

Додаючи "ЗАВДАННЯ 'X'", вам ніколи не доведеться мати справу зі стовпцем Блокування та не доведеться згадувати, яке значення блокування було при першому завантаженні таблиці.


4
Обмеження за замовчуванням також повинно бути названо, інакше воно отримає автоматично згенероване заплутане ім'я. Lock char(1) not null CONSTRAINT DF_T1_Lock DEFAULT 'X'
Девід С.

1
Крім того, ви повинні зробити значення за замовчуванням іншим, ніж "X". Я зробив свій довший рядок, і це моє повідомлення про помилку зараз, якщо я спробую вставити другий рядок: Порушення обмеження PRIMARY KEY 'PK_RestrictToOneRow'. Не вдається вставити дублікат ключа в об'єкт "dbo.1ROWTABLE". Дублікат значення ключа (Ця таблиця заблокована до одного рядка).
Geoff Griswald

14

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

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

Потім, щоб отримати поточну конфігурацію, ви використовуєте щось на зразок:

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

(залежно від вашого смаку СУБД).

Таким чином, ви все одно можете зберігати історію для цілей відновлення (ви можете запровадити процедури очищення, якщо таблиця стає занадто великою, але це малоймовірно), і ви все одно зможете працювати з останньою конфігурацією.


+1: Я чую, що ви говорите, але я волію записувати історію змін в окремі таблиці аудиту.
Мартін,

+1: за поширення цікавої ідеї впровадження аудиторського шляху.
Джон Сансом,

Приємно. JustSELECT TOP 1 ... ORDER BY creation_date_time DESC
Баодад

5

Ви можете застосувати тригер INSTEAD OF, щоб застосувати цей тип бізнес-логіки в базі даних.

Тригер може містити логіку, щоб перевірити, чи вже існує запис у таблиці, і якщо так, ЗВЕРНІТЬ Вставку.

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


+1 - Я бачу, як спрацьовує тригер, і, можливо, я повернусь до такого підходу, але мені подобається просте обмеження Демієна. Існує цікава дискусія щодо того, який тип даних конфігурації належить до файлу конфігурації, а що - до БД. У цьому випадку я думаю, що БД - правильне місце. Без сумніву, я доживу, щоб пошкодувати про це ... :-)
Мартін,

2

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


2

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

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

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

Потім створіть в таблиці тригер INSERT, який має лише СИГНАЛ (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

Старе питання, а як щодо використання ІДЕНТИЧНОСТІ (МАКС., 1) типу малого стовпця?

CREATE TABLE [dbo].[Config](
[ID] [tinyint] IDENTITY(255,1) NOT NULL,
[Config1] [nvarchar](max) NOT NULL,
[Config2] [nvarchar](max) NOT NULL

Ви можете додати ще один рядок, використовуючи SET IDENTITY_INSERT.
Razvan Socol

1

Ви можете написати тригер для дії вставки в таблиці. Всякий раз, коли хтось намагається вставити новий рядок у таблицю, вимкніть логіку видалення останнього рядка в коді тригера вставки.



-1

Тут ми також можемо зробити невидиме значення, яке буде однаковим після першого входу в базу даних. Приклад: Таблиця студента: Id: int firstname: char Тут у полі введення ми повинні вказати те саме значення для стовпця id, яке обмежить як після першого входу, крім написання бла бла бла через обмеження первинного ключа, таким чином, назавжди має лише один рядок. Сподіваюся, це допомагає!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.