使用表值函数尝试数据库本地化

时间:2009-11-07 16:55:10

标签: sql sql-server sql-server-2005 localization

我正在寻找以下本地化技术的意见:

我们从2个表开始:

tblProducts : ProductID, Name,Description,SomeAttribute
tblProductsLocalization : ProductID,Language,Name,Description

和表值函数:

CREATE FUNCTION [dbo].[LocalizedProducts](@locale nvarchar(50))
RETURNS TABLE
AS (SELECT a.ProductID,COALESCE(b.Name,a.Name)as [Name],COALESCE(b.Description,a.Description)as [Description],a.SomeAttribute
from tblProducts a 
left outer join tblProductsLocalization_Locale b 
on a.ProductID= b.ProductID and b.[Language]=@locale)

我打算做的是每当我需要返回本地化数据时都包含该函数:

select * from LocalizedProducts('en-US') where ID=1 

而不是

select * from tblProducts  where ID=1 

如果这个或任何showstoppers存在重大的性能问题,我很感兴趣。我不应该采用这个理由吗?

编辑:我已经标记了这个SQL2005,尽管我使用2008开发它,我认为部署目标只有SQL2005。如果有需要,我可以升级到2008年。

稍后编辑:

我创建了一个视图,内容相同但没有参数:

CREATE VIEW [dbo].[LocalizedProductsView]
AS
SELECT b.Language,a.ProductID,COALESCE(b.Name,a.Name)as [Name],
COALESCE(b.Description,a.Description)as [Description],a.SomeAttributefrom tblProducts a 
left outer join tblProductsLocalization_Locale b on a.ProductID= b.ProductID 

然后我继续进行一些测试: 估计的执行计划看起来与两个查询完全相同:

select * from LocalizedProducts('us-US') where SomeNonIndexedParameter=2

select * from LocalizedProductsView where (Language='us-US' or Language is null) and SomeNonIndexedPramaters=2

最终提出的问题:我是否应该了解TVF正在计算所有产品的翻译,无论WHERE参数如何?是在做同样的事情吗?

3 个答案:

答案 0 :(得分:1)

简短回答:作为一般规则,使用TVF进行此类操作没有任何问题,但我建议将ID作为参数:

CREATE FUNCTION [dbo].[LocalizedProducts](@ID int, @locale nvarchar(50))
RETURNS TABLE
AS (SELECT a.ProductID,COALESCE(b.Name,a.Name)as [Name],COALESCE(b.Description,a.Description)as [Description],a.SomeAttribute
from tblProducts a 
left outer join tblProductsLocalization _Locale b 
on a.ProductID= b.ProductID and b.[Language]=@locale)
where a.ProductId = @ID

像这样使用:

select * from LocalizedProducts(1, 'en-US')

更长的解释: 我在SQL 2008中从未尝试过类似的东西,所以SQL Server可能会优化这个问题。

但是,我在早期版本中的经验似乎表明SQL Server倾向于处理 用户定义的函数采用更多的过程而非声明的方式,因此它不会解释您想要的内容,然后找出最适合您的方式,但实际上是按照您编写的指令执行的。所以在我看来,这种方法会:

  1. 选择所有英文文本,将其放入表格变量中。
  2. 获取步骤#1的结果并选择具有给定ID的任何记录。
  3. 这将意味着大量浪费的周期,在将ID过滤器应用于该结果集之前,将大部分未使用的英文文本放入表变量中。另一方面,将所有过滤器放入UDF将让SQL Server确定是否最容易按ID过滤(更可能是假设采用标准索引方案),然后应用区域设置过滤器,反之亦然。无论哪种方式,如果您将所有过滤器放在一个位置,那么您应该在后台移动较少的数据,从而获得更好的性能。同样,这一切都假设SQL Server现在没有在优化方面实现巨大飞跃。但如果是这样,那就更有理由说,是的,使用TVF没有问题。

答案 1 :(得分:0)

可以肯定的是,您必须翻译的不仅仅是产品名称。所以我设计了翻译解决方案来处理任何类型的字符串。

例如,您可以使用如下的本地化表:

Id, TranslatableStringId, Language, Translation

然后每个产品都可以有一个可翻译的字符串。但也是产品清单顶部的解释性文字。

对于产品,您可以查询:

SELECT     *
FROM       Products p
INNER JOIN Translations t 
ON         p.DescriptionId = t.TranslatableStringId
AND        t.language = 'en-US'

对于解释性文字,你会得到一个简单的:

SELECT     t.Translation
FROM       Translations t 
WHERE      t.TranslatableStringId = 123 -- ID of string
AND        t.language = 'en-US'

P.S。对于一个真正的程序,我会使用比TranslatableStringId更简短的描述,比如tsid,因为翻译往往会随处可见。

答案 2 :(得分:0)

我想在做了更多测试之后再回答这个问题。 在我看来,在执行查询计划并相应地进行优化时,SQL2008实际上正在查看TVF:

例如:

select pr.* from LocalizedProducts('en-US') pr inner join LocalizedPhotos('en-US') ph on 
ph.ProductId=pr.Id where pr.SomeUnindexProperty= 5

此查询需要触及4个表:

Products
Products_Localization
Photos
Photos_Localization

查询计划的外观是(让我看看我是否可以格式化):

Product gets a Clustered Index Seek 
        -- >>  Products gets nested loop with Photos 
                              -->> nested loop Products_Localization -
                                          ->> nested loop Photos_Localization. 

如果TVF是黑匣子,那么这不是你所期望的。 Product得到索引的简单事实SEEK会告诉我该查询不会盲目地解释整个TVF。

我进行了大量的性能测试,平均而言,“本地化”TVF比使用直接表查询慢50%-100%,但这可能是因为TVF中涉及的表多于两倍。在正常的查询中。