使用sql_variant有什么陷阱?

Dan*_*iel 10 t-sql sql-server sql-server-2005 sqlclr variant

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

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

Sol*_*zky 5

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

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

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

  2. Object在 .NET 中强制转换所有内容不同,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'));
    
    Run Code Online (Sandbox Code Playgroud)

    返回:

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

公平地说,使用SQL_VARIANT过度强制转换所有内容的一个好处NVARCHARSQL_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;
Run Code Online (Sandbox Code Playgroud)

返回:

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.
Run Code Online (Sandbox Code Playgroud)

关于不能SQL_VARIANT用作 PK:这实际上不是问题,因为通用数据类型的本质几乎排除了它首先用于这种用途的可取性。

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

WHERE CONVERT(NVARCHAR(50), [sql_variant_field]) LIKE '%something%'
Run Code Online (Sandbox Code Playgroud)

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


Gar*_*ett 1

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