SQL CLR
Autor: Wojciech Kalbarczyk
Opublikowano: 2011-02-25
SQL Server 2008, podobnie jak poprzednia wersja serwera z 2005 roku, udostępnia środowisko Common Language Runtime (CLR). Oznacza to wiele nowych możliwości dla programistów, którzy mogą tworzyć obiekty baz danych przy użyciu platformy .NET, korzystając z języków C# i Visual Basic. Korzystanie z CLR znacznie też rozszerza możliwości programistyczne w porównaniu z używaniem samego T-SQL. W artykule tym postaram się pokazać, jak korzystać z CLR, jak tworzyć obiekty bazodanowe oraz kiedy warto korzystać z tego środowiska.
Jak już wspomniałem na wstępie, SQLCLR pozwala nam wykonywać kod zarządzany w procesie bazy danych. CLR posiada wiele mechanizmów wspierających programowanie. W skład środowiska CLR wchodzą:
- nadzorca klas – zarządza metadanymi, wczytuje interfejsy klas,
- kompilator JIT – konwertuje kod pośredni IL na maszynowy,
- nadzorca kodu – nadzoruje uruchamianym kodem,
- Garbage Collector – automatycznie zwalnia nieużywaną pamięć,
- interfejs bezpieczeństwa – zapewnia mechanizmy bezpieczeństwa,
- interfejs debugowania – pozwala na wyszukiwanie błędów i śledzenie stanu uruchamianego kodu,
- nadzorca typów – nadzoruje proces konwersji typów,
- nadzorca wyjątków – zarządza obsługą wyjątków,
- interfejs wątków – dostarcza interfejs dla programowania wielowątkowego,
- marszaler COM – dostarcza mechanizmy marszalingu do i z COM,
- bazowa biblioteka klas – integruje kod z biblioteką .NET.
Mechanizmy te znacznie ułatwiają pracę, wykonują bowiem niektóre czynności za programistę, który może skupić się na tworzeniu samych obiektów w jednym z języków – C# lub Visual Basic .NET. Pozwalają też na tworzenie obiektów takich jak:
- funkcje skalarne i tablicowe,
- procedury składowane,
- wyzwalacze,
- agregaty,
- własne typy danych.
Dodatkowo .NET Framework oferuje wiele wbudowanych klas i metod, które mogą być łatwo implementowane po stronie serwera. Wiele zadań, które były niewygodne lub trudne do wykonania za pomocą kodu transakcyjnego SQL, można zrealizować za pomocą kodu zarządzanego. W wyniku kompilacji takiego kodu otrzymujemy pliki assemblies, które możemy podejrzeć w naszej bazie danych za pomocą funkcji sys.assemblies*.*
Pracę ze środowiskiem CLR należy zacząć od włączenia tej usługi w SQL Server. Możemy to zrobić, korzystając z wbudowanej procedury sp_configure:
Przykład 1.
EXEC sp_configure 'clr enabled', 1;
GO
RECONFIGURE;
Teraz możemy już przystąpić do stworzenia pierwszego projektu – prostej funkcji, która zwróci większą z dwóch podanych liczb. Oczywiście mało kto korzysta z SQLCLR do tak prostych rzeczy, jednak przykład ten ma na celu jedynie pokazanie, jak tworzyć takie obiekty. W tym celu uruchamiamy MS Visual Studio .NET, wybieramy opcję File -> New -> Project. Następnie rozwijamy menu Database i z zakładki SQL Server wybieramy projekt SQL CLR dla wybranego języka – C# lub Visual Basic. Ja będę korzystał z tego pierwszego języka:
Rys. 1. Tworzenie nowego projektu.
Nadajemy nazwę naszemu projektowi i klikamy OK. Następnie ukazuje się okienko, w którym musimy wybrać serwer (pole 'Server name') oraz bazę danych (pole 'Select or enter database name'), z którymi chcemy się połączyć.
Rys. 2. Połączenie z bazą danych.
Możemy dodatkowo przeprowadzić test połączenia z naszą bazą, aby upewnić się, że wprowadzone przez nas dane są poprawne. Powinniśmy otrzymać następujący komunikat:
Rys. 3. Testowanie połączenia.
Po zatwierdzeniu wprowadzonych danych ukazuje nam się okno naszego projektu. Aby dodać nowy obiekt, przechodzimy do zakładki Solution Explorer i z menu kontekstowego wybieramy Add -> User-Defined Function.
Rys. 4. Dodawanie obiektu do projektu.
Następnie nadajemy nazwę naszej funkcji i dodajemy ją do projektu. Kolejnym krokiem jest zdefiniowanie naszej funkcji:
Przykład 2.
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
public partial class UserDefinedFunctions
{
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString max_function(int a, int b)
{
return new SqlString("max = " + Math.Max(a,b));
}
};
Teraz wystarczy tylko utworzyć plik DDL oraz przenieść go na serwer bazodanowy. W tym celu wybieramy z menu Build -> Build ProjectName oraz Build -> Deploy ProjectName.
Rys. 5. Tworzenie pliku DDL i przenoszenie go na serwer.
Aby sprawdzić działanie funkcji, uruchamiamy SQL Server Management Studio oraz wykonujemy następujące zapytanie:
Przykład 3.
SELECT dbo.max_function(3,1) AS 'max'
w wyniku którego powinniśmy otrzymać:
Rys. 6. Wynik działania funkcji max_function.
Udało nam się więc stworzyć za pomocą SQLCLR pierwszą funkcję. Pozostałe obiekty tworzymy podobnie. Stwórzmy więc jeszcze jeden przykład – procedurę składowaną, która wyświetli osoby o określonym wieku. Najpierw tworzymy tabelę, którą uzupełniamy przykładowymi danymi:
Przykład 4.
CREATE TABLE osoba
(
imie varchar(30),
nazwisko varchar(30),
wiek int
)
INSERT INTO osoba VALUES('Tomasz','Rybacki',31),('Agata','Mroz',25),('Roman','Jankowski',15),('Karolina','Kosecka',31)
Następnie przechodzimy do MS Visual Studio, gdzie tworzymy naszą procedurę:
Przykład 5.
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
public partial class StoredProcedures
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void wiek(SqlInt32 wiek)
{
using (SqlConnection conn = new SqlConnection("context connection=true"))
{
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = @"SELECT * FROM osoba WHERE wiek= @wiek";
cmd.Parameters.AddWithValue("@wiek",wiek);
conn.Open();
SqlContext.Pipe.ExecuteAndSend(cmd);
}
}
};
Wywołując stworzoną przez nas procedurę
Przykład 6.
EXEC dbo.wiek 31
otrzymujemy:
Rys. 7. Wynik wykonania procedury dbo.wiek.
Wiemy już zatem, jak należy tworzyć obiekty w SQLCLR, pozostaje więc jeszcze pytanie: kiedy warto korzystać ze środowiska .NET, a kiedy pozostać przy T-SQL? Generalnie T-SQL oferuje szybszy dostęp do danych i efektywniej wykonuje operacje na zbiorach rekordów. CLR z kolei polecany jest głównie do wszelkich obliczeń numerycznych oraz operacji z minimalnym dostępem do danych. O ile bardziej wydajny może być CLR od T-SQL? Nie ma na ten temat żadnych oficjalnych statystyk, zatem sprawdźmy sami, czy faktycznie warto korzystać z tego środowiska. W tym celu stwórzmy tabelę:
Przykład 7.
CREATE TABLE liczby
(
liczba float
)
oraz uzupełnijmy ją przykładowymi danymi:
Przykład 8.
DECLARE @counter int =1
WHILE @counter <= 1200
BEGIN
INSERT INTO liczby
VALUES(@counter)
SET @counter+=1;
END
Następnie stwórzmy funkcje liczące ciąg Fibonacciego dla podanej liczby – jedną w T-SQL, a drugą w CLR.
T-SQL:
Przykład 9.
CREATE FUNCTION fib1 (@liczba float)
RETURNS float
AS
BEGIN
DECLARE @l1 float=0
DECLARE @l2 float=1
DECLARE @wynik float=0
WHILE @liczba>0
BEGIN
SET @wynik=@l1 + @l2
SET @l1=@l2
SET @l2=@wynik
SET @liczba-=1
END
RETURN(@wynik)
END
CLR:
Przykład 10.
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
public partial class UserDefinedFunctions
{
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlDouble fib2(float a)
{
double l1 = 0;
double l2 = 1;
double wynik=0;
while (a > 0)
{
wynik = l1 + l2;
l1 = l2;
l2 = wynik;
a--;
}
return new SqlDouble(wynik);
}
};
I na koniec porównajmy, ile czasu zajmuje wykonie się poszczególnych funkcji.
T-SQL:
Przykład 11.
DECLARE @time TIME(7)= SYSDATETIME();
SELECT liczba, dbo.fib1(liczba) AS ciag_Fibonacciego
FROM liczby
DECLARE @t TIME(7)= SYSDATETIME();
SELECT DATEDIFF(MILLISECOND,@time,@t) AS 'czas_wykoniania(ms)'
Rys. 8. Wynik wykonania funkcji fib1.
CLR:
Przykład 12.
DECLARE @time TIME(7)= SYSDATETIME();
SELECT liczba, dbo.fib2(liczba) AS ciag_Fibonacciego
FROM liczby
DECLARE @t TIME(7)= SYSDATETIME();
SELECT DATEDIFF(MILLISECOND,@time,@t) AS 'czas_wykoniania_CLR(ms)'
Rys. 9. Wynik wykonania funkcji fib2.
Czasy wykonania:
T-SQL – 7110 ms.
** CLR – 170 ms.**
Widzimy tu miażdżącą wręcz przewagę dla SQLCLR. Zapytanie zostało wykonane prawie 42 razy szybciej niż z zastosowaniem Transact-SQL! Widzimy więc, że umiejętne dokonanie wyboru między CLR a T-SQL może przynieść ogromne korzyści zarówno programistom, jak i administratorom baz danych.
Udostępnienie CLR w SQL Server znacznie rozszerzyło możliwości programowania baz danych. Pozwoliło to na implementowanie funkcji, procedur, wyzwalaczy czy agregatów w środowisku .NET. Jeżeli jeszcze dodamy, że stosowanie tego środowiska może znacznie skrócić czas wykonywania niektórych operacji, to myślę, że każda osoba zajmująca się bazami danych powinna dość dokładnie zapoznać się z tym środowiskiem.