多语言系统的数据库设计

之前做的项目涉及到中国大陆和纽伦新港的用户使用,也就需要做成一个多语言的系统,现在总结下其中一些经验和思考。

首先我们需要确认我们要做的系统,多语言到底是要做多少种语言,以后会不会要求增加更多的语言。比如我们做一个给中国大陆和纽伦新港使用的系统,可以确定的语言就是简体中文、繁体中文和英语,而且可以确定以后也不会增加语言。确定以后是否需要增加语言这一点很重要,决定了我们在数据库设计时,是否需要考虑多语上的扩展性。

先说下在数据库设计时,可以有以下方案实现多语:

一、为每个多语字段建立对应语言的字段列。

比如我们有一个客户表,记录了客户Id、客户名称、客户地址、客户电话等,其中客户名称和客户地址是多语的,而且需要支持简体中文、繁体中文和英语,于是我们可以将客户表设计如下:

create table Client
(
 ClientId int primary key,
 NameChs nvarchar(50),
 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','13811255555');

select *
from Client c
where c.ClientId=1

二、建立统一的翻译表,在翻译表中使用多列存储多语言,然后在实体表中外键引用翻译表。

create table Translation
(
 TranslationId int primary key,
 TextChs nvarchar(200),
 TextCht nvarchar(200),
 TextEng varchar(200),
)

create table Client
(
 ClientId int primary key,
 NameTranId int references Translation(TranslationId),
 AddressTranId int references Translation(TranslationId),
 TelephoneNumber varchar(200)
)

这样要查询数据时,需要将Translation表JOIN2次,获得对应的Name和Address的多语。

insert into Translation values(10,'工商银行','工商銀行','ICBC');
insert into Translation values(20,'中国北京','中國北京','China,Beijing');
insert into Client values(1,10,20,'13811255555');

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 primary key,
 TelephoneNumber varchar(200)
)
create table Client_MultiLanguages
(
 CLId int primary key,
 ClientId int references Client(ClientId),
 Name nvarchar(200),
 Address nvarchar(200),
 Language char(3)
)

这样的优点是便于扩展,在Schema中并没有定义具体的语言,所以如果要增加语言的话,只需要在多语表中增加一行对应的数据即可。查询也相对比较简单,执行要将原表与对应的多语表JOIN,然后跟上具体的语言作为WHERE条件,即可完成对数据的查询,比如要查询Id为1的Client对象的英语实例:

insert into Client values(1,'13811255555');
insert into Client_MultiLanguages values(1,1,'工商银行','中国北京','CHS');
insert into Client_MultiLanguages values(2,1,'工商銀行','中國北京','CHT');
insert into Client_MultiLanguages values(3,1,'ICBC','China,Beijing','ENG');

select c.*,cm.Name,cm.Address
from Client c inner join Client_MultiLanguages cm
on c.ClientId=cm.ClientId
where c.ClientId=1 and cm.Language='ENG'

四、建立统一翻译表和对应的多语表,在每个多语列指向翻译表。

create table Translation
(
 TranslationId int primary key
)

create table Client
(
 ClientId int primary key,
 NameTranId int references Translation(TranslationId),
 AddressTranId int references Translation(TranslationId),
 TelephoneNumber varchar(200)
)

create table TranslationEntity
(
 TranslationEntityId int primary key,
 TranslationId int references Translation(TranslationId),
 Language char(3),
 TranslatedText nvarchar(200)
)

如果要查询Id为1的Client对应的英语实例,那么脚本为:

insert into Translation values(10);
insert into Translation values(20);
insert into Client values(1,10,20,'13811255555');
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=1 and tne.Language='ENG' and tae.Language='ENG'

这个数据的插入和查询也太复杂了。同时也可以注意到在查询时根本没有用到Translation表,其实这个表只是标识每个数据实例中的多语字段,可以直接使用数据库的Sequence生成或者使用GUID,只要保证全局唯一即可。另外也可以注意到在查询时JOIN了2次TranslationEntity 表,如果一个表的多语字段比较多,比如有10个字段有多语,那么查询是就需要JOIN10次,这个效率会很低。另外还可以注意到,在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是该对象中多语的属性个数.

时间: 2024-10-30 21:01:40

多语言系统的数据库设计的相关文章

公交车路线查询系统后台数据库设计——关联地名和站点

在<公交车路线查询系统后台数据库设计--查询算法>一文中,已经实现了查询站点到站点的路线 查询算法,但是,现实中用户不一定使用站点进行查询,而是使用地名.因此,公交车查询系统数据库必 需记录地名与站点的对应关系,在查询时将地名映射为站点.根据实际情况,某一地点附近通常有几个站 点,因此,地名与站点之间是多对多的关系.显然,只需创建一个地名站点关系表stop_spot(Stop,Spot) 用于储存这个关系即可.数据库关系图如下: 注: Route:路线表 Stop:站点表 Spot:地名表 s

