使用sql_variant有什么陷阱?

时间:2011-10-20 16:33:28

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

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

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

4 个答案:

答案 0 :(得分:4)

通过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
    
  3. 公平地说,使用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:这实际上不是问题,因为通用数据类型的本质几乎排除了它首先被用于此类用途。< / p>

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

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

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

答案 1 :(得分:2)

它还使编程错误更容易发生。 DBA / Programmer查看一个列,它看起来像一个整数,所以他在其中放入一个整数,但是在该行的另一端,进程希望它是一个字符串。我已经通过写入sql_variant列的写得不好看到了这一点。

答案 2 :(得分:1)

我已经看到了性能问题和与代码质量相关的问题:

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

每次使用时,您还必须投射此字段,从而进一步降低性能。

此外,sql_variant列不能作为主键的一部分,它们不能作为计算列的一部分工作,并且它们不能在WHERE子句中使用LIKE。

答案 3 :(得分:0)

我想到的唯一明显的陷阱是,您希望将值推送到超出其最大长度的sql_variant字段(8016字节,每个网页:http://msdn.microsoft.com/en-us/library/ms173829.aspx)。如果你的值永远不会接近这个限制,那么sql_variant可能是一个非常好的方法。否则,您仍然可以使用sql_variant,但提供单独的“isBlob”位字段,该字段指向具有varbinary(max)值的单独表(例如)。