SQL Server : 단일 행을 포함하도록 테이블을 제한하는 방법은 무엇입니까?


81

내 애플리케이션의 구성 테이블에 단일 행을 저장하고 싶습니다. 이 테이블에 행을 하나만 포함 할 수 있도록 강제하고 싶습니다.

단일 행 제약 조건을 적용하는 가장 간단한 방법은 무엇입니까?


(Name, Value)이름에 기본 키가있는 열 이 있는 테이블을 사용하지 않는 이유는 무엇입니까? 그런 다음 select Value from Table where Name = ?행이 반환되지 않거나 한 행이 반환되지 않을 것이라는 확신을 가질 수 있습니다.
a'r

2
SQL이 여기에 가장 적합한 솔루션인지 잘 모르겠습니다. 간단한 xml 파일이 구성에 더 적합 할 수 있습니다. 나는 구성! = 데이터와 SQL이 데이터를 위해 만들어 졌다고 생각하는데 사용합니다.
remi bourgarel

2
@ar-정수를 읽을 것으로 예상 할 때 심하게 잘못되어 값 열에 잘못된 형식의 값이 표시되는 것을 보았습니다.
Damien_The_Unbeliever

@Damien_The_Unbeliever 왜 그럴까요? 에 대해 존재하지 않는 값을 지정했기 때문에 Name?
Noumenon

1
@Noumenon-내 의견은 ars 의견 에 대한 응답이었습니다 . 문제는 이름 / 값 쌍을 저장하는 경우 값이 문자열이어야 하며 데이터베이스에서 유효성 검사 를 시행 할 수단이 없다는 것 입니다. 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 여야하는 경우 DB에서 int 만 읽을 수 있다는 사실을 아는 것이 훨씬 좋습니다.


2
+1. 이것은 Celko가 "SQL for Smarties"의 보조 상수 테이블에 사용하는 접근 방식입니다
Martin Smith

이것은 얻는 것처럼 간단합니다. 감사.
Martin

+1 : 흥미로운 솔루션입니다. 나는 이것을 전에 본 적이 없으므로 공유해 주셔서 감사합니다. 항상 방법을 알면 쉽게 ....
John Sansom

생각해 볼 후속 질문이 있습니다. 이 테이블의 기본 키는 무엇입니까? :)
nvogel 2010 년

2
@BZ-cdt는 comp.databases.theory, 유즈넷 그룹 (Google 그룹을 통해 볼 수 있음 )의 축약 형 으로 최근에 많이 읽지 않았 음을 인정합니다. SQL보다 관계 이론에 더 중점을 두었지만 dportas / sqlvogel도 같은 그룹을 자주 사용한다는 것을 알게되었습니다. TTM은 The Third Manifesto 에 대한 참조 였는데 , 이것은 SQL보다는 관계 이론에 대해 (다시) 이야기하는 좋은 책입니다.
Damien_The_Unbeliever

53

나는 항상 나를 위해 잘 작동했던 Damien의 접근 방식을 사용하지만 한 가지 추가합니다.

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'"를 추가하면 잠금 열을 처리 할 필요가 없으며 테이블을 처음로드 할 때 잠금 값이 무엇인지 기억할 필요가 없습니다.


4
기본 제약 조건도 이름을 지정해야합니다. 그렇지 않으면 자동 생성 된 혼란스러운 이름을 얻게됩니다. Lock char(1) not null CONSTRAINT DF_T1_Lock DEFAULT 'X'
David S.

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

(DBMS 맛에 따라 다름).

이렇게하면 복구 목적으로 기록을 유지하고 (테이블이 너무 커지지 만 가능성이 적을 경우 정리 절차를 시작할 수 있음) 최신 구성으로 계속 작업 할 수 있습니다.


+1 : 말씀하신 내용을 듣고 있지만 변경 내역을 별도의 감사 테이블에 기록하는 것을 선호합니다.
Martin

+1 : 감사 추적 구현에 대한 흥미로운 아이디어를 공유합니다.
John Sansom

좋은. JustSELECT TOP 1 ... ORDER BY creation_date_time DESC
Baodad

5

INSTEAD OF 트리거 를 구현 하여 데이터베이스 내에서 이러한 유형의 비즈니스 논리를 적용 할 수 있습니다 .

트리거는 테이블에 레코드가 이미 있는지 확인하는 논리를 포함 할 수 있으며 있다면 삽입을 ROLLBACK합니다.

이제 더 큰 그림을보기 위해 한 걸음 뒤로 물러서서 아마도이 정보를 예를 들어 구성 파일이나 환경 변수에 저장할 수있는 더 적합한 대안이 있는지 궁금합니다.


+1-방아쇠가 어떻게 작동하는지 볼 수 있고 그 접근 방식으로 되돌아 갈 수도 있지만 Damien의 단순한 제약이 마음에 듭니다. 구성 파일에 어떤 유형의 구성 데이터가 있고 DB에 무엇이 속하는 지에 대해 흥미로운 논의가 있습니다. 이 경우 DB가 올바른 장소라고 생각합니다. 의심 할 여지없이 나는 이것을 후회하기 위해 살 것이다 ... :-)
Martin

2

IsActive라는 이름의 기본 키에 비트 필드를 사용합니다. 따라서 최대 2 개의 행이있을 수 있으며 유효한 행을 가져 오는 SQL은 다음과 같습니다. select * from Settings where IsActive = 1 if the table named Settings.


2

다음은 Y 또는 N (예 : 애플리케이션 잠금 상태)을 보유하는 하나의 행만 포함 할 수있는 잠금 유형 테이블에 대해 생각 해낸 솔루션입니다.

하나의 열이있는 테이블을 만듭니다. 한 열에 검사 제약을 두어 Y 또는 N 만 넣을 수 있습니다. (또는 1 또는 0, 또는 무엇이든)

테이블에 "정상"상태로 한 행을 삽입합니다 (예 : N은 잠기지 않음을 의미 함).

그런 다음 SIGNAL (DB2) 또는 RAISERROR (SQL Server) 또는 RAISE_APPLICATION_ERROR (Oracle) 만있는 테이블에 INSERT 트리거를 만듭니다. 따라서 애플리케이션 코드가 테이블을 업데이트 할 수 있지만 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

SET IDENTITY_INSERT를 사용하여 다른 행을 추가 할 수 있습니다.
Razvan Socol 2018 년

1

테이블의 삽입 조치에 대한 트리거를 작성할 수 있습니다. 누군가가 테이블에 새 행을 삽입하려고 할 때마다 삽입 트리거 코드에서 최신 행을 제거하는 논리를 실행하십시오.



-1

여기서 우리는 데이터베이스에 처음 입력 한 후 동일하게 될 보이지 않는 값을 만들 수도 있습니다. 기본 키 제약으로 인해 잠금 bla bla를 작성하는 것 이외의 첫 번째 항목 이후와 같이 영원히 하나의 행만 있습니다. 도움이 되었기를 바랍니다!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.