【问题标题】:Streamlining T-SQL user function for persisted computed column为持久化计算列简化 T-SQL 用户函数
【发布时间】:2020-10-01 20:50:23
【问题描述】:

我正在尝试简化以下 T-SQL 用户函数,以便在我的一个表的持久计算列中使用。我知道我可以在视图中使用计算列以及其他方法,但由于用户要求的各种原因,最好这样实现此列。

但是,为了在计算机专栏中使用此标量函数,我正在尝试使其尽可能高效和流线型。该函数的某些部分带有以下SELECT COUNT 语句

SELECT COUNT([JunctionID])
FROM dbo.ApplicationJunctionT
WHERE [AccountID] = @AccountID 
  AND [Outcome] IS NULL 
  AND [Change Account Status] IS NULL

我反复使用,我想知道是否有更好的方法来重组此功能以提高效率。我确实尝试使用 CTE 来包装这个 select 语句,但不断遇到语法问题,因此放弃了这个想法。

ALTER FUNCTION [dbo].[fnAccountReadyByID_Test] 
    (@JunctID INT = Null,
     @AccountID VARCHAR(20) = NULL)
RETURNS VARCHAR(10)
AS
BEGIN
    DECLARE @FamilyAccountReady as varchar(10),
            @TotalFamily as INT;

    SET @TotalFamily = (SELECT COUNT([JunctionID])
                        FROM dbo.ApplicationJunctionT
                        WHERE [AccountID] = @AccountID 
                          AND [Outcome] IS NULL 
                          AND [Change Account Status] IS NULL)

    IF @TotalFamily = 1
        SET @FamilyAccountReady = (SELECT AccountStatus 
                                   FROM dbo.ApplicationJunctionT 
                                   WHERE JunctionID = @JunctID)
    ELSE
        SET @FamilyAccountReady = (CASE 
                                      WHEN (SELECT COUNT([JunctionID]) 
                                            FROM dbo.ApplicationJunctionT 
                                            WHERE [AccountID] = @AccountID 
                                              AND [Outcome] IS NULL 
                                              AND [Change Account Status] IS NULL 
                                              AND [AccountStatus] = '-') > 0 
                                         THEN '-'
                                      WHEN (SELECT COUNT([JunctionID]) 
                                            FROM dbo.ApplicationJunctionT 
                                            WHERE [AccountID] = @AccountID 
                                              AND [Outcome] IS NULL 
                                              AND [Change Account Status] IS NULL 
                                              AND [AccountStatus] = 'Frozen') > 0 
                                         THEN 'Frozen'
                                      WHEN (SELECT COUNT([JunctionID]) 
                                            FROM dbo.ApplicationJunctionT 
                                            WHERE [AccountID] = @AccountID 
                                              AND [Outcome] IS NULL 
                                              AND [Change Account Status] IS NULL 
                                              AND [AccountStatus] = 'Closed') > 0 
                                         THEN 'Closed'
                                      WHEN (SELECT COUNT([JunctionID]) 
                                            FROM dbo.ApplicationJunctionT 
                                            WHERE [AccountID] = @AccountID 
                                              AND [Outcome] IS NULL 
                                              AND [Change Account Status] IS NULL 
                                              AND [AccountStatus] = 'Tentative') > 0 
                                         THEN 'Tentative'
                                      ELSE 'GRANT'
                               END)

       -- Return the result of the function
       RETURN UPPER(@FamilyAccountReady)
END

此外,即使在网上阅读了多篇文章后,我也无法找到计算机栏的触发方式。更新记录中的任何字段时是否触发?还是仅在变量字段更新时触发?

在这种情况下,我使用AccountIDJunctionID 作为变量,它们都是不变的静态字段。我的计算列在这种情况下是否仍然有效?

尽管我不喜欢效率低下,但我可能不得不采用这种设计,除非这里的专家社区可以提出一些改进建议!

非常感谢您的帮助!

【问题讨论】:

  • 我认为这种方法行不通,因为您的函数是不确定的。即使使用此代码,您能否完成整个设置?
  • 嗨,罗杰,还没有测试它是否有效。目前我在视图内的触发器中使用它来更新指定列,但我想删除该列并将其作为持久计算列。
  • 旁白:如果不出意外,您可以用when exists ( select 42 ... ) 替换重复的when ( select count ... ) > 0 逻辑。无需精确计数即可与零进行比较。

标签: sql-server performance tsql user-defined-functions calculated-columns


【解决方案1】:

如果您想使用 CTE,请尝试以下方法:

WITH CTE AS (
    SELECT [AccountStatus]
    FROM dbo.ApplicationJunctionT 
    WHERE [AccountID] = @AccountID 
        AND [Outcome] IS NULL 
        AND [Change Account Status] IS NULL 
) 
SELECT @FamilyAccountReady = CASE 
    WHEN EXISTS (SELECT * FROM CTE WHERE [AccountStatus] = '-') THEN '-'
    WHEN EXISTS (SELECT * FROM CTE WHERE [AccountStatus] = 'Frozen') THEN 'Frozen'
    WHEN EXISTS (SELECT * FROM CTE WHERE [AccountStatus] = 'Closed') THEN 'Closed'
    WHEN EXISTS (SELECT * FROM CTE WHERE [AccountStatus] = 'Tentative') THEN 'Tentative'
    ELSE 'GRANT'
END

但是,我认为使用基于访问其他表的函数的计算列不是一个好主意:您将收到错误提示 "Computed column 'ColumnName' in table 'TableName'无法持久化,因为该列是不确定的。”

【讨论】:

  • 早上好 Razvan,感谢您与 Roger 和 HABO 一起提出的建议,我正在探索这个选项,因为在触发器上计算这个似乎需要从用户到服务器来回发送太多数据。我可能需要重写该函数以使其更精简,但您对使用 CTE 的建议绝对是非常有帮助的。非常感谢你们的意见!
  • 如果您使用触发器,则数据不会从用户来回发送到服务器(触发器由服务器执行)。
猜你喜欢
  • 2010-10-29
  • 2010-12-16
  • 1970-01-01
  • 2011-08-25
  • 1970-01-01
  • 2012-12-16
  • 1970-01-01
  • 2010-09-18
相关资源
最近更新 更多