多语言用户界面背后的数据库

Mik*_*kis 8 database-design

这个问题是关于一个比这些旧问题中已经解决的问题更复杂的问题,所有这些都是彼此重复的:

多语言数据库结构建议(2011年6月)

保存多语言数据的最佳数据库结构是什么?(2010 年 2 月)

多语言数据库设计的最佳实践是什么?(2009 年 5 月)

多语言数据库的架构(2008 年 11 月)


支持多语言用户界面的最流行的数据库方案似乎是将所有语言的所有翻译文本放在一个包含 3 列的表中:文本 ID、语言代码和文本本身。文本 id 和语言代码共同构成了主键。

这一切都很好,但现在考虑一个复杂的问题:假设文本需要可搜索。例如,假设这是一个多语言电子商店。这意味着对于输入到数据库中的每个产品类别,店主都会以支持的 N 种语言中的每一种输入产品类别的名称,然后购物者就可以通过名称搜索产品类别,用他们自己的语言

有一个问题:Collat​​ion

不同的语言有不同的整理顺序,适用于一种语言的整理顺序不适用于另一种语言。因此,如果所有语言的所有文本都在一个列中,那么它们将具有什么样的整理顺序?我们将如何查询数据库以查找特定文本的文本 ID?虽然在 Web 产品中搜索准确性和性能可能不是非常重要,但为了本次讨论的目的,让我们假设它们确实很重要。

大多数数据库管理员都熟悉“数据库的整理”意义上的整理概念。幸运的是,这只是默认排序规则,如果不存在其他排序规则信息,则使用默认排序规则,但也存在其他可以指定排序规则的地方:

  • SQL CREATE INDEX 命令支持归类规范。(虽然有传言说 Microsoft SQL Server 不支持它;有人知道吗?)

  • SQL SELECT 语句也支持排序规则,但在这种情况下,排序规则规范作为一个函数工作,导致索引扫描而不是索引查找,如果我们想要性能,这可能是不允许的。(再说一次,如果这是我们所能拥有的最好的,那可能总比没有好。)

  • 我还听说在 Microsoft SQL Server 上,您可以拥有非持久化的计算列,您可以在这些列上指定排序规则并创建过滤索引,尽管我以前从未听说过,如果它是 Microsoft-SQL-Server-only功能,那么我宁愿不要使用它,无论它多么酷和经过深思熟虑。

那么,鉴于所有这些,如果目标是一个可更新和可搜索的多语言数据库,我们如何构建我们的数据库,以及我们如何执行我们的查询?


这个问题的灵感来自于此处发生的讨论:如果某些数据少于 4000 个字符,nvarchar(max) 如何将数据存储在数据库中会很快吗?

Pau*_*ite 8

这是可能的存储串与使用同一列不同的排序规则SQL_VARIANT

CREATE TABLE dbo.Localized
(
    text_id     INTEGER NOT NULL,
    lang_id     INTEGER NOT NULL,
    text_body   SQL_VARIANT NOT NULL,

    CONSTRAINT [PK dbo.Localized text_id, lang_id]
        PRIMARY KEY CLUSTERED (text_id, lang_id),
)
GO
INSERT dbo.Localized
    (text_id, lang_id, text_body)
VALUES
    (1001, 2057, N'Database problems' COLLATE Latin1_General_CI_AS);
GO
INSERT dbo.Localized
    (text_id, lang_id, text_body)
VALUES
    (1001, 1025, N'????? ?????? ???????' COLLATE Arabic_CI_AS)
Run Code Online (Sandbox Code Playgroud)

这种设计有几个缺点(包括限制为 8000 字节),尤其是在搜索区域:SQL_VARIANT不能全文索引,并且某些字符串比较功能(例如LIKE)也不能直接使用。在另一方面,它可以创建定期索引SQL_VARIANT并执行更为基本的比较(例如,<=,>)在核对感知方式:

CREATE UNIQUE INDEX uq1 ON dbo.Localized (text_body)
GO
-- One row
SELECT
    l.*
FROM dbo.Localized AS l 
WHERE
    l.text_body = CONVERT(SQL_VARIANT, N'Database problems' COLLATE Latin1_General_CI_AS)

-- No rows (and no collation error!)
SELECT
    l.*
FROM dbo.Localized AS l
WHERE
    l.text_body = CONVERT(SQL_VARIANT, N'Database problems' COLLATE Arabic_CI_AS)

-- One row, index seek, manual version of "LIKE 'D%'"
SELECT
    l.*
FROM dbo.Localized AS l 
WHERE
    l.text_body >= CONVERT(SQL_VARIANT, N'D' COLLATE Latin1_General_CI_AS)
    AND l.text_body < CONVERT(SQL_VARIANT, N'E' COLLATE Latin1_General_CI_AS)
Run Code Online (Sandbox Code Playgroud)

我们也可以编写通常的程序:

CREATE PROCEDURE dbo.GetLocalizedString
    @text_id    INTEGER,
    @lang_id    INTEGER,
    @text_body  SQL_VARIANT OUTPUT
AS
BEGIN
    SELECT
        @text_body = l.text_body
    FROM dbo.Localized AS l
    WHERE
        l.text_id = @text_id
        AND l.lang_id = @lang_id
END
GO
DECLARE @text SQL_VARIANT

EXECUTE dbo.GetLocalizedString
    @text_id = 1001,
    @lang_id = 1025,
    @text_body = @text OUTPUT

SELECT @text
Run Code Online (Sandbox Code Playgroud)

当然,全文索引在“所有翻译的单表”设计中也是有问题的,因为全文索引(all but)需要每列的语言id设置。Joop Eggen 描述的多表设计可以进行全文索引(尽管它自然需要每个表一个索引)。

另一个主要选项是在基表中为每个区域设置一列:

CREATE TABLE dbo.Example
(
    text_id     INTEGER NOT NULL,
    text_2057   NVARCHAR(MAX) COLLATE Latin1_General_CI_AS NULL,
    text_1025   NVARCHAR(MAX) COLLATE Arabic_CI_AS NULL,

    CONSTRAINT [PK dbo.Example text_id]
        PRIMARY KEY CLUSTERED (text_id)
)
Run Code Online (Sandbox Code Playgroud)

这种安排确实具有一定的简单性,并且适用于全文索引,尽管它确实需要为每种新语言添加一个新列,并且许多开发人员发现这种结构不雅且不能令人满意。

每个替代方案都有优点和缺点,并且需要在某个级别或另一个级别进行间接访问,因此这可能取决于相关的开发人员最喜欢找到该间接访问的位置。我想大多数人会在大多数情况下更喜欢多桌设计。


Joo*_*gen 5

显然,您需要每种语言的表格:xxx_enxxx_frxxx_eo。这将是更优化的,并允许依赖于语言的排序规则。甚至可以想象你有一个每种语言的数据库[en][xxx] , [fr][xxx] , [eo][xxx]

技术细节是次要的(可以或不能优化更多)。

实际的文本键位于表xxx 上

  • 问题在于它是非常不相关的。 (2认同)