SQL Server: jak ograniczyć tabelę, aby zawierała pojedynczy wiersz?


81

Chcę przechowywać pojedynczy wiersz w tabeli konfiguracji dla mojej aplikacji. Chciałbym wymusić, aby ta tabela zawierała tylko jeden wiersz.

Jaki jest najprostszy sposób na wymuszenie ograniczenia jednego wiersza?


Dlaczego nie użyć tabeli z kolumnami (Name, Value)z kluczem podstawowym w nazwie. Wtedy możesz mieć select Value from Table where Name = ?pewność, że nie zostaną zwrócone żadne wiersze lub jeden wiersz.
a'r

2
Nie jestem pewien, czy sql jest tutaj najlepszym rozwiązaniem. Może zwykły plik xml jest bardziej odpowiedni do konfiguracji. Myślę, że konfiguracja! = Data i sql została stworzona dla danych.
remi bourgarel

2
@ar - zauważyłem, że źle się to dzieje, gdy spodziewasz się odczytać, powiedzmy, liczbę całkowitą, i otrzymujesz źle sformatowaną wartość w kolumnie wartości.
Damien_The_Unbeliever

@Damien_The_Unbeliever Dlaczego tak się stało? Ponieważ podałeś nieistniejącą wartość Name?
Noumenon

1
@Noumenon - zwróć uwagę, że mój komentarz był odpowiedzią na arkomentarz s. Problem polega na tym, że jeśli przechowujesz tylko pary nazwa / wartość, wartość całkiem dobrze musi być ciągiem i nie masz możliwości wymuszenia walidacji w bazie danych. Gdy używasz jednorzędowej tabeli z oddzielnymi kolumnami dla każdego ustawienia (zgodnie z wymaganiami OP), możesz łatwo wymusić walidację dla każdego ustawienia konfiguracji za pomocą ograniczeń sprawdzających.
Damien_The_Unbeliever

Odpowiedzi:


97

Upewnij się, że jedna z kolumn może zawierać tylko jedną wartość, a następnie ustaw ją jako klucz podstawowy (lub zastosuj ograniczenie niepowtarzalności).

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

Mam wiele takich tabel w różnych bazach danych, głównie do przechowywania konfiguracji. O wiele przyjemniej jest wiedzieć, że jeśli element konfiguracyjny powinien być intem, zawsze będziesz czytać tylko int z DB.


2
+1. Takie podejście stosuje Celko dla pomocniczej tabeli stałych w „SQL for Smarties”
Martin Smith

To jest tak proste, jak to tylko możliwe. Dzięki.
Martin,

+1: Ciekawe rozwiązanie. Nie widziałem tego wcześniej, więc dziękuję za udostępnienie. Zawsze droga, łatwa, kiedy wiesz jak ...
John Sansom

Oto pytanie dodatkowe do przemyślenia. Jaki jest klucz podstawowy tej tabeli? :)
nvogel

2
@BZ - cdt jest skrótem od comp.databases.theorygrupy usenet (widocznej przez grupy Google), o której przyznaję, że ostatnio niewiele czytałem. Był bardziej zorientowany na teorię relacyjną niż na SQL - ale akurat wiedziałem, że dportas / sqlvogel również uczęszczał do tej samej grupy. TTM było nawiązaniem do Trzeciego Manifestu , który jest dobrą książką mówiącą (znowu) o teorii relacyjnej, a nie o SQL.
Damien_The_Unbeliever

53

Zwykle stosuję podejście Damiena, które zawsze działało świetnie, ale dodam też jedną rzecz:

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

Dodając „DEFAULT 'X'”, nigdy nie będziesz musiał zajmować się kolumną Lock i nie będziesz musiał pamiętać, która była wartością blokady podczas ładowania tabeli po raz pierwszy.


4
Domyślne ograniczenie również powinno zostać nazwane, w przeciwnym razie otrzyma automatycznie wygenerowaną mylącą nazwę. Lock char(1) not null CONSTRAINT DF_T1_Lock DEFAULT 'X'
David S.

1
Ponadto powinieneś ustawić swoją domyślną wartość inną niż „X”. Zrobiłem mój dłuższy ciąg i to jest teraz mój komunikat o błędzie, jeśli spróbuję wstawić drugi wiersz: Naruszenie ograniczenia PRIMARY KEY „PK_RestrictToOneRow”. Nie można wstawić zduplikowanego klucza do obiektu „dbo.1ROWTABLE”. Zduplikowana wartość klucza to (Ta tabela jest zablokowana w jednym wierszu).
Geoff Griswald

