【问题标题】:What are the pitfalls of using sql_variant?使用 sql_variant 有哪些陷阱?
【发布时间】:2011-12-11 22:55:54
【问题描述】:

我已经多次阅读和听到应该避免使用sql_variant。我认为我有一个很好的用例。我过去曾使用varchar(max) 将不同类型存储在同一列中,但当有一个完全符合我要求的内置类型时,避免反序列化开销似乎是明智的。

那么,究竟使用sql_variant 的陷阱是什么?它们是与性能相关的,还是容易犯的编程错误,还是其他什么?顺便说一句,如果需要考虑的话,我将从客户端代码和 CLR 函数与本专栏进行交互。

【问题讨论】:

  • 为什么在同一列中存储不同的类型?这是EAV 结构吗?
  • 没有。我真的不想偏离用例的有效性,但我有一个可以应用于各种列的过滤器表。因此,可比较的值具有不同的类型。

标签: sql-server sql-server-2005 tsql sqlclr variant


【解决方案1】:

通过SQL_VARIANT 将不同类型存储在同一列中几乎与在.NET 中将所有内容转换为Object 相同。有时使用这种类型是有正当理由的,因为它当然可以允许更通用的编程结构。

但是,正如您所预料的那样,您应该注意使用 SQL_VARIANT 的一些陷阱,尤其是其中一个可能会破坏交易:

  1. 就像在 .NET 中将所有内容都强制转换为 Object(并且可能需要根据基本类型进行装箱/拆箱),使用 SQL_VARIANT 时会有一定的性能影响。根据用例,如果功能确实需要它和/或使用不是很频繁(即每秒多次),降低性能可能是可以接受的。

  2. 与在 .NET 中将所有内容强制转换为 Object 不同,SQL_VARIANT 数据类型对它可以包含的基本数据类型有限制。以下数据类型不能存储为SQL_VARIANT

    • VARCHAR(MAX)
    • NVARCHAR(MAX)
    • VARBINARY(MAX)
    • XML
    • TIMESTAMP / ROWVERSION
    • TEXT(从 SQL Server 2005 开始,您不应该使用这种类型)
    • NTEXT(从 SQL Server 2005 开始,您不应该使用这种类型)
    • IMAGE(从 SQL Server 2005 开始,您不应该使用这种类型)

    如果需要存储这些数据类型中的任何一个,此限制可以很容易地阻止使用SQL_VARIANT 的可能性。请注意,这里的问题是基本数据类型,而不是数据的大小,如以下测试所示:

    DECLARE @tmp1 TABLE (col1 SQL_VARIANT NOT NULL);
    INSERT INTO @tmp1 (col1) VALUES (CONVERT(VARCHAR(MAX), 'g'));
    

    返回:

    Msg 206, Level 16, State 2, Line 2
    Operand type clash: varchar(max) is incompatible with sql_variant
    

公平地说,使用 SQL_VARIANT 而不是将所有内容强制转换为 NVARCHAR 的一个好处是,SQL_VARIANT 保留了基础类型信息并强制使用它,这样您就不会在完全不适当的上下文中轻易滥用值。

DECLARE @tmp2 TABLE (col1 SQL_VARIANT NOT NULL);
INSERT INTO @tmp2 (col1) VALUES (1);

SELECT CONVERT(DATETIME, col1) FROM @tmp2;

SELECT CONVERT(TIME, col1) FROM @tmp2;

返回:

1900-01-02 00:00:00.000

Msg 529, Level 16, State 3, Line 6
Explicit conversion from data type int to time is not allowed.

关于不能将 SQL_VARIANT 用作 PK:这确实不是问题,因为泛型数据类型的本质几乎排除了它首先不适合这种用途。

关于无法将SQL_VARIANTLIKE 运算符一起使用:这主要不是问题,因为能够将其转换为可与LIKE 一起使用的适当类型,例如:

WHERE CONVERT(NVARCHAR(50), [sql_variant_field]) LIKE '%something%'

上述当然不是最有效的,但它是功能性的,如上所述,效率已经被排除在外,因为在决定使用 SQL_VARIANT 数据类型时牺牲了功能性。

【讨论】:

    【解决方案2】:

    这也使编程错误更容易发生。 DBA/程序员查看一列,它看起来像一个整数,所以他将一个整数放入其中,但在更远的地方,一个进程希望它是一个字符串。我在 sql_variant 列中看到了写得很糟糕的导入。

    【讨论】:

      【解决方案3】:

      想到的唯一明显缺陷是在您想要将超过其最大长度(8016 字节,每个网页:http://msdn.microsoft.com/en-us/library/ms173829.aspx)的值推入 sql_variant 字段的情况下。如果您的值从未接近该限制,那么 sql_variant 可能是一个非常好的方法。否则,您仍然可以使用 sql_variant,但提供一个单独的“isBlob”位字段,该字段指向带有您的 varbinary(max) 值的单独表(例如)。

      【讨论】:

        【解决方案4】:

        我见过性能问题和代码质量相关的问题:

        大多数情况下,您访问此字段时,您必须检查类型(使用 sql_variant_property)。这会使您的查询更加复杂,这可能会导致您列出的两个问题。

        您还必须在每次使用该字段时强制转换它,从而导致进一步的性能损失。

        此外,sql_variant 列不能是主键的一部分,它们不能作为计算列的一部分,也不能与 WHERE 子句中的 LIKE 一起使用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-05-03
          • 2015-07-07
          • 2012-06-05
          • 2021-03-13
          • 2016-08-19
          • 1970-01-01
          相关资源
          最近更新 更多