在 SQL 中定义命名常量的最佳方法是什么?

Wil*_*Cau 5 sql sql-server-2008

如果数据库中的多个存储过程和函数需要一个“常量”值,是否有一种标准方法可以在一个地方定义它,以便在任何地方都可用?

例如,假设我使用xp_logeventinCATCH块在发生时将某些内容写入事件日志RAISERROR,但我想根据严重性将严重性分为信息性、警告性和错误性RAISERROR

我可以设置一个常量EventSeverity,使得:

  • 如果RAISERROR严重性 = 0,则xp_logevent是信息性的。
  • 如果RAISERROR严重性 <= EventSeveritythanxp_logevent是警告。
  • 如果RAISERROR严重性 > EventSeveritythanxp_logevent是错误。

警告和错误严重性之间的界限不太可能改变,但如果改变,我只想在一个地方改变它。

我想到了这些可能性:

  • 使用“ @@variable ”来存储值。

    • 优点:访问开销低。易于在代码中访问。
    • 缺点:强制执行顺序,变量必须在其他过程和函数访问之前声明和设置。更改值意味着更改代码。

      DECLARE @@EventSeverity INT = 9
      ...
      BEGIN CATCH
          IF ERROR_SEVERITY() < @@EventSeverity
              ...
          ELSE
              ...
      END CATCH
      
      Run Code Online (Sandbox Code Playgroud)
  • 使用函数返回值。

    • 优点: 相当低的访问开销。易于在代码中访问。
    • 缺点:改变值意味着改变代码。

      CREATE FUNCTION dbo.EventSeverity()
      RETURNS INT
      AS
      BEGIN
          RETURN 9
      END
      ...
      BEGIN CATCH
          IF ERROR_SEVERITY() < dbo.EventSeverity()
              ...
          ELSE
              ...
      END CATCH
      
      Run Code Online (Sandbox Code Playgroud)
  • 使用“设置”表来存储值。

    • 优点:改变价值意味着改变数据。
    • 缺点:访问开销高。难以在代码中访问。难以用作参数。用户可以更改值。

      CREATE TABLE dbo.Settings
      (
          Name VARCHAR(...),
          Value VARCHAR(...)
      )
      ...
      INSERT INTO dbo.Settings (Name, Value)
      VALUES ('EventSeverity', CAST(9 AS VARCHAR))
      ...
      BEGIN CATCH
          IF ERROR_SEVERITY() < (SELECT CAST(Value AS INT) FROM dbo.Settings WHERE Name = 'EventSeverity')
              ...
          ELSE
              ...
      END CATCH
      
      Run Code Online (Sandbox Code Playgroud)
  • 使用带有功能的“设置”表来简化访问。

    • 优点:易于更改值。易于在代码中访问。
    • 缺点:开销大。用户可以更改值。

      CREATE TABLE dbo.Settings
      (
          Name VARCHAR(...),
          Value VARCHAR(...)
      )
      ...
      INSERT INTO dbo.Settings (Name, Value)
      VALUES ('EventSeverity', CAST(9 AS VARCHAR))
      ...
      CREATE FUNCTION dbo.EventSeverity()
      RETURNS INT
      AS
      BEGIN
          DECLARE @result INT
          SET @result = (SELECT CAST(Value AS INT) FROM dbo.Settings WHERE Name = 'EventSeverity')
          IF @result IS NULL
              SET @result = 9
          RETURN @result
      END
      ...
      BEGIN CATCH
          IF ERROR_SEVERITY() < dbo.EventSeverity()
              ...
          ELSE
              ...
      END CATCH
      
      Run Code Online (Sandbox Code Playgroud)

有没有最佳实践方法来做到这一点?

Lyn*_*ler 3

在其他条件相同的情况下,为了性能,我会选择硬编码的 FUNCTION。为了安全起见,这个函数应该放在一个不同的 SCHEMA 中,比如 MyDatabase.CONF.SettingsFunc 而不是通常的 DBO;将此模式设置为权限,因此只有管理员有权更改数据。

如果您需要为许多不同的用途集中各种配置设置,那么最后一种方法(函数+表)将具有更大的吸引力,只要您为每个用例创建一个索引。同样,这个“设置”表应该处于受限模式中,与函数不同,函数可以保留在默认模式中以便于编码。

但是,如果必须使用默认模式,那么在此“设置”表中配置“INSTEAD OF UPDATE”触发器就变得很有趣,这样用户就不会轻易更改数据;不要忘记后一种方法不能称为“安全”,因为用户仍然可以更改(或删除!)触发器。