14

Możesz przemyśleć tę strategię. W podobnych sytuacjach często uważałem za nieocenione pozostawienie starych rzędów konfiguracji w poszukiwaniu informacji historycznych.

Aby to zrobić, faktycznie masz dodatkową kolumnę creation_date_time(data / godzina wstawienia lub aktualizacji) i wyzwalacz wstawiania lub wstawiania / aktualizacji, który wypełni ją poprawnie bieżącą datą / godziną.

Następnie, aby uzyskać aktualną konfigurację, użyj czegoś takiego:

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

(w zależności od twojego smaku DBMS).

W ten sposób nadal możesz zachować historię do celów odzyskiwania (możesz wprowadzić procedury czyszczenia, jeśli tabela stanie się zbyt duża, ale jest to mało prawdopodobne) i nadal możesz pracować z najnowszą konfiguracją.


+1: Słyszę, co mówisz, ale wolę zapisywać historię zmian w oddzielnych tabelach audytu.
Martin

+1: Za podzielenie się ciekawym pomysłem wdrożenia ścieżki audytu.
John Sansom,

Miły. Po prostuSELECT TOP 1 ... ORDER BY creation_date_time DESC
Baodad,

5

Możesz zaimplementować wyzwalacz INSTEAD OF Trigger, aby wymusić ten typ logiki biznesowej w bazie danych.

Wyzwalacz może zawierać logikę, aby sprawdzić, czy rekord już istnieje w tabeli, a jeśli tak, ROLLBACK wstawkę.

Teraz, cofając się, by spojrzeć na szerszą perspektywę, zastanawiam się, czy może istnieje alternatywny i bardziej odpowiedni sposób przechowywania tych informacji, na przykład w pliku konfiguracyjnym lub zmiennej środowiskowej?


+1 - Widzę, jak zadziała spust, i może wrócę do tego podejścia, ale podoba mi się proste ograniczenie Damiena. Istnieje interesująca dyskusja na temat tego, jaki typ danych konfiguracyjnych należy do pliku konfiguracyjnego, a co do bazy danych. W tym przypadku myślę, że DB jest właściwym miejscem. Bez wątpienia będę tego żałować ... :-)
Marcin

2

Używam pola bitowego dla klucza podstawowego o nazwie IsActive. Więc mogą być maksymalnie 2 wiersze, a sql, aby uzyskać prawidłowy wiersz, to: wybierz * z Ustawień, gdzie IsActive = 1, jeśli tabela ma nazwę Ustawienia.


2

Oto rozwiązanie, które wymyśliłem dla tabeli typu blokady, która może zawierać tylko jeden wiersz, zawierający Y lub N (na przykład stan blokady aplikacji).

Utwórz tabelę z jedną kolumną. Położyłem ograniczenie sprawdzające na jednej kolumnie, aby można było w niej umieścić tylko Y lub N. (Lub 1 lub 0 lub cokolwiek)

Wstaw jeden wiersz w tabeli ze stanem „normalnym” (np. N oznacza niezablokowane)

Następnie utwórz wyzwalacz INSERT w tabeli, która ma tylko SIGNAL (DB2) lub RAISERROR (SQL Server) lub RAISE_APPLICATION_ERROR (Oracle). To sprawia, że ​​kod aplikacji może aktualizować tabelę, ale każda operacja INSERT kończy się niepowodzeniem.

Przykład 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';

Pracuje dla mnie.


2

Stare pytanie, ale co powiesz na użycie IDENTITY (MAX, 1) małego typu kolumny?

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

Możesz dodać kolejny wiersz, używając SET IDENTITY_INSERT.
Razvan Socol

1

Możesz napisać wyzwalacz dla akcji wstawiania na stole. Za każdym razem, gdy ktoś próbuje wstawić nowy wiersz do tabeli, odpal logikę usuwania ostatniego wiersza w kodzie wyzwalacza wstawiania.



-1

Tutaj możemy również stworzyć niewidoczną wartość, która będzie taka sama po pierwszym wpisie do bazy danych Przykład: Tabela uczniów: Id: int firstname: char Tutaj w polu wejściowym musimy podać tę samą wartość dla kolumny id, która ograniczy jak po pierwszym wpisie innym niż zapis lock bla bla ze względu na ograniczenie klucza podstawowego, a zatem posiadanie tylko jednego wiersza na zawsze. Mam nadzieję że to pomoże!

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.