Хранение различных типов в одном и том же столбце с помощью SQL_VARIANT
- это почти то же самое, что и приведение всего к Object
в .NET. И иногда есть веские причины для использования этого типа, поскольку он, безусловно, может предусматривать более общую программную структуру.
Однако, как вы и ожидали, при использовании SQL_VARIANT
есть некоторые подводные камни, о которых вам следует знать, тем более что одна из них может нарушить условия сделки:
Точно так же, как приведение всего к Object
в .NET (и, возможно, требующий упаковки / распаковки в зависимости от базового типа), при использовании SQL_VARIANT
наблюдается определенное снижение производительности. В зависимости от варианта использования может быть приемлемым снижение производительности, если функциональность действительно в этом нуждается и / или использование не очень частое (т.е. много раз в секунду).
В отличие от преобразования данных в 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'));
Возвращает:
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
.