기본 제약, 그만한 가치가 있습니까?


19

나는 보통 다음 규칙에 따라 데이터베이스를 설계합니다.

  • db_owner 및 sysadmin 이외의 다른 사람은 데이터베이스 테이블에 액세스 할 수 없습니다.
  • 사용자 역할은 응용 프로그램 계층에서 제어됩니다. 일반적으로 하나의 db 역할을 사용하여 뷰, 저장 프로 시저 및 함수에 대한 액세스 권한을 부여하지만 경우에 따라 일부 저장 프로 시저를 보호하기 위해 두 번째 규칙을 추가합니다.
  • 우선 TRIGGERS를 사용하여 중요한 정보의 유효성을 검사합니다.

CREATE TRIGGER <TriggerName>
ON <MyTable>
[BEFORE | AFTER] INSERT
AS
    IF EXISTS (SELECT 1 
               FROM   inserted
               WHERE  Field1 <> <some_initial_value>
               OR     Field2 <> <other_initial_value>)
    BEGIN
        UPDATE MyTable
        SET    Field1 = <some_initial_value>,  
               Field2 = <other_initial_value>  
        ...  
    END
  • DML은 저장 프로 시저를 사용하여 실행됩니다.

sp_MyTable_Insert(@Field1, @Field2, @Field3, ...);
sp_MyTable_Delete(@Key1, @Key2, ...);
sp_MyTable_Update(@Key1, @Key2, @Field3, ...);

이 시나리오에서 DEFAULT CONSTRAINT를 사용할 가치가 있다고 생각합니까, 아니면 DB 서버에 불필요한 작업을 추가하고 있습니까?

최신 정보

DEFAULT 제약 조건을 사용하여 데이터베이스를 관리 해야하는 다른 사람에게 더 많은 정보를 제공하고 있음을 이해합니다. 그러나 나는 주로 성능에 관심이 있습니다.

올바른 값을 제공하더라도 데이터베이스가 항상 기본값을 확인한다고 가정하므로 동일한 작업을 두 번 수행합니다.

예를 들어, 트리거 실행 내에서 DEFAULT 제약 조건을 피할 수있는 방법이 있습니까?


1
모든 DML에 절차를 사용하는 경우 유효성 검사 논리를 수행합니다. 제한 조건을 사용하여 기본 테이블에 액세스하는 사람이 실수를하지 않도록 방지하지만 트리거는 삽입 속도를 크게 저하시킵니다.
Jonathan Fite

트리거에서 어떤 유효성 검사를 수행하고 있습니까? 대신 검사 제한 조건으로 수행 할 수 있습니까?
Martin Smith

@MartinSmith 의존하지만 검사 제약 조건이 항상 적용되므로 사용자, 현재 시간 등과 같은 초기 값을 확인해야합니다. 내 질문 중 다른 것을 살펴보십시오. dba.stackexchange.com/questions/164678/…
McNets

1
"일반적인"답변을 원하면 "제약"부분을 제거해야합니다. SQL에서 제약 조건은 입력 값의 유효성을 검사 합니다. 그들은 기본 정의하지 않은 값을 . SQL에는 "기본 제약 조건"과 같은 것이 없습니다. 나는 태그를 추가 sql-server하고 tsql당신의 질문의 코드로 그 DBMS에 매우 다릅니다
a_horse_with_no_name

답변:


21

올바른 값을 제공하더라도 데이터베이스가 항상 기본값을 확인한다고 가정하므로 동일한 작업을 두 번 수행합니다.

음, 왜 그렇게 생각하십니까? ;-). 연결된 열이 INSERT명령문에 존재하지 않을 때 기본값을 제공하여 값을 제공한다는 점을 감안할 때 , 연관된 열이 INSERT명령문 에 있으면 완전히 무시된다고 가정합니다 .

다행스럽게도 우리 중 어느 누구도 문제 의이 진술로 인해 아무것도 가정 할 필요가 없습니다.

나는 주로 성능에 관심이 있습니다.

성능에 대한 질문은 거의 항상 테스트 할 수 있습니다. 따라서 SQL Server (여기의 실제 권한)가이 질문에 대답 할 수 있도록 테스트를해야합니다.

설정

다음을 한 번 실행하십시오.

SET NOCOUNT ON;

