Typ SQL_Variant w SQL Server

Ciekawym typem wbudowanym w SQL Server jest SQL_VARIANT. Pozwala on na przechowywanie wartości o różnym typie danych w ramach pojedynczej kolumny czy też zmiennej. W ramach niniejszego artykułu postaram się przybliżyć ten typ danych i wskazać zarówno jego wad jak i zalet.

Gdy zdefiniujemy zmienną typu SQL_Variant to może ona przechowywać np. wartości CHAR, DECIMAL i DATE jednocześnie przy zachowaniu właściwości specyficznych dla danego typu. Zobrazujmy to na przykładzie. Na samym początku stwórzmy sobie testową bazę danych na której będziemy wykonywać przykłady – w niniejszym artykule możemy użyć domyślnych wartości przy tworzeniu bazy danych, nie będą one aż tak istotne:

CREATE DATABASE SQLVariant
GO
USE SQLVariant
GO

Następnie stwórzmy tabelę, która będzie zawierała kolumnę typu SQL_VARIANT:

CREATE TABLE dbo.sqlvar
(
Variant_Column SQL_VARIANT
)
GO

Teraz możemy wstawić do nowo powstałej tabeli wartości różnych typów – kolejno: datetime, smallint, decimal, varchar, nvarchar:

INSERT INTO dbo.sqlvar
        ( 
		  Variant_Column 
		)
VALUES  ( 
		  GETDATE() 
        )
GO

INSERT INTO dbo.sqlvar
        ( 
		  Variant_Column 
		)
VALUES  ( 
		  23
        )
GO

INSERT INTO dbo.sqlvar
        ( 
		  Variant_Column 
		)
VALUES  ( 
		  23.12
        )
GO

INSERT INTO dbo.sqlvar
        ( 
		  Variant_Column 
		)
VALUES  ( 
		  'This is test text value'
        )
GO

INSERT INTO dbo.sqlvar
        ( 
		  Variant_Column 
		)
VALUES  ( 
		  N'This is test unicode text value'
        )
GO

Zapytanie zakończyło się sukcesem, mamy również pewność, że w tym momencie nie zaszła żadna niejawna konwersja ponieważ powyższe typy nie są pomiędzy sobą kompatybilne. Sprawdźmy używając funkcji DATALENGTH ile bajtów zajmują wartości które wstawiliśmy:

SELECT 
	Variant_Column,
	DATALENGTH(Variant_Column) AS Length
 FROM dbo.sqlvar

Jak widać każda z wartości odpowiada rozmiarem swojej bazowej jednostce, można ten fakt sprawdzić używając dokumentacji jednak Microsoft stworzył ku temu specjalną funkcję, która poda nam te informacje wprost – chodzi mianowicie o funkcję SQL_VARIANT_PROPERTY.  Funkcja ta pozwala nam odczytać kilka interesujących własności pola z typem SQL_VARIANT. Na pierwszy rzut sprawdźmy bazowe typy danych dla kolejnych wartości- służy ku temu parametr BASE_TYPE:

 SELECT 
	Variant_Column,
	DATALENGTH(Variant_Column) AS Length,
	SQL_VARIANT_PROPERTY(Variant_Column,'BaseType') AS Base_Type
 FROM dbo.sqlvar

Jak widać na poniższym zrzucie ekranowym typy danych zgadzają się z tym co chcieliśmy osiągnąć wstawiając kolejne wiersze:

Oprócz typu bazowego możemy użyć innych parametrów tejże funkcji tj.:

  • Precision – czyli liczba znaków dla typów numerycznych
  • Scale – czyli liczba znaków po przecinku dla typów numerycznych
  • TotalBytes – liczba bajtów potrzebna do zapisania konkretnej wartości oraz jej metadanych
  • Collation – kolacja konkretnej wartości
  • MaxLength – maksymalna długość typu danych

Jak można zauważyć opisywany typ danych daje nam bardzo wiele możliwości i jest niesamowicie elastyczny.  Jednakże z typem tym wiąże się bardzo wiele wad np. to, że dane w tym typie nie mogą być większe niż 8016 bajtów (dane +metadane) tak więc nie ma możliwości przechowywania tam długich typów jak np. varchar(max)., varbinary(max) itd

Największą wadą tego typu jest jednak to, że sterowniki ODBC i OLEDB nie w pełni wspierają ten typ. Oznacza to, że używając tego typu dane będą zwracane przez sterownik jako dane binarne.

Oczywiście możliwe jest wykonywanie funkcji języka TSQL natomiast warto zawsze wybierać tylko te wiersze, które są odpowiednie dla danej funkcji. Ponadto wiersze, które chcemy użyć jako parametr muszą zostać przekonwertowane na odpowiedni dla danej funkcji typ danych. Dla przykładu użyjemy funkcji SUM tylko dla typów

 SELECT
	SUM(CAST(Variant_Column AS INT)) AS Agg
 FROM
	dbo.sqlvar
 WHERE
	SQL_VARIANT_PROPERTY(Variant_Column,'BaseType')='int'

Jak widać używanie SQL_VARIANT jest stosunkowo proste, co prawda umożliwia zaprojektowanie bardzo ciekawego rozwiązania, lecz należy pamiętać aby go używać tylko w określonych przypadkach. Użycie tego typu wiąże się z dużym narzutem przez co wydajność całego rozwiązania może ucierpieć.

2 Comments

    • Ten typ jest niestety ogólnie rzecz biorąc niewspierany przez większość mechanizmów. Zrzuty już są widoczne:)

Leave a Reply