新闻-教务网站信息发布系统的数据库设计

问题描述 教务网站信息发布系统的数据库设计 教务网站信息发布系统需求分析 一.新闻发布 普通富文本编辑器(百度UEditor等).格式字符经过转义以后储存在数据库中. 允许上传图片.图片采用文件格式储存,不用数据库储存. 允许上传附件(一个),限制格式. 允许自定义排序,采用排序号. 允许设置所属专题. 允许设置标题链接:设置后,点击不进入内容页面,直接进入设置的链接. 置顶方式:采用排序号. 二.栏目管理 只有超级管理员具有栏目管理权限. 新闻最多分为三级栏目.(或者以设置父栏目的形式实现多级

数据库建模-业务系统系统权限数据库设计问题

问题描述 业务系统系统权限数据库设计问题 本人菜鸟,最近公司正在准备一个新项目,在权限部分的数据库设计上出现了问题. 之前框架的权限部分是基于角色的权限管理,角色与模块和操作多对多关联,关联信息存在中间表里,但是这样做会使中间表数据量非常大,每次登录都去关联各个表查询. 现在还有一个新的方法,就是取消掉模块和操作表,取而代之的是把各个角色的模块和操作权限直接用一条JSON保存到角色表的字段里,这样每次登录只查询角色表把权限JSON拿到后台处理就行了,查询速度是快了,但是如果模块和操作变动的时候就

公交车路线查询系统后台数据库设计——查询算法

1. 公交车路线信息在数据库中的存储方式 显然,如果在数据库中简单的使用表bus_route(路线名,路线经过的站点,费用)来保存公交车路线的线 路信息,则很难使用查询语句实现乘车线路查询,因此,应该对线路的信息进行处理后再保存到数据库中 ,笔者使用的方法是用站点-路线关系表stop_route(站点,路线名,站点在路线中的位置)来存储公交车路 线,例如,如果有以下3条路线 R1:S1->S2->S3->S4->S5 R2:S6->S7->S2->S8 R3:S8

求产品销售系统的数据库设计

问题描述 本人毕业设计题目:产品销售系统.系统要求:系统采用B/S结构的MVC模式,对用户浏览的相关产品提供相关度推荐链接以及相关产品销售情况等介绍.实现商品的各项销售管理功能,包括用户登录,产品分类,价格查询,结算等模块等.求解数据库的设计.

公交车路线查询系统后台数据库设计——引入步行路线

在<查询算法>和<关联地名和站点>两篇文章中,已经实现了通过地名或站点进行路线查询的算法 ,但是在现实中,从起点到终点不一定全程都是乘车,例如,有以下3条路线: R1: S1->S2->S3->S4->S5 R2: S6->S7->S2->S8 R3: S8->S9->S10 假如现在要从站点S1到S7,如果用Inquiry查询路线,显然没有合适的乘车方案.但是S2和S7相距仅仅 一个站的距离,可以用步行代替,因此可以先从S1乘

公交车路线查询系统后台数据库设计——换乘算法改进与优化

在<查询算法>一文中已经实现了换乘算法,但是,使用存储过程InquiryT2查询从"东圃镇"到"车 陂路口"的乘车路线时,发现居然用了5分钟才查找出结果,这样的效率显然不适合实际应用.因此,有 必要对原有的换乘算法进行优化和改进.在本文中,将给出一种改进的换乘算法,相比原有的算法,改进 后的算法功能更强,效率更优. 1. "压缩"RouteT0 假设RouteT0有以下几行 如下图所示,当查询S1到S4的二次换乘路线时,将会产生3×2

.NET数据库编程求索之路--2.示例系统概览与设计(1)

2  示例系统概览与设计(1) 本章首先向你介绍一个贯穿全书的小型示例系统(订单管理系统),然后引导你完成系统数据库和界面的设计.创建工作. 本章主要内容: 系统概览 设计数据库 创建数据库 设计界面 2.1  示例系统概览 示例非常简单,是一家商店用的订单管理系统.你可以把这家商店想象成一个家居用品商店,顾客在商店里体验并挑选中意的商品,售货员记录顾客的联系方式.订购的商品并收取定金,然后通知仓库配货和发货,最后顾客收到货物并支付尾款,交易完成.我们的订单管理系统是辅助售货员工作的程序,它在主

一个购物系统的数据库订单表该如何设计

问题描述 一个购物系统的数据库订单表该如何设计 订单中要显示购买者信息,购买者配送地址,商品名,价格,数量其中商品可能是多个. 解决方案 这个最好是设计2个表,,一个订单,,一个订单详情,,订单中是用户的信息与购买的商品名,,订单详情中是商品的具体价格,数量,介绍等信息 解决方案二: 一张用户信息表,一张订单详情表,用户信息表唯一,主键是订单详情表的外键