所以这是我的情况:
我正在为我的一个项目进行本地化,通常我会在C#代码中进行此操作,但是我想在SQL中进行更多操作,因为我想稍微弄点点SQL。
环境:SQL Server 2014 Standard,C#(.NET 4.5.1)
注意:编程语言本身应该无关紧要,为了完整起见,我仅将其包括在内。
所以我完成了我想要的,但是没有达到我想要的程度。自从我完成了JOIN
除基本SQL 之外的任何SQL以来,已经有一段时间了(至少一年),这相当复杂JOIN
。
这是数据库相关表的示意图。(还有更多,但是这部分不是必需的。)
映像中描述的所有关系都已在数据库中完成- PK
和FK
约束都已设置和运行。所描述的列均不null
可用。所有表都具有架构dbo
。
现在,我有一个查询这几乎做什么,我想:即给定任何的标识SupportCategories
和任何的标识Languages
,这将返回:
如果有合适的,正确的翻译是语言该字符串(即StringKeyId
- > StringKeys.Id
存在,并在LanguageStringTranslations
StringKeyId
,LanguageId
以及StringTranslationId
是否同时存在,那么它的负载StringTranslations.Text
为StringTranslationId
。
如果LanguageStringTranslations
StringKeyId
,LanguageId
和StringTranslationId
组合没有不存在,那么它加载的StringKeys.Name
值。该Languages.Id
是给定的integer
。
我的查询很混乱,如下所示:
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 English
还是当前的语言48 French
,或目前使用的任何其他语言):
Id Result
0 Billing
1 API
2 Sales
附加示例
假设我要为French
(即添加1 48 8
到LanguageStringTranslations
)添加一个本地化,输出将更改为(注意:这仅是示例,显然我将向添加一个本地化的字符串StringTranslations
)(以法语示例更新):
Result
Les APIs
所需的附加输出
给定上面的示例,将需要以下输出(以法语示例更新):
Id Result
0 Billing
1 Les APIs
2 Sales
(是的,从一致性的角度来看,我从技术上知道这是错误的,但这是在这种情况下需要的。)
编辑:
小更新后,我确实更改了dbo.Languages
表的结构,并Id (int)
从表中删除了列,并替换为Abbreviation
(现在已重命名为Id
,并且更新了所有相关的外键和关系)。从技术的角度来看,我认为这是一个更合适的设置,因为该表仅限于ISO 639-1代码,而该代码最初是唯一的。
l
所以说:这个问题,我怎么能修改此查询返回的一切从SupportCategories
再或者返回StringTranslations.Text
为StringKeys.Id
,Languages.Id
组合,或将StringKeys.Name
如果它没有不存在吗?
我最初的想法是,我可以以某种方式将当前查询转换为另一个临时类型,作为另一个子查询,然后将此查询包装在另一个SELECT
语句中,然后选择我想要的两个字段(SupportCategories.Id
和Result
)。
如果我什么都没找到,我将执行我通常使用的标准方法,将所有方法都加载SupportCategories
到我的C#项目中,然后使用它对每个手动运行上面的查询SupportCategories.Id
。
感谢您提出的所有建议/评论/批评。
另外,我为它太长了而感到抱歉,我只是不想有任何歧义。我经常在StackOverflow上,看到缺乏实质性的问题,不想在这里犯错误。