递归获取语言字符串

时间:2016-04-12 08:44:26

标签: sql sql-server sql-server-2008

在我的多语言程序中,我在语言文件生成期间从语言数据库中获取语言字符串,如下所示:

SELECT [key], translation FROM language WHERE lang=@lang 
UNION 
SELECT [key],translation FROM language WHERE lang='EN'
AND [key] NOT IN(SELECT [key] FROM language WHERE lang=@lang)

正如您所看到的,如果我们遇到缺少的翻译字符串,英语就是所有语言的后备。

表格布局为:

[lang] VARCHAR(5)
[key] VARCHAR(100)
[translation] VARCHAR(MAX)

现在有一个语言树是有意义的,除了"英语"有一个后备语言定义。像这样:

English
|- English (UK)
|- German
|  |- German (Switzerland)
|  |- German (Austria)
|- French
|  |- French (Canada)
|  |- Italian
|  |- Portuguese
|  |  |- Portuguese (Brazil)
|- Russian
|  |- Czech

这样,意大利语中尚未提供的语言字符串可能会暂时替换为法语语言字符串,这对于意大利语用户而言可能比英语 [citation needed] 更易读。

此外,德语(瑞士)只需包含与德语完全不同的字符串,并且不必是所有字符串的完整副本。

但是当从数据库中获取语言字符串时,我该怎么做?

我试图理解https://technet.microsoft.com/en-us/library/ms186243(v=sql.105).aspx,但我不知道如何添加约束"只提取尚未成为结果集的一部分的关键字"进入复苏的部分:

WITH MyCTE ([key], translation)
AS
(
    SELECT [key],translation FROM language l1
    WHERE lang = @lang
    UNION ALL
    SELECT [key],translation FROM language l2
    WHERE lang = (SELECT translation FROM language l3 WHERE lang = @lang AND [key] = 'PARENTLANGUAGE')
    AND [key] NOT IN l1
)
SELECT *
FROM MyCTE

样本表滴滴涕:

CREATE TABLE language(
  lang VARCHAR(5) NOT NULL,
  [key] VARCHAR(100) NOT NULL,
  [translation] VARCHAR(MAX) NOT NULL
)

示例数据:

INSERT INTO language([lang],[key],[translation]) ('EN','YES','Yes');
INSERT INTO language([lang],[key],[translation]) ('EN','NO','No');
INSERT INTO language([lang],[key],[translation]) ('EN','OK','OK');
INSERT INTO language([lang],[key],[translation]) ('EN-US','NO','Nope');
INSERT INTO language([lang],[key],[translation]) ('DE','YES','Ja');
INSERT INTO language([lang],[key],[translation]) ('DE','NO','Nein');
INSERT INTO language([lang],[key],[translation]) ('EN','JANUARY','January');
INSERT INTO language([lang],[key],[translation]) ('DE','JANUARY','Januar');
INSERT INTO language([lang],[key],[translation]) ('DE-AT','JANUARY','Jänner');
INSERT INTO language([lang],[key],[translation]) ('EN-US','PARENTLANGUAGE','EN');
INSERT INTO language([lang],[key],[translation]) ('DE','PARENTLANGUAGE','EN');
INSERT INTO language([lang],[key],[translation]) ('DE-AT','PARENTLANGUAGE','DE');

lang = 'DE-AT'的预期结果:

[key]   translation
-------------------
YES     Ja
NO      Nein
OK      OK
JANUARY Jänner

1 个答案:

答案 0 :(得分:1)

这是一个可能的选择。我会继续说,这几乎肯定不是最优雅的方式。

CREATE TABLE dbo.Languages
(
    lang varchar(5) not null CONSTRAINT PK_Languages PRIMARY KEY CLUSTERED,
    langparen varchar(5) null CONSTRAINT FK_Languages_Parent FOREIGN KEY REFERENCES dbo.Languages(lang)
);

CREATE TABLE dbo.Translations
(
    translation_id int not null identity(1,1),
    [key] varchar(100) not null,
    lang varchar(5) not null constraint FK_Translations_Languages FOREIGN KEY REFERENCES dbo.Languages(lang),
    translation varchar(MAX) not null,
    CONSTRAINT PK_Translations PRIMARY KEY NONCLUSTERED([key],lang)
)

CREATE CLUSTERED INDEX CX_Translations ON dbo.Translations (translation_id);
GO

INSERT INTO dbo.Languages
(lang, langparen)
SELECT 'EN', NULL
UNION ALL
SELECT 'EN-US', 'EN'
UNION ALL
SELECT 'DE', 'EN'
UNION ALL 
SELECT 'DE-AT', 'DE';

INSERT INTO dbo.Translations([lang],[key],[translation]) VALUES('EN','YES','Yes');
INSERT INTO dbo.Translations([lang],[key],[translation]) VALUES('EN','NO','No');
INSERT INTO dbo.Translations([lang],[key],[translation]) VALUES('EN','OK','OK');
INSERT INTO dbo.Translations([lang],[key],[translation]) VALUES('EN-US','NO','Nope');
INSERT INTO dbo.Translations([lang],[key],[translation]) VALUES('DE','YES','Ja');
INSERT INTO dbo.Translations([lang],[key],[translation]) VALUES('DE','NO','Nein');
INSERT INTO dbo.Translations([lang],[key],[translation]) VALUES('EN','JANUARY','January');
INSERT INTO dbo.Translations([lang],[key],[translation]) VALUES('DE','JANUARY','Januar');
INSERT INTO dbo.Translations([lang],[key],[translation]) VALUES('DE-AT','JANUARY','Jänner');

go


with recurse as
(
    select lang, langparen, lvl = 0, list = cast(lang as varchar(8000)) from dbo.Languages where langparen IS NULL
    UNION ALL
    select lang.lang, lang.langparen, paren.lvl + 1, list = cast(paren.list + ',' + lang.lang as varchar(8000))
    FROM dbo.Languages lang
    JOIN recurse paren ON paren.lang = lang.langparen 
)
select * into #recurse from recurse

declare @lang varchar(100) = 'DE-AT';

declare @langstring varchar(8000);

select @langstring = list from #recurse where lang = @lang

;WITH lists AS
(
SELECT t.*, rid = ROW_NUMBER() OVER (PARTITION BY t.[key] order by lvl desc)
FROM dbo.Translations t
join #recurse r ON t.lang = r.lang
join dbo.split (@langstring, ',') s on s.Item = t.lang
)

SELECT * FROM lists where rid = 1

一些要点:

  1. 我创建了一个单独的Languages表,它既有给定语言又有最直接的父语句,因此我可以为树构建层次结构,如上所述。我把翻译表称为“翻译”。

  2. 我写了一个递归查询,它也从上到下结合了树的“路径”。使用hierarchy数据类型几乎可以更优雅地完成这一点,但我发现这种方式不那么繁琐。

  3. 对于主查询,我同时提取递归结果以及最高级别的字符串(在本例中为“De-At”)。通过递归结果,您可以针对每个可用密钥执行ROW_NUMBER调用,该调用从递归的最高“级别”开始,然后向下运行。