비프라이머리 키에 대한 외부 키
데이터를 저장하는 테이블이 있는데, 그 행 중 하나가 다른 테이블에 존재해야 합니다.그래서 나는 참조 무결성을 유지하기 위해 외부 키를 원한다.
CREATE TABLE table1
(
ID INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
AnotherID INT NOT NULL,
SomeData VARCHAR(100) NOT NULL
)
CREATE TABLE table2
(
ID INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
AnotherID INT NOT NULL,
MoreData VARCHAR(30) NOT NULL,
CONSTRAINT fk_table2_table1 FOREIGN KEY (AnotherID) REFERENCES table1 (AnotherID)
)
그러나 보시다시피 외부 키인 테이블은 PK가 아닙니다.이 외부 키를 작성하는 방법 또는 이 참조 무결성을 유지하는 더 나은 방법이 있습니까?
비프라이머리 키에 대해 외부 키를 만들려면 해당 키에 고유한 제약 조건이 있는 열이어야 합니다.
온라인 서적:
FORNE KEY 제약조건은 다른 테이블의 PRIMAY KEY 제약조건에만 링크할 필요는 없습니다.또, 다른 테이블의 UNIQUIE 제약조건의 열을 참조하도록 정의할 수도 있습니다.
당신 같은 에는 '우리'를 때AnotherID
니크,,,허허허것것고유한 제약을 적용할 수 없다면 운이 없을 것입니다. 하지만 생각해보면 이 방법은 정말 타당합니다.
앞에서 설명한 바와 같이 후보 키로 완벽한 프라이머리 키를 가지고 있다면 그것을 사용해 보는 것은 어떨까요?
다른 사람들이 지적한 바와 같이 외부 키는 프라이머리 키(일반적으로 ID 열)에 대한 참조로 작성되는 것이 이상적입니다.그러나 우리는 이상적인 세계에 살고 있지 않습니다.또한 스키마를 조금만 변경해도 애플리케이션 로직에 큰 영향을 미칠 수 있습니다.
SSN 열(및 멍청한 기본 키)이 있는 고객 테이블과 SSN 열(고객 데이터의 비즈니스 로직으로 채워지지만 FK는 존재하지 않음)이 포함된 클레임 테이블의 경우를 생각해 보십시오.설계에 결함이 있지만 몇 년 동안 사용되어 왔으며 스키마 상에 3개의 다른 애플리케이션이 구축되었습니다.클레임을 뜯어내는 것은 명백해야 한다.SSN과 진정한 PK-FK 관계를 맺는 것이 이상적이지만 상당한 점검이 될 것입니다.한편, 고객에게 일의의 제약을 가하고 있습니다.SSN 및 FK on Claim 추가.SSN은 애플리케이션에 거의 또는 전혀 영향을 미치지 않고 참조 무결성을 제공할 수 있습니다.
오해하지 마세요. 저는 정상화에 전적으로 찬성하지만, 때로는 실용주의가 이상주의를 이기기도 합니다.반창고로 평범한 디자인을 도울 수 있다면 수술은 피할 수 있을 것이다.
네크로맨싱.
누군가 이곳에 도착하면 특이한 열쇠가 들어 있는 테이블에서 열을 지을 외부 열쇠가 필요할 거예요
문제는 이 문제가 발생하면 데이터베이스 스키마가 정규화 해제된다는 것입니다.
예를 들어 Room-uid 기본 키, DateFrom 및 DateTo 필드 및 다른 uid(여기 RM_Aperture)를 사용하여 룸을 테이블 내에 보관합니다.같은 회의실을 추적하는 ID 및 소프트 삭제 필드(RM_Status 등)입니다.여기서 99는 '삭제'를 의미하며 <>99는 '액티브'를 의미합니다.
그래서 첫 번째 방을 만들 때 RM_을 삽입합니다.UID 및 RM_ApertureRM_UID와 같은 값의 ID.그 후 회의실을 날짜로 종료하고 새로운 날짜 범위로 재정립하면 RM_UID는 newid()이고 RM_Aperture는 newid()입니다.이전 엔트리의 ID가 새로운 RM_Aperture가 됩니다.아이디
그렇다면 RM_Aperture는ID는 고유하지 않은 필드이므로 다른 테이블에서 외부 키를 설정할 수 없습니다.
또한 T_ZO_REM_AP_Raum_Reinigung(여기서 RM_UID는 실제 RM_Aperture)과 같이 고유하지 않은 열/인덱스에 외부 키를 설정할 수 없습니다.아이디)
그러나 잘못된 값을 금지하려면 외부 키를 설정해야 합니다. 그렇지 않으면 데이터 낭비가 더 빨리 발생합니다.
이 경우(애플리케이션 전체를 재기입하는 것 이외)는, 키의 존재를 체크하는 스칼라 함수를 가지는 CHECK 제약 조건을 삽입할 수 있습니다.
IF EXISTS (SELECT * FROM sys.check_constraints WHERE object_id = OBJECT_ID(N'[dbo].[Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung]') AND parent_object_id = OBJECT_ID(N'[dbo].[T_ZO_REM_AP_Raum_Reinigung]'))
ALTER TABLE dbo.T_ZO_REM_AP_Raum_Reinigung DROP CONSTRAINT [Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[fu_Constaint_ValidRmApertureId]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
DROP FUNCTION [dbo].[fu_Constaint_ValidRmApertureId]
GO
CREATE FUNCTION [dbo].[fu_Constaint_ValidRmApertureId](
@in_RM_ApertureID uniqueidentifier
,@in_DatumVon AS datetime
,@in_DatumBis AS datetime
,@in_Status AS integer
)
RETURNS bit
AS
BEGIN
DECLARE @bNoCheckForThisCustomer AS bit
DECLARE @bIsInvalidValue AS bit
SET @bNoCheckForThisCustomer = 'false'
SET @bIsInvalidValue = 'false'
IF @in_Status = 99
RETURN 'false'
IF @in_DatumVon > @in_DatumBis
BEGIN
RETURN 'true'
END
IF @bNoCheckForThisCustomer = 'true'
RETURN @bIsInvalidValue
IF NOT EXISTS
(
SELECT
T_Raum.RM_UID
,T_Raum.RM_Status
,T_Raum.RM_DatumVon
,T_Raum.RM_DatumBis
,T_Raum.RM_ApertureID
FROM T_Raum
WHERE (1=1)
AND T_Raum.RM_ApertureID = @in_RM_ApertureID
AND @in_DatumVon >= T_Raum.RM_DatumVon
AND @in_DatumBis <= T_Raum.RM_DatumBis
AND T_Raum.RM_Status <> 99
)
SET @bIsInvalidValue = 'true' -- IF !
RETURN @bIsInvalidValue
END
GO
IF EXISTS (SELECT * FROM sys.check_constraints WHERE object_id = OBJECT_ID(N'[dbo].[Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung]') AND parent_object_id = OBJECT_ID(N'[dbo].[T_ZO_REM_AP_Raum_Reinigung]'))
ALTER TABLE dbo.T_ZO_REM_AP_Raum_Reinigung DROP CONSTRAINT [Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung]
GO
-- ALTER TABLE dbo.T_AP_Kontakte WITH CHECK ADD CONSTRAINT [Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung]
ALTER TABLE dbo.T_ZO_REM_AP_Raum_Reinigung WITH NOCHECK ADD CONSTRAINT [Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung]
CHECK
(
NOT
(
dbo.fu_Constaint_ValidRmApertureId(ZO_RMREM_RM_UID, ZO_RMREM_GueltigVon, ZO_RMREM_GueltigBis, ZO_RMREM_Status) = 1
)
)
GO
IF EXISTS (SELECT * FROM sys.check_constraints WHERE object_id = OBJECT_ID(N'[dbo].[Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung]') AND parent_object_id = OBJECT_ID(N'[dbo].[T_ZO_REM_AP_Raum_Reinigung]'))
ALTER TABLE dbo.T_ZO_REM_AP_Raum_Reinigung CHECK CONSTRAINT [Check_RM_ApertureIDisValid_T_ZO_REM_AP_Raum_Reinigung]
GO
프라이머리 키는 항상 일의여야 합니다.테이블이 1대 다의 관계인 경우 외부 키는 일의 이외의 값을 허용해야 합니다.테이블이 1 대 다 관계가 아닌 1 대 1 관계로 연결되어 있는 경우에는 외부 키를 프라이머리 키로 사용해도 문제 없습니다.
FORNE KEY 제약조건은 다른 테이블의 PRIMAY KEY 제약조건에만 링크할 필요는 없습니다.또, 다른 테이블의 UNIQUIE 제약조건의 열을 참조하도록 정의할 수도 있습니다.
네, 일반적으로 적어도 색인화해야 합니다.
create table student(
id int,
name varchar(30),
index inName(id)
);
CREATE TABLE grade(
id int,
subject varchar(30),
mark double,
foreign key(id) references student(id)
);
언급URL : https://stackoverflow.com/questions/18435065/foreign-key-to-non-primary-key
'source' 카테고리의 다른 글
테이블(아직 존재하지 않는 경우)에 열을 추가합니다. (0) | 2023.04.07 |
---|---|
datetime2 데이터 유형을 datetime 데이터 유형으로 변환하면 값이 범위를 벗어납니다. (0) | 2023.04.07 |
SQL - 문자열을 검색하는 동안 대/소문자 무시 (0) | 2023.04.07 |
첫 번째 줄에 가입하는 방법 (0) | 2023.04.07 |
SQL Server 트랜잭션의 올바른 사용 (0) | 2023.04.07 |