SQL Server - 在嵌套的非确定性视图堆栈中处理字符串的本地化

bee*_*eks 20 performance sql-server sql-server-2008-r2 view functions query-performance

在分析数据库时,我遇到了一个视图,该视图引用了一些非确定性函数,对于此应用程序池中的每个连接,这些函数每分钟被访问1000-2500 次。一个简单的视图产生以下执行计划:SELECT

在此处输入图片说明 在此处输入图片说明 在此处输入图片说明 在此处输入图片说明

对于少于一千行且每隔几个月可能会看到一两行更改的视图来说,这似乎是一个复杂的计划。但以下其他注意事项会变得更糟:

  1. 嵌套视图是不确定的,所以我们不能索引它们
  2. 每个视图引用多个 UDFs 来构建字符串
  3. 每个 UDF 包含嵌套 UDFs 以获取本地化语言的 ISO 代码
  4. 堆栈中的视图使用s返回的附加字符串构建器UDF作为JOIN谓词
  5. 每个视图堆被视为一个表,这意味着有INSERT/ UPDATE/DELETE在每个触发器来写入底层表
  6. 在视图上,这些触发器使用CURSORSEXEC存储过程作为参考更多的这些串建设UDF秒。

这对我来说似乎很糟糕,但我只有几年的 TSQL 经验。也越来越好!

看来开发人员认为这是一个好主意,这样做是为了让存储的几百个字符串可以根据从UDF特定于模式的a 返回的字符串进行翻译。

这是堆栈中的一个视图,但它们都同样糟糕:

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
    ,CASE
       WHEN ISNULL(il.I18NID, N'') = N''
       THEN id.IETFLangCode
       ELSE il.IETFLangCode
       END AS IETFLangCode
    ,dbo.User3StringI18N_KeyValue(b.WKType, b.WKIndex, 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, b.WKIndex, N'WKS')
AND il.IETFLangCode = dbo.UserI18N_Session_Locale_Key()
)
LEFT OUTER JOIN User3StringI18N id
ON    (
id.I18NID       = dbo.User3StringI18N_KeyValue(b.WKType, b.WKIndex,N'WKS')
AND id.IETFLangCode = dbo.UserI18N_Database_Locale_Key()
)
GO
Run Code Online (Sandbox Code Playgroud)

这就是为什么UDFs 被用作JOIN谓词的原因。该I18NID列通过连接形成:STRING + [ + ID + | + ID + ]

在测试这些时,SELECT视图中的一个简单返回 ~309 行,执行时间为 900-1400 毫秒。如果我将字符串转储到另一个表中并在其上建立索引,则相同的选择会在 20-75 毫秒内返回。

所以,长话短说(我希望你能理解其中的一些愚蠢)我想成为一个好心人,为 99% 的运行这个产品的客户重新设计和重写这个,他们根本使用任何本地化 - -[en-US]即使英语是第二/第三语言,最终用户也应该使用语言环境。

由于这是一个非官方的黑客攻击,我在考虑以下几点:

  1. 创建一个新的 String 表,其中填充了来自原始基表的一组干净连接的数据
  2. 索引表。
  3. 在堆栈中创建一组替换顶级视图,其中包括NVARCHARINT列的WKTypeWKIndex列。
  4. 修改一些UDF引用这些视图的s 以避免在某些连接谓词中进行类型转换(我们最大的审计表是 500-2,000M 行,并将 an 存储INTNVARCHAR(4000)用于连接WKIndex列 ( INT)的列中。)
  5. 架构绑定视图
  6. 向视图添加一些索引
  7. 使用设置逻辑而不是游标重建视图上的触发器

现在,我的实际问题:

  1. 是否有通过视图处理本地化字符串的最佳实践方法?
  2. 使用 aUDF作为存根有哪些替代方案?(我可以VIEW为每个架构所有者编写一个特定的代码,并对语言进行硬编码,而不是依赖于各种UDF存根。)
  3. 是否可以通过完全限定嵌套的UDFs 然后模式绑定视图堆栈来简单地使这些视图具有确定性?

Mar*_*miK 1

看看给定的代码,我们可以说,

  • 首先,这不应该是视图,而应该是存储过程,因为它不仅仅是从表中读取,而且还使用 UDF。
  • 其次,不应为同一列频繁调用 UDF。这里,它在 select 中被调用一次

    ,dbo.User3StringI18N_KeyValue(b.WKType, b.WKIndex, N'WKS') AS I18NID 
    
    Run Code Online (Sandbox Code Playgroud)

    以及第二次加入

    .IETFLangCode = dbo.User3StringI18N_KeyValue(b.WKType, b.WKIndex, N'WKS')
    
    Run Code Online (Sandbox Code Playgroud)

可以在临时表中生成值,或者使用 CTE(通用表表达式)在连接发生之前首先获取这些值。

我生成了一个示例 USP,它将提供一些改进:

CREATE PROCEDURE usp_UserWKStringI18N
AS
BEGIN
    -- Do operation using UDF 
    SELECT b.WKType
        ,b.WKIndex
        ,dbo.User3StringI18N_KeyValue(b.WKType, b.WKIndex, N'WKS') AS I18NID
        ,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
        ,b.WKIndex
        ,CASE 
            WHEN ISNULL(il.I18NID, N'') = N''
                THEN id.I18NString
            ELSE il.I18nString
            END AS WKString
        ,CASE 
            WHEN ISNULL(il.I18NID, 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
Run Code Online (Sandbox Code Playgroud)

请尝试这个