Widoki to nic innego jak zapytania SQL, które zostały zapisane i są przechowywane w bazie danych pod pewną nazwą. W rzeczywistości jest to tabela, która została wygenerowana predefiniowanym zapytaniem SQL.
Widok może zawierać wszystkie wiersze danej tabeli lub interesującą nas część. Może zostać utworzony na bazie jednej lub wielu tabel. Wszystko zależy od samego zapytania SQL i efektu, który chcemy uzyskać.
Widoki są typem wirtualnych tabel na których możemy wykonywać szereg operacji takich jak:
strukturyzowanie danych w przystępny sposób;
ograniczenie (widocznych) danych do jedynie niezbędnych;
łączenie wyników z różnych tabel, które mogą stanowić podstawę do generowania raportów.
Tworzenie widoków
Bazowym poleceniem pozwalającym na tworzenie widoków jest CREATE VIEW.
Podstawowe zapytanie przy wykorzystaniu powyższego polecenia przybiera poniższą postać:
CREATE VIEW nazwa_widoku AS
SELECT kolumna1, kolumna2, ... kolumnaN
FROM nazwa_tabeli
WHERE (warunki)
Tak jak wspominałem wcześniej, możemy tworzyć skomplikowane zapytania, łączyć wiele tabel czy używać podzapytań tak jak w przypadku normalnego SELECT’A.
Dysponując bazą danych Adventure Works przygotujemy prosty widok reprezentujący historię zatrudnienia pracowników w firmie – sprawdzimy czy pracują od początku na jednym stanowisku czy też awansowali bądź zmieniali swoją specjalizację. Dobrą praktyką jest przygotowanie w pierwszej kolejności zapytania, które zwraca zadowalające nas rezultaty a dopiero później utworzenie widoku:
SELECT
e.BusinessEntityID,
e.LoginID,
p.FirstName + ' ' + p.LastName as Employee,
e.JobTitle,
d.Name
FROM HumanResources.Employee as e
INNER JOIN Person.Person as p on e.BusinessEntityID = p.BusinessEntityID
INNER JOIN HumanResources.EmployeeDepartmentHistory as edh on e.BusinessEntityID = edh.BusinessEntityID
INNER JOIN HumanResources.Department as d on e.BusinessEntityID = d.DepartmentID
W drugim kroku dokonamy utworzenia widoku na podstawie powyższego polecenia:
CREATE VIEW EmployeeDepartmentHistoryView AS
SELECT
e.BusinessEntityID,
e.LoginID,
p.FirstName + ' ' + p.LastName as Employee,
e.JobTitle,
d.Name
FROM HumanResources.Employee as e
INNER JOIN Person.Person as p on e.BusinessEntityID = p.BusinessEntityID
INNER JOIN HumanResources.EmployeeDepartmentHistory as edh on e.BusinessEntityID = edh.BusinessEntityID
INNER JOIN HumanResources.Department as d on e.BusinessEntityID = d.DepartmentID
Jak wygląda odczyt danych z widoków? W identyczny sposób jak przy zwykłym zapytaniu. Zamiast nazwy tabeli podajemy nazwę zdefiniowanego widoku:
SELECT * FROM EmployeeDepartmentHistoryView
Dodawanie/Aktualizowanie/Kasowanie rekordów
Temat ten wymaga nieco głębszej analizy. Jest jednocześnie mało skomplikowany w przypadku utworzenia widoku na bazie jednej tabeli. W takim scenariuszu wszelkie operacje DML (dodawanie, kasowanie, aktualizacja) dokonane na widoku zostaną automatycznie przeniesione to tabeli bazowej.
Jeżeli spróbujecie dokonać usunięcia rekordu z naszego widoku przy pomocy przykładowego polecenia:
DELETE FROM EmployeeDepartmentHistoryView
where BusinessEntityID = 8
zobaczycie poniższy komunikat:
View or function 'EmployeeDepartmentHistoryView' is not updatable because the modification affects multiple base tables.
W tym miejscu musimy jeszcze wspomnieć o klauzuli WITH CHECK OPTION. Jej użycie pozwala nam upewnić się, że polecenia INSERT oraz UPDATE spełniają warunek/warunki określone w definicji widoku – w przeciwnym wypadku zostanie zwrócony błąd.
Dodajmy nowy widok utworzony na bazie tabeli HumanResources.Department który będzie zwracał tylko nazwy departamentów zaczynające się od litery 'P':
CREATE VIEW DeparemntsFromK AS
SELECT * FROM HumanResources.Department
WHERE Name Like 'P%'
WITH CHECK OPTION;
Dodając WITH CHECK OPION nakładamy ograniczenie na instrukcje dodawania i aktualizowania rekordów. Jeżeli ten warunek będzie spełniony nowy rekord zostanie dodany/zaktualizowany zarówno w widoku jak i tabeli bazowej:
-- pamiętajcie również o ograniczeniach nałożonych na tabelę bazową
INSERT INTO DeparemntsFromK(Name, GroupName) Values ('Produkcja', 'Fabryka')
Wykonanie polecenia nie spełniającego naszego warunku zwróci nam błąd:
-- The attempted insert or update failed because the target view either specifies WITH CHECK OPTION
-- or spans a view that specifies WITH CHECK OPTION and one or more rows resulting from the
-- operation did not qualify under the CHECK OPTION constraint.
-- The statement has been terminated.
INSERT INTO DeparemntsFromK(Name, GroupName) Values ('Kosmici', 'Fabryka')
Kasowanie widoków
Po zakończonych próbach z widokami warto zrobić porządki w bazie danych i je skasować – jeżeli nie są nam potrzebne. W tym celu posłużymy się bardzo prostym poleceniem:
DROP VIEW nazwa_widoku
Dla naszego widoku na którym eksperymentowaliśmy polecenie to przyjmie postać: