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


20

在对数据库进行概要分析时,我遇到了一个视图,该视图引用了一些不确定性函数,对于该应用程序池中的每个连接,每分钟可访问1000-2500次。从视图中看,一个简单的代码将产生以下执行计划:SELECT

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

对于少于一千行的视图来说,这似乎是一个复杂的计划,每隔几个月可能会看到一两行发生变化。但是随着以下其他情况的恶化,情况变得更糟:

  1. 嵌套视图是不确定的,因此我们无法为其编制索引
  2. 每个视图都引用多个UDFs来构建字符串
  3. 每个UDF都包含嵌套UDF的,以获取本地化语言的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

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

在对它们进行测试期间,SELECT从视图中返回一个简单的行将返回〜309行,并花费900-1400ms的时间执行。如果我将字符串转储到另一个表中并在其上打索引,则相同的选择将在20-75ms内返回。

因此,长话短说(我希望您对这种愚蠢的行为表示赞赏),我想成为一名优秀的撒玛利亚人,并为99%运行该产品的客户完全使用任何本地化功能进行重新设计和重写, - [en-US]即使英语是第二/第三语言,最终用户也应使用语言环境。

由于这是非官方的骇客,因此我想到了以下几点:

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

现在,我的实际问题是:

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

5
尝试将标量UDF转换为值UDF的内联表。也发布您的UDF定义。另外,请参考T-SQL用户定义的函数:好的,坏的和丑陋的
Kin Shah


Answers:


1

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

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

    ,dbo.User3StringI18N_KeyValue(b.WKType, b.WKIndex, N'WKS') AS I18NID 

    和第二次加入

    .IETFLangCode = dbo.User3StringI18N_KeyValue(b.WKType, b.WKIndex, N'WKS')

可以在临时表中生成值,也可以使用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

请尝试这个


您好MarmiK,感谢您抽出宝贵时间阅读这篇文章。不幸的是,这是一个视图(在一系列嵌套视图中),因此将其移至存储过程是不可能的。
beeks

在那种情况下,我们可以在视图中使用CTE,因为在View中不建议使用临时表。或临时表的行可以由某些存储过程生成,并可以在视图中调用。
MarmiK
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.