-- DROP TABLE #HasDefault;
CREATE TABLE #HasDefault
(
  [HasDefaultID] INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
  [SomeInt] INT NULL,
  [SomeDate] DATETIME NOT NULL DEFAULT (GETDATE())
);

-- DROP TABLE #NoDefault;
CREATE TABLE #NoDefault
(
  [NoDefaultID] INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
  [SomeInt] INT NULL,
  [SomeDate] DATETIME NOT NULL
);

-- make sure that data file and Tran Log file are grown, if need be, ahead of time:
INSERT INTO #HasDefault ([SomeInt])
  SELECT TOP (2000000) NULL
  FROM   [master].sys.[all_columns] ac1
  CROSS JOIN [master].sys.[all_columns] ac2;

타이밍 1을 기울이기 때문에 테스트 1A와 1B를 개별적으로 실행하십시오. 각각에 대한 평균 타이밍을 파악하려면 각각을 여러 번 실행하십시오.

테스트 1A

TRUNCATE TABLE #HasDefault;
GO

PRINT '#HasDefault:';
SET STATISTICS TIME ON;
INSERT INTO #HasDefault ([SomeDate])
  SELECT TOP (1000000) '2017-05-15 10:11:12.000'
  FROM   [master].sys.[all_columns] ac1
  CROSS JOIN [master].sys.[all_columns] ac2;
SET STATISTICS TIME OFF;
GO

테스트 1B

TRUNCATE TABLE #NoDefault;
GO

PRINT '#NoDefault:';
SET STATISTICS TIME ON;
INSERT INTO #NoDefault ([SomeDate])
  SELECT TOP (1000000) '2017-05-15 10:11:12.000'
  FROM   [master].sys.[all_columns] ac1
  CROSS JOIN [master].sys.[all_columns] ac2;
SET STATISTICS TIME OFF;
GO

타이밍이 왜곡되므로 테스트 2A 및 2B를 개별적으로 실행하십시오. 각각에 대한 평균 타이밍을 파악하려면 각각을 여러 번 실행하십시오.

테스트 2A

TRUNCATE TABLE #HasDefault;
GO

DECLARE @Counter INT = 0,
        @StartTime DATETIME,
        @EndTime DATETIME;

BEGIN TRAN;
--SET STATISTICS TIME ON;
SET @StartTime = GETDATE();
WHILE (@Counter < 100000)
BEGIN
  INSERT INTO #HasDefault ([SomeDate]) VALUES ('2017-05-15 10:11:12.000');
  SET @Counter = @Counter + 1;
END;
SET @EndTime = GETDATE();
--SET STATISTICS TIME OFF;
COMMIT TRAN;
PRINT DATEDIFF(MILLISECOND, @StartTime, @EndTime);

테스트 2B

TRUNCATE TABLE #NoDefault;
GO

DECLARE @Counter INT = 0,
        @StartTime DATETIME,
        @EndTime DATETIME;

BEGIN TRAN;
--SET STATISTICS TIME ON;
SET @StartTime = GETDATE();
WHILE (@Counter < 100000)
BEGIN
  INSERT INTO #NoDefault ([SomeDate]) VALUES ('2017-05-15 10:11:12.000');
  SET @Counter = @Counter + 1;
END;
SET @EndTime = GETDATE();
--SET STATISTICS TIME OFF;
COMMIT TRAN;
PRINT DATEDIFF(MILLISECOND, @StartTime, @EndTime);

테스트 1A와 1B 사이 또는 테스트 2A와 2B 사이의 타이밍에는 실제 차이가 없음을 알아야합니다. 따라서, DEFAULT정의되었지만 사용 하지 않은 성능에 대한 불이익은 없습니다 .

또한 의도 된 동작을 문서화하는 것 외에도 저장 프로 시저에 완전히 포함 된 DML 문에 관심이있는 사람은 대부분 사용자임을 명심해야합니다. 지원 담당자는 신경 쓰지 않습니다. 미래의 개발자는 모든 DML을 이러한 저장 프로 시저 내에 캡슐화하거나 알고있는 경우에도 관리하려는 사용자의 요구를 인식하지 못할 수 있습니다. 그리고 당신이 사라진 후에 (다른 프로젝트 나 직업)이 DB를 유지하는 사람은 아무리 항의하더라도 ORM의 사용을 신경 쓰지 않거나 막지 못할 수도 있습니다. 따라서 Defaults 는 지원 담당자가 수행 할 때 INSERT특별 INSERT하게 수행 할 때 사람들에게 "아웃"을 제공하는 데 도움이 될 수 있습니다. 날짜 열).


