Sql-Server

使用者特徵

  • May 13, 2020

我真的在與一個問題作鬥爭,我有一個使用者表,該表具有越來越多的使用者特徵(宗教、吸煙偏好等)。到目前為止,我使用的策略是為每個偏好添加一個列,該列與另一個表相關。

例如,如果使用者 XYZ 的 RelgionId 為 3,則可能意味著他們是基督徒。在執行時,如果我需要他們的宗教信仰,我會加入另一張桌子。

到目前為止,這一策略已經奏效。但是,隨著偏好數量的增加,我開始擔心表格中的列數。此外,如果我需要獲取單個使用者的所有值,此策略會導致許多連接。

我想找出表示這些數據的最規範化的方式。有人有什麼想法嗎?

你可以通過創建本質上是這些值的屬性包來做這種事情。這不是我非常喜歡的一種方法,但與 SQL Server 中的所有內容一樣,它有它的位置,你的場景很可能就是這種情況。

這本質上是 EAV 方法,Aaron Bertrand 在這裡有一篇很棒的文章,更詳細地描述了利弊

CREATE TABLE Users
(
   UserID int identity(1, 1) primary key clustered
   ,UserName varchar(200)
);

CREATE TABLE PreferenceType
(
   PreferenceTypeID int identity(1, 1) primary key clustered
   ,PreferenceName varchar(200)
);

CREATE TABLE PreferenceValue
(
   PreferenceValueID int identity(1, 1) primary key clustered
   ,PreferenceValueName varchar(200)
   ,PreferenceTypeID int
);


CREATE TABLE UserPreference
(
   UserPreferenceID int identity(1, 1) primary key clustered
   ,UserID int
   ,PreferenceValueID int
);

ALTER TABLE PreferenceValue
ADD CONSTRAINT  FKValue_Type FOREIGN KEY (PreferenceTypeID) REFERENCES PreferenceType(PreferenceTypeID);

ALTER TABLE UserPreference
ADD CONSTRAINT  FK_UserPreference_User FOREIGN KEY (UserID) REFERENCES Users(UserID);

ALTER TABLE UserPreference
ADD CONSTRAINT  FK_UserPreference_Value FOREIGN KEY (PreferenceValueID) REFERENCES PreferenceValue(PreferenceValueID);


insert into Users (UserName)
Values ('User1' ) --tblUserID = 1
, ('User2' );   --tblUserID = 2

Insert into PreferenceType  (PreferenceName)
Values ('Religion') --Type1
, ('Smoker'); --Type 2

Insert into PreferenceValue (PreferenceValueName, PreferenceTypeID)
Values ('Christian', 1) --Value 1
, ('Muslim', 1) --Value 2
, ('Non Smoker', 2)  --Value 3
, ('Smoker' , 2); -- Value 4


--User 1 is a Christian Smoker
--User 2 is a Muslim Non-Smoker
Insert into UserPreference (UserID, PreferenceValueID)
Values (1, 1)
, (1, 4)
, (2, 2)
, (2, 3);

視覺上:

在此處輸入圖像描述

因此,當您想獲取使用者 1 的資訊時,您將執行此選擇:

select u.UserID, u.UserName, pv.PreferenceValueName, pt.PreferenceName
From Users u
join UserPreference up on u.UserID = up.UserID
join PreferenceValue pv on up.PreferenceValueID = pv.PreferenceValueID
join PreferenceType pt on pv.PreferenceTypeID = pt.PreferenceTypeID
Where u.UserName = 'User1';

你會得到以下格式的結果

使用者 ID、使用者名、PreferenceValue、PreferenceType

1、User1、基督教、宗教

1,使用者1,吸煙者,吸煙者

要在與此類似的模式中包含 Date 值,使用已創建但添加兩個新表的表,您可以執行以下操作:

CREATE TABLE DateType
(
   DateTypeID int identity(1, 1) primary key clustered
   ,DateTypeName varchar(200)
);

CREATE TABLE UserDateType
(
   UserDateTypeID int identity(1, 1) primary key clustered
   ,UserID int
   ,DateTypeID int
   ,DateValue datetime
);

CREATE UNIQUE NONCLUSTERED INDEX
   uq_UserDateType on UserDateType (UserID, DateTypeID);  --Optional. To only allow one row per type.  

ALTER TABLE UserDateType
ADD CONSTRAINT  FKDateType FOREIGN KEY (DateTypeID) REFERENCES DateType(DateTypeID);

ALTER TABLE UserDateType
ADD CONSTRAINT  FKUserDate_UserID FOREIGN KEY (UserID) REFERENCES Users(UserID);


Insert into DateType (DateTypeName)
Values ('BirthDate') --Type1
, ('Anniversary'); --Type 2

Insert into UserDateType (UserID, DateTypeID, DateValue)
Values (1, 1, '01/01/1980') --User 1 has a Birthday of 01/01/1980
, (1, 2, '01/01/2000')  --User 1 has an Anniversary of 01/01/2000
, (2, 1, '02/01/1995');  --User 2 has an Birthday of 01/01/2000

通過擁有這些類型的表,您可以動態添加新的日期類型並以這種方式將它們與使用者相關聯。

如上所述,我不確定這是不是理想的方法,因為它肯定有缺陷,但這是一種你可以用 Date 值做這類事情的方法。

規範化是一個很好的概念,對於性能良好的關係數據庫至關重要。然而,它可能會適得其反。例如,您幾乎從未見過這樣分解的地址:

StateRegionTable    CityTable        ZipTable
  StateId             CityId           ZipId
  StateDesc           CityDesc         ZipDesc

AddressTable
  AddressId
  Address1
  Address2
  CityId
  StateId
  ZipId

它並不是那麼有用,並且通過額外的連接減慢了速度。在您的情況下,我建議刪除表只有 Id/Description 對的額外表。

因此,沒有一個 ReligionTable 和一個 SmokingTable 和一個 GenderTable 等,而是只有帶有 CHECK 約束的描述列。這確實意味著如果您想向列表中添加額外的值,您將不得不修改檢查約束,但對於這些類型的表,它不應該經常發生。當您的查找表需要額外資訊時,您不想這樣做。例如,如果您的 SmokingTable 有一個“附加保險費用”列或類似的內容,那麼您需要一個單獨的查找表。

你的桌子看起來像這樣:

CREATE TABLE #Users (
   UserId INT,
   Religion VARCHAR(50) CONSTRAINT ck_Users_Religion CHECK (Religion IN ('Christian','Jew','Budist')),
   SmokingPreference CHAR(3) CONSTRAINT ck_Users_Smoking CHECK (SmokingPreference IN ('YES','NO'))
   )

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