实体内容多语⾔设计⽅案(数据库)
⾸先我们需要确认我们要做的系统,多语⾔到底是要做多少种语⾔,以后会不会要求增加更多的语⾔。⽐如我们做⼀个给中国⼤陆和纽伦新港使⽤的系统,可以确定的语⾔就是简体中⽂、繁体中⽂和英语,⽽且可以确定以后也不会增加语⾔。确定以后是否需要增加语⾔这⼀点很重要,决定了我们在数据库设计时,是否需要考虑多语上的扩展性。
先说下在数据库设计时,可以有以下⽅案实现多语:
⼀、为每个多语字段建⽴对应语⾔的字段列。
⽐如我们有⼀个客户表,记录了客户Id、客户名称、客户地址、客户电话等,其中客户名称和客户地址是多语的,⽽且需要⽀持简体中⽂、繁体中⽂和英语,于是我们可以将客户表设计如下:
create table Client
(
ClientId int IDENTITY(1,1) NOT NULL,
NameChs nvarchar(50),values翻译
NameCht nvarchar(50),
NameEng varchar(200),
AddressChs nvarchar(50),
AddressCht nvarchar(50),
AddressEng varchar(200),
TelephoneNumber varchar(50)
)
这样做的优点是容易理解,容易查询,⼀个客户实例对应的就是数据库中的⼀条数据,与普通的⾮多语数据库⽆异,⽽且由于没有形成新的表,所以也不需要额外的Join,所以查询效率很⾼:
insert into Client values(1,'⼯商银⾏','⼯商銀⾏','ICBC','中国北京','中國北京','China,Beijing','138********');
select*
from Client c
where c.ClientId=1
当然种设计也有缺陷,就是不利于扩展更多的语⾔,冗余字段也可能⽐较多。
⼆、建⽴统⼀的翻译表,在翻译表中使⽤多列存储多语⾔,然后在实体表中外键引⽤翻译表。
create table Translation
(
TranslationId int IDENTITY(1,1) NOT NULL,
TextChs nvarchar(200),
TextCht nvarchar(200),
TextEng nvarchar(200)
)
create table Client
(
ClientId int IDENTITY(1,1) NOT NULL,
NameTranId int,
AddressTranId int,
TelephoneNumber nvarchar(200),
CONSTRAINT FK_Client_Translation_NameTranId_TranslationId FOREIGN KEY
(NameTranId) REFERENCES[Translation](TranslationId) ON DELETE NO ACTION,
CONSTRAINT FK_Client_Translation_AddressTranId_TranslationId FOREIGN KEY
(AddressTranId) REFERENCES[Translation](TranslationId) ON DELETE NO ACTION
)
这样要查询数据时,需要将Translation表JOIN2次,获得对应的Name和Address的多语⾔。
insert into Translation values(10,'⼯商银⾏','⼯商銀⾏','ICBC');
insert into Translation values(20,'中国北京','中國北京','China,Beijing');
insert into Client values(1,10,20,'138********');
select c.ClientId,c.TelephoneNumber,
tn.TextChs as NameChs,tn.TextCht as NameCht,tn.TextEng as NameEng,
ta.TextChs as AddressChs,ta.TextCht as AddressCht,ta.TextEng as AddressEng
from Client c
inner join Translation tn
on c.NameTranId=tn.TranslationId
inner join Translation ta
on c.AddressTranId=ta.TranslationIdwhere
where c.ClientId=1
以上介绍的⽅法都是将多语⾔作为列输出,也就是说有多少种语⾔就会有多少对应的列,不利于新语⾔的增加。
下⾯再介绍将语⾔以数据⾏的形式保存的设计⽅法,这种⽅法可以在后期任意增加语⾔⽽不改动数据库的Schema.
三、将每个表中需要多语的字段独⽴出来,形成⼀个对应的多语表。
多语表外键关联原表,每个需要多语的字段在多语表中对应⼀列,多语表中增加“语⾔”字段。同样以Client表为例,那么对应的表结构如下:create table Client
(
ClientId [INT]IDENTITY(1, 1) NOT NULL,
TelephoneNumber varchar(200)
)
create table ClientLocalizedField
(
ClientLocalizedFieldId [INT]IDENTITY(1, 1) NOT NULL,
ClientId int NOT NULL,
Name nvarchar(200) NOT NULL,
Address nvarchar(200) NULL,
Language byte NOT NULL,
CONSTRAINT FK_ClientLocalizedField_Client_ClientLocalizedFieldId_ClientId FOREIGN KEY
(ClientId) REFERENCES[Client](ClientId) ON DELETE CASCADE
)
这样的优点是便于扩展,在Schema中并没有定义具体的语⾔,所以如果要增加语⾔的话,只需要在多
语表中增加⼀⾏对应的数据即可。查询也相对⽐较简单,执⾏要将原表与对应的多语表JOIN,然后跟上具体的语⾔作为WHERE条件,即可完成对数据的查询,⽐如要查询Id为1的Client对象的英语实例:
insert into Client values(1,'138********');
insert into ClientLocalizedField values(1,1,'⼯商银⾏','中国北京','CHS');
insert into ClientLocalizedField values(2,1,'⼯商銀⾏','中國北京','CHT');
insert into ClientLocalizedField values(3,1,'ICBC','China,Beijing','ENG');
select c.*,cl.Name,cl.Address
from Client c inner join ClientLocalizedField cl
on c.ClientId=cl.ClientId
where c.ClientId=1and cl.Language='ENG'
四、建⽴统⼀翻译表和对应的多语表,在每个多语列指向翻译表。
create table Translation
(
TranslationId int IDENTITY(1,1) NOT NULL,
)
create table Client
(
ClientId int IDENTITY(1,1) NOT NULL,
NameTranId int,
AddressTranId int,
TelephoneNumber nvarchar(200),
CONSTRAINT FK_Client_Translation_NameTranId_TranslationId FOREIGN KEY
(NameTranId) REFERENCES[Translation](TranslationId) ON DELETE NO ACTION,
CONSTRAINT FK_Client_Translation_AddressTranId_TranslationId FOREIGN KEY
(AddressTranId) REFERENCES[Translation](TranslationId) ON DELETE NO ACTION
)
create table TranslationEntity
(
TranslationEntityId int IDENTITY(1,1) NOT NULL,
TranslationId int,
Language char(3),
TranslatedText nvarchar(200),
CONSTRAINT FK_TranslationEntity_Translation_TranslationId_TranslationId FOREIGN KEY
(TranslationId) REFERENCES[Translation](TranslationId) ON DELETE CASCADE
)
如果要查询Id为1的Client对应的英语实例,那么脚本为:
insert into Translation values(10);
insert into Translation values(20);
insert into Client values(1,10,20,'138********');
insert into TranslationEntity values(1,10,'CHS','⼯商银⾏');
insert into TranslationEntity values(2,10,'CHT','⼯商銀⾏');
insert into TranslationEntity values(3,10,'ENG','ICBC');
insert into TranslationEntity values(4,20,'CHS','中国北京');
insert into TranslationEntity values(5,20,'CHT','中國北京');
insert into TranslationEntity values(6,20,'ENG','China,Beijing');
select c.ClientId,tne.TranslatedText as Name,tae.TranslatedText as Address,c.TelephoneNumber
from Client c
inner join TranslationEntity tne
on c.NameTranId=tne.TranslationId
inner join TranslationEntity tae
on c.AddressTranId=tae.TranslationId
where c.ClientId=1and tne.Language='ENG'and tae.Language='ENG'
这个数据的插⼊和查询也太复杂了。同时也可以注意到在查询时根本没有⽤到Translation表,其实这个表只是标识每个数据实例中的多语字段,可以直接使⽤数据库的Sequence⽣成或者使⽤GUID,只要保证全局唯⼀即可。另外也可以注意到在查询时JOIN了2次TranslationEntity 表,如果⼀个表的多语字段⽐较多,⽐如有10个字段有多语,那么查询是就需要JOIN 10次,这个效率会很低。另外还可以注意到,在WHERE条件中写了2次Language='ENG',如果多个多语字段,那么就要写多次。刚才这个查询写的不够严谨,因为不能保证Name字段和Address字段必然就有英⽂值,如果没有英⽂值会导致查询结果为空,所以正确的写法应该是:
select c.ClientId,tne.TranslatedText as Name,tae.TranslatedText as Address,c.TelephoneNumber
from Client c
left join TranslationEntity tne
on c.NameTranId=tne.TranslationId and tne.Language='ENG'
left join TranslationEntity tae
on c.AddressTranId=tae.TranslationId and tae.Language='ENG'
where c.ClientId=1
实际项⽬中,如果我们使⽤了NHibernate等ORMapping⼯具,那么多语字段就会映射成⼀个集合,所以对于某种语⾔的实例,那么需要执⾏N+1次SQL查询,⽽不是JOIN查询,N是该对象中多语的属性个数.

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。