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


8

这个问题是一个比在这些旧问题中已经解决过的问题稍微复杂的问题,所有这些都是重复的:

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

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

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

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


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

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

有一个问题:排序规则

不同的语言具有不同的排序规则序列,而适用于一种语言的排序规则不适用于另一种语言。因此,如果所有语言的所有文本都在同一列上,它们将具有什么排序顺序?我们将如何查询数据库以查找特定文本的文本ID?尽管在网络产品中,搜索的准确性和性能可能并不十分重要,但出于讨论的目的,让我们假设它们确实很重要。

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

  • SQL CREATE INDEX命令支持排序规则规范。(尽管有传言说Microsoft SQL Server不支持它;有人知道吗?)

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

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

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


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


2
如果仅Microsoft产品的功能真的很酷并且经过深思熟虑,则它应该有很大的机会及时获得其他供应商对类似产品的支持。只是一个想法。

Answers:


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)

此设计具有多个缺点(包括限制为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)

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

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

当然,在“所有翻译的单一表”设计中,全文索引也是有问题的,因为全文索引(所有索引)要求每列设置语言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)
)

这种安排确实具有一定的简单性,并且可以与全文索引一起使用,尽管它确实需要在每种新语言中添加新的列,而且许多开发人员发现这种结构使用起来很拙劣和不令人满意。

每个替代方案都有优点和缺点,并且需要在某种程度上进行间接调用,因此它可能取决于相关开发人员在最容易找到这种间接调用的位置。我想大多数人会出于大多数目的而选择多表设计。


我可能会使用一个单独的表,而不是更好的物理布局一个单独的列:这是我的回答说,鼓舞了这个问题dba.stackexchange.com/a/9954/630
GBN

5

显然,您希望使用每种语言的表格:xxx_enxxx_frxxx_eo。这将是最佳选择,并允许依赖于语言的排序规则。甚至可以想象每种语言[en] [xxx][fr] [xxx][eo] [xxx]都有一个数据库。

因此,技术细节具有次要的重要性(要么可以优化,要么不能优化)。

实际的文本键放在表格xxx上


2
问题在于它是非常不相关的。
Mike Nakis 2011年

是的,我的经验是,无论是数据库支持还是自行完成的文本搜索都很难进行关系集成。无论如何,谢谢您的意见。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.