我已经多次读到和听到应该避免使用sql_variant
。我认为我有一个很好的用例。我过去使用varchar(max)
在同一列中存储不同的类型,但是当有一个内置类型可以完全满足我的需求时,避免反/序列化开销似乎是明智的。
那么,使用sql_variant
的陷阱到底是什么呢?它们是否与性能有关,或容易犯编程错误,或其他什么?顺便提一下,如果需要考虑的话,我将通过客户机代码和CLR函数与本专栏进行交互。
通过SQL_VARIANT
在同一列中存储不同类型几乎与在。net中将所有内容转换为Object
相同。有时使用这种类型有正当的理由,因为它当然可以允许更通用的编程结构。
然而,正如您所预料的,使用SQL_VARIANT
有一些陷阱,您应该意识到,特别是其中一个可能是一个交易破坏者:
-
就像在。net中将所有内容强制转换为
Object
一样(可能需要根据基本类型进行装箱/拆箱),使用SQL_VARIANT
时肯定会对性能造成影响。根据用例,如果功能确实需要它和/或使用不是很频繁(即每秒多次),那么降低性能可能是可以接受的。 -
不像在。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_VARIANT
与LIKE
运算符一起使用:由于能够将其转换为与LIKE
一起工作的适当类型,这基本上是一个没有问题的,如:
WHERE CONVERT(NVARCHAR(50), [sql_variant_field]) LIKE '%something%'
上面当然不是最有效的,但它是功能性的,正如上面提到的,当决定使用SQL_VARIANT
数据类型时,效率已经被排除在外,因为它是为了功能而牺牲的。
这也使编程错误更容易发生。DBA/程序员看到一个列,它看起来像一个整数,所以他把一个整数放进去,但在这行后面,一个进程希望它是一个字符串。我在sql_variant列的导入中看到过这种情况。
想到的唯一明显的陷阱是在您想要将值推入sql_variant字段的情况下,该值超过了其最大长度(8016字节,根据此网页:http://msdn.microsoft.com/en-us/library/ms173829.aspx)。如果您的值从未接近该限制,那么sql_variant可能是一个非常好的方法。否则,您仍然可以使用sql_variant,但是提供一个单独的"isBlob"位字段,该字段指向一个带有varbinary(max)值的单独表(例如)。
我看到了性能问题和代码质量相关的问题:
在访问这个字段的大多数时候,您都必须检查类型(使用sql_variant_property)。这会使您的查询更加复杂,从而可能导致您列出的两个问题。
您还必须在每次使用该字段时强制转换该字段,从而导致进一步的性能损失。
此外,sql_variant列不能作为主键的一部分,它们不能作为计算列的一部分,也不能在WHERE子句中使用LIKE。