그리고, DEFAULT관련 열이 INSERT명령문에 존재할 때 a 가 검사 되는지 여부를 다소 객관적으로 표시 할 수 있다는 것이 나에게 발생했습니다 . 유효하지 않은 값을 제공하십시오. 다음과 같은 테스트 만 수행됩니다.

-- DROP TABLE #BadDefault;
CREATE TABLE #BadDefault
(
  [BadDefaultID] INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
  [SomeInt] INT NOT NULL DEFAULT (1 / 0)
);


INSERT INTO #BadDefault ([SomeInt]) VALUES (1234); -- Success!!!
SELECT * FROM #BadDefault; -- just to be sure ;-)



INSERT INTO #BadDefault ([SomeInt]) VALUES (DEFAULT); -- Error:
/*
Msg 8134, Level 16, State 1, Line xxxxx
Divide by zero error encountered.
The statement has been terminated.
*/
SELECT * FROM #BadDefault; -- just to be sure ;-)
GO

보시다시피, 열 (및 키워드 DEFAULT가 아닌 값 )이 제공되면 기본값은 100 % 무시됩니다. 우리는 INSERT성공 하기 때문에 이것을 알고 있습니다. 그러나 Default가 사용되면 최종적으로 실행될 때 오류가 발생합니다.


트리거 실행 내에서 DEFAULT 제약 조건을 피할 수있는 방법이 있습니까?

(이 문맥에서 적어도) 초기 구속을 피하기 위해 필요하지만 완전성을 위하여는 단지 "를 방지의"내 기본 구속 할 수있을 것이라는 점을 알 수있다 완전히 불필요 INSTEAD OF하지만, 트리거 되지 내의 AFTER트리거. CREATE TRIGGER 의 문서에 따르면 :

트리거 테이블에 제한 조건이 존재하면 INSTEAD OF 트리거 실행 후 및 AFTER 트리거 실행 전에 제한 조건이 점검됩니다. 제한 조건을 위반하면 INSTEAD OF 트리거 조치가 롤백되고 AFTER 트리거가 실행되지 않습니다.

물론 INSTEAD OF트리거를 사용 하려면 다음이 필요합니다.

  1. 기본 구속 조건 비활성화
  2. AFTER구속 조건을 활성화 하는 트리거 생성

그러나 나는 이것을하는 것을 권장하지 않습니다.


1
@McNets 문제 없습니다 :-). 이 질문은 실제로 무언가를 연구 할 수있는 좋은 기회를 제공했습니다. 그리고 그렇게하면서, 나는 MySQL에서 테스트하려고 시도했지만 (일반적으로 RDBMS에 대해 일반적으로 질문했기 때문에) MySQL의 기본값은 상수이어야 CURRENT_TIMESTAMP하며 날짜 시간 열에 대한 예외는 예외입니다 . 따라서 유효하지 않은 표현식을 사용하는 "쉬운"테스트는 작동하지 않으며 (유효한 값은 CREATE TABLE유효성 검사 이후 사용할 수 없습니다 ) 성능 테스트를 구성 할 시간이 없습니다. 그러나 대부분의 RDBMS가 동일한 방식으로 처리 할 것이라고 생각합니다.
Solomon Rutzky

9

기본 제약 조건이 있으면 크게 해가되지 않습니다. 사실, 나는 하나의 특별한 장점을 보았습니다-테이블 정의 자체와 동일한 수준의 논리에서 기본값을 정의했습니다. 저장 프로 시저에 제공 한 기본값이 있으면 누군가 기본값으로 이동해야합니다. 그리고 그것은 시스템을 처음 접하는 사람에게는 분명하지 않을 것입니다. (예를 들어, 내일 10 억 달러를 물려 받고, 자신의 열대 섬을 구입하고, 그 곳을 떠나서 이동하면 다른 가난한 수액은 물건을 알아낼 수 있습니다) 자체적으로).

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