我正在为我的项目进行本地化,通常我会在C#代码中执行此操作,但是我想在sql中执行此操作,因为我试图稍微增加sql.
环境:sql Server 2014 Standard,C#(.NET 4.5.1)
注意:编程语言本身应该是无关紧要的,我只是为了完整性而包含它.
所以我完成了我想要的东西,但没有达到我想要的程度.这已经有一段时间了(至少一年),因为除了基本的sql JOIN之外我已经完成了任何sql JOIN,这是一个非常复杂的JOIN.
以下是数据库相关表格的图表. (这部分还有很多,但不是必需的.)
图像中描述的所有关系在数据库中都是完整的 – PK和FK约束都是设置和操作的.所描述的列都不可为空.所有表都具有模式dbo.
现在,我有一个几乎完成我想要的查询:也就是说,给定任何支持类别ID和任何语言ID,它将返回:
如果该字符串的语言存在正确的正确翻译(即,StringKeyId – > StringKeys.Id存在,并且存在LanguageStringTranslations StringKeyId,LanguageId和StringTranslationId组合,则它将为该StringTranslationId加载StringTranslations.Text.
如果LanguageStringTranslations StringKeyId,LanguageId和StringTranslationId组合不存在,则它将加载StringKeys.Name值. Languages.Id是给定的整数.
我的查询,一团糟,如下:
SELECT CASE WHEN T.x IS NOT NULL THEN T.x ELSE (SELECT CASE WHEN dbo.StringTranslations.Text IS NULL THEN dbo.StringKeys.Name ELSE dbo.StringTranslations.Text END AS Result FROM dbo.SupportCategories INNER JOIN dbo.StringKeys ON dbo.SupportCategories.StringKeyId = dbo.StringKeys.Id INNER JOIN dbo.LanguageStringTranslations ON dbo.StringKeys.Id = dbo.LanguageStringTranslations.StringKeyId INNER JOIN dbo.StringTranslations ON dbo.StringTranslations.Id = dbo.LanguageStringTranslations.StringTranslationId WHERE dbo.LanguageStringTranslations.LanguageId = 38 AND dbo.SupportCategories.Id = 0) END AS Result FROM (SELECT (SELECT CASE WHEN dbo.StringTranslations.Text IS NULL THEN dbo.StringKeys.Name ELSE dbo.StringTranslations.Text END AS Result FROM dbo.SupportCategories INNER JOIN dbo.StringKeys ON dbo.SupportCategories.StringKeyId = dbo.StringKeys.Id INNER JOIN dbo.LanguageStringTranslations ON dbo.StringKeys.Id = dbo.LanguageStringTranslations.StringKeyId INNER JOIN dbo.StringTranslations ON dbo.StringTranslations.Id = dbo.LanguageStringTranslations.StringTranslationId WHERE dbo.LanguageStringTranslations.LanguageId = 5 AND dbo.SupportCategories.Id = 0) AS x) AS T
问题是它无法为我提供所有SupportCategories及其各自的StringTranslations.Text(如果存在),或者它们的StringKeys.Name(如果它不存在).提供任何一个都是完美的,但根本不提供.基本上,强制执行如果某种语言没有特定键的翻译,则默认使用StringKeys.Name,它是StringKeys.DefaultLanguageId的翻译. (理想情况下,它甚至不会这样做,而是加载StringKeys.DefaultLanguageId的翻译,如果指向其他查询的正确方向,我可以自己做.)
我花了很多时间在这上面,我知道如果我只是用C#写它(就像我通常那样)它现在就完成了.我想在sql中执行此操作,并且我无法获得我喜欢的输出.
唯一需要注意的是,我想限制应用的实际查询数量.所有列都已编入索引,例如我现在喜欢它们,如果没有真正的压力测试,我无法进一步索引它们.
编辑:另一个注意事项,我正在尝试尽可能地保持数据库的规范化,所以如果我可以避免它,我不想复制内容.
示例数据
资源
dbo.SupportCategories(整个):
Id StringKeyId 0 0 1 1 2 2
dbo.Languages(185条记录,仅显示两个例子):
Id Abbreviation Family Name Native 38 en Indo-European English English 48 fr Indo-European French français,langue française
dbo.LanguagesStringTranslations(整数):
StringKeyId LanguageId StringTranslationId 0 38 0 1 38 1 2 38 2 3 38 3 4 38 4 5 38 5 6 38 6 7 38 7 1 48 8 -- added as example
dbo.StringKeys(整数):
Id Name DefaultLanguageId 0 Billing 38 1 API 38 2 Sales 38 3 Open 38 4 Waiting for Customer 38 5 Waiting for Support 38 6 Work in Progress 38 7 Completed 38
dbo.StringTranslations(整数):
Id Text 0 Billing 1 API 2 Sales 3 Open 4 Waiting for Customer 5 Waiting for Support 6 Work in Progress 7 Completed 8 Les APIs -- added as example
电流输出
Result Billing
期望的输出
理想情况下,我希望能够省略特定的SupportCategories.Id,并获取所有这些内容(无论是使用语言38英语,还是48法语,或目前任何其他语言):
Id Result 0 Billing 1 API 2 Sales
附加示例
鉴于我要为法语添加本地化(即添加1 48 8到LanguageStringTranslations),输出将更改为(注意:这仅是示例,显然我将向StringTranslations添加本地化字符串)(使用法语示例更新):
Result Les APIs
额外的期望输出
Id Result 0 Billing 1 Les APIs 2 Sales
(是的,从技术上讲,从一致性的角度来看,我知道这是错误的,但这是在这种情况下所希望的.)
编辑:
小更新,我确实更改了dbo.Languages表的结构,并从中删除了Id(int)列,并将其替换为缩写(现在将其重命名为Id,并更新所有相对的外键和关系) .从技术角度来看,我认为这是一个更合适的设置,因为该表仅限于ISO 639-1代码,这些代码一开始就是独一无二的.
文艺青年最爱的
所以:问题是,如何修改此查询以从SupportCategories返回所有内容,然后返回StringKeys.Id的StringTranslations.Text,Languages.Id组合,或者如果它不存在则返回StringKeys.Name?
我最初的想法是,我可以以某种方式将当前查询转换为另一个临时类型作为另一个子查询,并将此查询包装在另一个SELECT语句中并选择我想要的两个字段(SupportCategories.Id和Result).
如果我没有找到任何东西,我将只使用我通常使用的标准方法,即将所有SupportCategories加载到我的C#项目中,然后使用它在上面针对每个SupportCategories.Id手动运行查询.
感谢任何和所有建议/评论/批评.
此外,我为它荒谬漫长道歉,我只是不想有任何歧义.我经常在StackOverflow上看到缺乏实质的问题,不想在这里犯这个错误.
解决方法
DECLARE @ChosenLanguage INT = 48; SELECT sc.Id,Result = MAX(COALESCE( CASE WHEN lst.LanguageId = @ChosenLanguage THEN st.Text END,CASE WHEN lst.LanguageId = sk.DefaultLanguageId THEN st.Text END) ) FROM dbo.SupportCategories AS sc INNER JOIN dbo.StringKeys AS sk ON sc.StringKeyId = sk.Id LEFT OUTER JOIN dbo.LanguageStringTranslations AS lst ON sk.Id = lst.StringKeyId AND lst.LanguageId IN (sk.DefaultLanguageId,@ChosenLanguage) LEFT OUTER JOIN dbo.StringTranslations AS st ON st.Id = lst.StringTranslationId --WHERE sc.Id = 1 GROUP BY sc.Id ORDER BY sc.Id;
基本上,获取与所选语言匹配的潜在字符串并获取所有默认字符串,然后进行聚合,这样您只需为每个Id选择一个 – 优先选择所选语言,然后将默认值作为后备.
您可以使用UNION / EXCEPT执行类似的操作,但我怀疑这几乎总会导致针对相同对象的多次扫描.