这似乎是一个复杂的计划,一个不到一千行的视图,每隔几个月就会看到一行或两行.但随着以下其他纪念活动,情况会变得更糟:
>嵌套视图是非确定性的,因此我们无法对它们编制索引
>每个视图引用多个UDF来构建字符串
>每个UDF都包含嵌套的UDF,以获取本地化语言的ISO代码
>堆栈中的视图使用从UDF返回的其他字符串构建器作为JOIN谓词
>每个视图堆栈都被视为一个表,这意味着每个视图堆栈都有INSERT / UPDATE / DELETE触发器写入基础表
>视图上的这些触发器使用CURSORS EXEC存储过程,这些过程引用了更多这些字符串构建UDF.
这对我来说似乎很糟糕,但我只有几年的Tsql经验.它也变得更好!
看起来开发人员认为这是一个好主意,做了所有这些,以便存储的几百个字符串可以基于从特定于模式的UDF返回的字符串进行转换.
这是堆栈中的一个视图,但它们都同样糟糕:
CREATE VIEW [UserWKStringI18N] AS SELECT b.WKType,b.WKIndex,CASE WHEN ISNULL(il.I18NID,N'') = N'' THEN id.I18NString ELSE il.I18nString END AS WKString,N'') = N'' THEN id.IETFLangCode ELSE il.IETFLangCode END AS IETFLangCode,dbo.User3StringI18N_KeyValue(b.WKType,N'WKS') AS I18NID,dbo.UserI18N_Session_Locale_Key() AS IETFSessionLangCode,dbo.UserI18N_Database_Locale_Key() AS IETFDatabaseLangCode FROM UserWKStringBASE b LEFT OUTER JOIN User3StringI18N il ON ( il.I18NID = dbo.User3StringI18N_KeyValue(b.WKType,N'WKS') AND il.IETFLangCode = dbo.UserI18N_Session_Locale_Key() ) LEFT OUTER JOIN User3StringI18N id ON ( id.I18NID = dbo.User3StringI18N_KeyValue(b.WKType,N'WKS') AND id.IETFLangCode = dbo.UserI18N_Database_Locale_Key() ) GO
这就是为什么UDF被用作JOIN谓词的原因. I18NID列通过连接:STRING [ID | ID ]
在测试这些过程中,视图中的一个简单的SELECT返回~309行,并执行900-1400ms.如果我将字符串转储到另一个表中并在其上打一个索引,则相同的select将在20-75ms内返回.
所以,长话短说(我希望你对这种愚蠢行为表示赞赏)我希望成为一名优秀的撒玛利亚人,并为99%运行该产品的客户重新设计并重新编写此内容,而这些客户并未使用任何本地化 – 即使英语是第二/第三语言,即使用户也应使用[en-US]语言环境.
由于这是一个非正式的黑客攻击,我想到的是:
>创建一个新的String表,其中填充了原始基表中干净连接的数据集
>索引表格.
>在堆栈中创建一组替换的顶级视图,其中包括WKType和WKIndex列的NVARCHAR和INT列.
>修改引用这些视图的少数UDF,以避免某些连接谓词中的类型转换(我们最大的审计表是500-2,000M行,并在NVARCHAR(4000)列中存储INT,用于连接WKIndex列( INT).)
> Schemabind的观点
>向视图添加一些索引
>使用set logic而不是游标在视图上重建触发器
现在,我的实际问题:
>是否有通过视图处理本地化字符串的最佳实践方法?
>使用UDF作为存根有哪些替代方案? (我可以为每个架构所有者编写一个特定的VIEW并对该语言进行硬编码,而不是依赖于各种UDF存根.)
>通过完全限定嵌套UDF然后对视图堆栈进行模式绑定,可以简单地使这些视图成为确定性的吗?
解决方法
>首先,这不应该是一个视图,但它应该是一个存储过程,因为它不仅仅是从表中读取,而是使用UDF.
>其次,不应经常为同一列调用UDF.在这里,它在select中被调用一次
,N'WKS') AS I18NID
第二次加入
.IETFLangCode = dbo.User3StringI18N_KeyValue(b.WKType,N'WKS')
可以在临时表中生成值或使用CTE(公用表表达式)在连接发生之前首先获取这些值.
我已经生成了一个样本USP,它将提供一些改进:
CREATE PROCEDURE usp_UserWKStringI18N AS BEGIN -- Do operation using UDF SELECT b.WKType,dbo.UserI18N_Session_Locale_Key() AS IETFSessionLangCode,dbo.UserI18N_Database_Locale_Key() AS IETFDatabaseLangCode INTO #tempTable FROM UserWKStringBASE b; -- Now final Select SELECT b.WKType,CASE WHEN ISNULL(il.I18NID,N'') = N'' THEN id.I18NString ELSE il.I18nString END AS WKString,N'') = N'' THEN id.IETFLangCode ELSE il.IETFLangCode END AS IETFLangCode,b.I18NID,b.IETFSessionLangCode,b.IETFDatabaseLangCode FROM #tempTable b LEFT OUTER JOIN User3StringI18N il ON il.I18NID = b.I18NID AND il.IETFLangCode = b.IETFSessionLangCode LEFT OUTER JOIN User3StringI18N id ON id.I18NID = b.I18NID AND id.IETFLangCode = b.IETFDatabaseLangCode END
请试试这个