O wykorzystaniu CLR w SQL Server 2005 można by napisać wiele książek (i nie wątpię, że w najbliższym czasie tak się stanie). My zajmiemy się dość zasadniczymi zagadnieniami, które będą mogły przedstawić rewolucyjny charakter usprawnień. Pierwszym krokiem będzie napisanie funkcji do formatowania daty. Drugim nieco bardziej skomplikowanym zagadnieniem będzie napisanie własnej funkcji agregującej.
Funkcje bazodanowe
Pierwszym krokiem, jakiego musimy dokonać jest uruchomienie MS Visual Studio .NET 2005. Gdy już to będziemy mieli za sobą, musimy utworzyć odpowiedni projekt. W tym celu musimy wykonać następujące czynności. W głównym menu wybieramy File -> New -> Project. Następnie z lewego okna rozwijamy Visual C# zaznaczamy Database i naciskamy OK.
Po utworzeniu projektu pojawi się okienko dialogowe z możliwością wyboru serwera, z jakim chcemy się połączyć i bazy danych na nim istniejącym. My uzupełnimy to tak jak na zdjęciu poniżej.
Gdy projekt się uruchomi, odnajdujemy zakładkę Solution Explorer i klikamy prawym guzikiem myszy na projekt w celu dodania naszej funkcji. Wybieramy Add -> User Defined Funcition po otworzeniu okna dialogowego wpisujemy nazwę funkcji DateFormater i naciskamy OK.
Pojawiło nam się okno z wygenerowaną przykładową funkcją. Przechodzimy teraz do napisania funkcji, która jako argument będzie pobierać datę oraz format daty w jakim chcemy ją przedstawić. Jak pamiętamy z wcześniejszych artykułów, standardowo zwracana data wygląda następująco: 2005-08-20 17:19:22.977. Co jednak, jeśli chcemy, aby data zwracana była w formacie 20.08.2005 lub 20//08//2005? Aby rozwiązać ten problem wystarczy tylko jedna linia kodu w C#. Zmodyfikujmy wygenerowaną funkcję do takiej postaci:
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString
DateFormater(SqlDateTime myDate, SqlString dateFormat)
{
return
(SqlString)string.Format((string)("{0:" + dateFormat +
"}"), myDate.Value);
}
Metoda pobiera standardowo dwa argumenty: datę oraz format, w jakim chcemy datę przedstawić. Format daty przedstawiany jest w następujący sposób: „y” reprezentuje rok, „M” miesiąc, a „d” dzień. Pomiędzy podane znaczniki można podstawiać dowolne znaki. Teraz wystarczy tylko utworzyć plik DDL i przerzucić go na serwer bazodanowy. Z menu wybieramy Build -> Build Solution, a następnie Bulid- > Deploy Solution. Jeśli wszystko poszło dobrze możemy teraz przejść do serwera SQL i sprawdzić czy nasza funkcja działa poprawnie. Musimy tylko jeszcze włączyć na MS SQL Server 2005 opcje korzystania z CLR. Funkcja ta ze względów bezpieczeństwa jest domyślnie wyłączona. Aby ją uruchomić, wystarczy uruchomić następującą linię kodu.
sp_configure 'clr enabled', 1
go
reconfigure
go
Teraz możemy przetestować naszą funkcję. Poniższy skrypt pokazuje w jaki sposób można ją uruchomić i wyniki jakie otrzymujemy.
USE AdventureWorks
GO
SELECT dbo.DateFormater(GetDate(),
'yy.MM.dd') 05.08.20
SELECT dbo.DateFormater(GetDate(),
'dd///MM///yyyy') 20///08///2005
SELECT dbo.DateFormater(GetDate(),
'yy**MM**dd') 05**08**20
SELECT
dbo.DateFormater(GetDate(), 'yy:-)MM:-)dd') 05:-)08:-)20
Funkcje agregujące
Funkcjom agregującym poświęciliśmy jeden z artykułów. I od tego czasu mam nadzieję, że wszyscy zdają sobie sprawę, jak ważnym zagadnieniem są agregaty. A co jeśli moglibyśmy napisać własne funkcje agregujące, z własnymi zasadami agregacji? Taką właśnie możliwość daje nam połączenie SQL Server 2005 z .NET Framework.
Aby pracować z własnymi User-Defined Aggregates (UDA), należy zapoznać się z czterema podstawowymi zasadami rządzącymi ich działaniem. Entering, accumulating, mering i existing. Kiedy uruchamiamy funkcję agregującą na serwerze, musimy zainicjalizować wartość początkowe. Jeśli robilibyśmy operacje dodawania, odejmowania - wartością startową byłoby zero. Jeśli jednak chcielibyśmy przeprowadzić operacje mnożenia wartością pierwotną byłaby wartość jeden.
Ponieważ nasz przykład nie będzie nazbyt wyrachowany i ograniczy się do powielenia agregatu SUM, wartość domyślną inicjalizujemy zerem.
public void Init()
{
result = 0;
}
Następnym krokiem jest zdefiniowanie tego, co się dzieje z kolejną wartością, jaka jest pobierana. Czyli co się dzieje, jeśli podczas procesu przechodzenia po kolejnych wierszach w tabeli dostajemy nowe wartości. Jak mają się zachować w stosunku do wartości, które już mamy.
public void Accumulate(SqlInt32 Value)
{
result += (int)Value;
}
W naszym przypadku odpowiedź jest dość prosta. Należy dodać wartość, która została podana do wartości, które już zsumowaliśmy.
Metoda Terminale zwraca wynik po wykonaniu metody Accumulate. Jest również odpowiedzialna za zwrócenie wyniku do kodu wywołującego. W naszym przypadku zwróci sumę wyniku z całej tabeli.
public SqlInt32 Terminate()
{
return new SqlInt32(result);
}
Merge jest specjalną metodą, która pomaga w utrzymaniu spójności na poziomie obiektowym.
public void Merge(MyIntegerCalculate
Group)
{
result += Group.result;
}
W ten sposób napisaliśmy funkcję agregującą SUM, która w naszym przypadku nazywa się MyIntegerCalculate i używamy jej dokładnie w ten sam sposób, jak SUM z tą różnicą, że wstawiamy przed nazwę agregata dbo.
Przykład ten miał za zadanie zademonstrować, jak działają funkcje agregujące i przedstawić cztery podstawowe zasady tworzenia własnych agregatów. Następnym i wydaje się ostatnim krokiem na drodze tworzeniem własnych efektywnych i poprawnie działających funkcji są atrybuty.
Atrybuty agregatów
Atrybuty są specjalnymi
znacznikami na klasach w .NET, za pomocą których możemy
definiować dodatkową funkcjonalność dla danej klasy. Przykładem mogą być Web
serwisy, przy których należy do każdej z metod dodać atrybut [WebMethod],
aby umożliwić korzystanie z niej za pośrednictwem stron WWW.
W przypadku funkcji agregujących, które z definicji pracują na bazach danych,
gdzie najważniejsza jest wydajność, atrybuty są wyjątkowo ważne. Pomagają one
SQL
Server Query Optimizer efektywniej budować zapytania.
Format – mówi SQL czy powinien serializować dane
IsInvariantToNull – jeśli jest ustawione na true, serwer SQL ignoruje wartości NULL. W naszym przypadku nie ma potrzeby pobierać wartości nieokreślonych, więc możemy bez problemu ustawić na true. Jeśli jednak tworzylibyśmy funkcję wyliczającą wartość średnią musielibyśmy również uwzględnić wartości NULL.
IsInvariantToOrder – jeśli ustawimy na true, oznacza to, że nie zważamy na kolejność w jakiej dane są pobierane. Ponieważ suma jest operacją przemienną, nie ma potrzeby zachowywać kolejności.
IsNullIfEmpty – jeśli jest ustawione na true, zwraca wartość nieokreśloną NULL, gdy nie ma żadnych wartości. W naszym przypadku ogranicza się to do tego, że jeśli tabela będzie pusta, lub wiersze, które podaliśmy do sumowania posiadają wartość NULL to wynik również będzie NULL.
Podsumowanie
Wydaje mi się, że wszystkich zachęciłem do zagłębienie się w CLR i nowe możliwości jakie stoją przed SQL Server 2005 i jego integracją z .NET Framework. Mimo wielkich możliwości, jakie zostały dane zarówno administratorom baz danych, jak i developerom powinniśmy pamiętać, iż operacje wykonywane za pomocą Frameworka są znacznie bardziej kosztowne i czasochłonne, niż alternatywna operacja w Transact-SQL. Przy użyciu CLR powinniśmy najpierw sprawdzić, czy korzyści wynikające z łatwiejszego zakodowania są na tyle duże, aby zrezygnować z SQL.