Sql-Server

表列引用表 B 的部分鍵

  • April 14, 2018

在我的數據庫(MS SQL Server)中,我使用特定於語言的文本。所以我定義表格文本如下:

CREATE TABLE [dbo].[Texts](
   [Id] [int] NOT NULL,
   [language] [nchar](5) NOT NULL,
   [text] [nvarchar](max) NULL,
CONSTRAINT [PK_Texts] PRIMARY KEY CLUSTERED 
(
   [Id] ASC,
   [language] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
   IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, 
   ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]


ALTER TABLE [dbo].[Texts]  WITH CHECK ADD  CONSTRAINT [CK_Texts_language]       
CHECK  (([language]='en-US' OR [language]='de-DE'))

ALTER TABLE [dbo].[Texts] CHECK CONSTRAINT [CK_Texts_language]

這些文本應該在我的表格中的任何地方使用,例如這裡:

CREATE TABLE [dbo].[MyObject](
   [id] [int] NOT NULL,
   [name] [int] NOT NULL,
   [comment] [int] NOT NULL,
CONSTRAINT [PK_PlantDescription] PRIMARY KEY CLUSTERED 
(
   [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

如何指定一個值MyObject.nameMyObject.comment應該是部分鍵值的約束Texts.Id

一個 id 可能有多個文本,使用不同的語言。所以 ( Id, language) 是唯一的,而不是Id它本身。

編輯:這是表格文本的範例:

Id  language    text
1   de-DE   Holunder
1   en-US   elder
2   de-DE   Von diedem Busch kann man sowohl die Blüten als auch die Früchte verwerten.
2   en-US   From this bush you can use both flowers and fruits.

和表 MyObject:

Id  name    comment
512 1       2

與UDF一起

CREATE FUNCTION [dbo].[GetText]
(
   @id int,
   @language nchar(5)
)
RETURNS nvarchar(MAX)
AS
BEGIN
   Declare @result nvarchar(max);

   Select @result = text from dbo.Texts where id = @id AND [language] = @language;
   if @result is NULL
   BEGIN
       Select TOP 1 @result = text from dbo.Texts where id = @id ;
   END
   return @result;
END

使用這些定義和以下查詢

SELECT [Id] 
     ,[dbo].[GetText]([name], 'en-US') AS Name
     ,[dbo].[GetText]([comment], 'en-US') AS Comment
 FROM [dbo].[PlantDescription]

我得到結果:

Id  Name    Comment
512 elder   From this bush you can use both flowers and fruits.

您會看到:當我刪除行 (Id = 1) 時,會有一些我必須通過觸發器刪除的孤立行。

或者是否有我在 inetrnet 中找不到的特定語言文本的標準方法?

一種方法是為文本鍵創建域表:

CREATE TABLE TEXT_KEYS
( TEXT_KEY INT NOT NULL PRIMARY KEY ); 

CREATE TABLE Texts
( TEXT_KEY INT NOT NULL REFERENCES TEXT_KEYS (TEXT_KEY)
, [language] [nchar](5) NOT NULL
, [text] [nvarchar](max) NULL
,   CONSTRAINT [PK_Texts] PRIMARY KEY CLUSTERED 
   ( TEXT_KEY ASC, [language] ASC
[...]

現在您可以使用TEXT_KEYfromTEXT_KEYS作為其他表的外鍵。

您可以使用查詢創建一個INSERT TRIGGER查詢以在插入時驗證數據,或者您可以使用類似於如何基於查詢創建 CHECK CONSTRAINT 中的範例的使用者定義函式?. 從那個文章:

-- we want to check if the values in the following tables are a subset of the main state table
CREATE TABLE t1 (stateId INT NOT NULL)

-- create a function to check the state values and return 1 if stateId is invalid
CREATE FUNCTION dbo.fnc_IsValidState(@StateId INT, @TypeId Int)     RETURNS BIT
AS 
BEGIN 
  DECLARE @flag BIT = 1

  IF NOT EXISTS (SELECT 1 FROM state WHERE stateId =@StateId AND TypeId=@TypeId)
  BEGIN
     SET @flag = 0      
  END   

  RETURN      @flag

END

go

--Add a check constraints to the table to call the function 
ALTER TABLE dbo.[t1]  WITH NOCHECK ADD  CONSTRAINT [CK_StateCHeck] CHECK  ((dbo.fnc_IsValidState(StateId,1)=1))

我確實找到了一篇關於小心使用Tibor Karaszi 呼叫 UDF 的約束的警告文章。從那個文章:

小心呼叫 UDF 的約束

你可能只是不明白你的想法。如果還沒有寫部落格,我會感到驚訝,但如果是這樣,那值得重複。這是交易(來自論壇的範例,稍作修改):

我希望一列中的值是唯一的,假設另一列中的值為 1。我可以為此使用 UDF 嗎?

從表面上看,是的。您可以編寫一個 UDF 來傳遞應該有條件唯一的值,並在該 UDF 中檢查有多少行具有此值和 othercolumn = 1。如果超過 1 行,則函式返回 0,否則返回 1(或其他信號“OK”或“Not OK”)。現在,您可以在 CHECK 約束中呼叫此函式。像 CHECK(myFunction(uniqueCol) = 1) 這樣的東西。只要您 INSERT 到表中,這將在表面上完成它的工作。但是如果你更新了一行並且只將某行的 otherColumn 設置為 0 到 1,那麼檢查約束將不會被檢查。優化器足夠聰明,可以理解更新不會改變我們在 CHECK 約束中引用的任何內容,那麼為什麼還要檢查約束呢?這裡的最終結果是約束不’ t 做我們想讓它做的事情。改用觸發器(或其他方法)。

引用自:https://dba.stackexchange.com/questions/203589