Procedury składowane c.d. Parametry tablicowe w Transact-SQL. W SQL Serwerze 2008 wprowadzono parametry tablicowe (Table Valued Parameters - TVP). Zadanie 1. Proszę napisad procedurę składowaną, która wpisze do tabeli Customers dane z parametru tablicowego. CREATE TYPE CustomerTableType AS TABLE ( CustomerID NCHAR(5) PRIMARY KEY, CompanyName NVARCHAR(40) NOT NULL, ContactName NVARCHAR(30), Address NVARCHAR(60), City NVARCHAR(15), Region NVARCHAR(15), PostalCode NVARCHAR(10), Country NVARCHAR(15), Phone NVARCHAR(24), Fax NVARCHAR(24) ); CREATE PROC Zad1 @TVP CustomerTableType READONLY AS INSERT INTO dbo.customers( CustomerID, CompanyName, ContactName, Address, City, Region, PostalCode, Country, Phone, Fax) SELECT * FROM @TVP END Proszę przy użyciu powyższej procedury wpisad do tabeli Customers dane wszystkich dostawców z tabeli Suppliers. DECLARE @Tabela CustomerTableType INSERT INTO @Tabela SELECT SupplierID, CompanyName, ContactName, Address, City, Region, PostalCode, Country, Phone, Fax FROM Suppliers 1
SELECT * FROM @Tabela EXECUTE Zad1 @Tabela Kursory jako parametry w Transact-SQL. Kursory Transact-SQL (implementowane po stronie serwera - serwerowe). ISO Syntax DECLARE cursor_name [ INSENSITIVE ] [ SCROLL ] CURSOR FOR select_statement [ FOR { READ ONLY UPDATE [ OF column_name [,...n ] ] } ] [;] Transact-SQL Extended Syntax DECLARE cursor_name CURSOR [ LOCAL GLOBAL ] [ FORWARD_ONLY SCROLL ] [ STATIC KEYSET DYNAMIC FAST_FORWARD ] [ READ_ONLY SCROLL_LOCKS OPTIMISTIC ] [ TYPE_WARNING ] FOR select_statement [ FOR UPDATE [ OF column_name [,...n ] ] ] [;] Pobieranie wierszy: FETCH [ [ NEXT PRIOR FIRST LAST ABSOLUTE { n @nvar } RELATIVE { n @nvar } ] FROM ] { { [ GLOBAL ] cursor_name } @cursor_variable_name } [ INTO @variable_name [,...n ] ] ISO: INSENSITIVE tworzona jest kopia statyczna danych w bazie tempdb. Nie można aktualizowad (domyślnie można). SCROLL można wykorzystywad wszystkie opcje FETCH: FIRST, LAST, PRIOR, NEXT, RELATIVE, ABSOLUTE (domyślnie można używad tylko NEXT). READ ONLY nie można wykonywad pozycjonowanych zmian danych (domyślnie można, np. UPDATE WHERE CURRENT OF nazwakursora). UPDATE OF można aktualizowad tylko wymienione kolumny (samo UPDATE oznacza zgodę na aktualizacje wszystkich kolumn). T-SQL: LOCAL zakres (scope) kursora jest lokalny do wsadu (batch), procedury składowanej, wyzwalacza, gdzie kursor został zadeklarowany. Do kursora można się odwoływad przez lokalne zmienne lub parametr OUTPUT. Dealokacja następuje przy zakooczeniu wykonywania wsadu, procedury, wyzwalacza lub (w przypadku parametru OUTPUT) gdy ostatnia zmienna odwołująca się do kursora wyjdzie z zakresu lub zostanie zdealokowana. GLOBAL kursor będzie globalny dla połączenia. Zwolnienie pamięci będzie zrealizowane przy zamknięciu połączenia lub wskutek jawnie wykonanej operacji deallocate. 2
FORWARD_ONLY określa, że kursor może byd skrolowany tylko od pierwszego do ostatniego wiersza (można używad tylko FETCH NEXT). Jeśli wyspecyfikowano FORWARD_ONLY bez słowa STATIC, KEYSET lub DYNAMIC, kursor będzie dynamiczny (DYNAMIC). Jeśli nie wyspecyfikowano ani FORWARD_ONLY ani SCROLL, FORWARD_ONLY jest przyjmowany domyślnie, chyba, że jedno ze słów STATIC, KEYSET lub DYNAMIC zostało wyspecyfikowane. Dla kursorów STATIC, KEYSET i DYNAMIC domyślną opcją jest SCROLL. STATIC odpowiednik INSENSITIVE. KEYSET w bazie temdb tworzona jest tabela z wartościami klucza (kluczy) określającymi zestaw wierszy. Jeśli w chociaż jednej tabeli, do której odwołuje się kursor, nie ma indeksu unikalnego, to typ kursora zmieniany jest na STATIC. Zmiany na danych są widoczne poprzez kursor (w kolejnych operacjach FETCH), z wyjątkiem dodania nowego wiersza, skasowania wiersza lub aktualizacji klucza. Przy próbie pobrania nieistniejącego wiersza funkcja @@FETCH_STATUS zwraca wartośd -2. READ_ONLY zmiany pozycjonowane nie są możliwe (domyślnie są). OPTIMISTIC zmiany pozycjonowane są możliwe, jest realizowane optymistyczne sterowanie współbieżnością. SCROLL LOCK zmiany pozycjonowane są możliwe, jest realizowane pesymistyczne sterowanie współbieżnością z blokadami. Jeśli nie wyspecyfikowano ani READ_ONLY, ani OPTIMISTIC ani SCROLL LOCK, to będzie przyjęte ustawienie według poniższych reguł: Jeśli zdanie SELECT nie wspiera aktualizacji (np. niewystarczające uprawnienia), kursor będzie READ_ONLY. Standardowo kursory STATIC i FAST_FORWARD są READ_ONLY. Standardem dla kursorów DYNAMIC i KEYSET jest OPTIMISTIC. Przykład wykorzystania kursora jako parametru procedury składowanej (przykład z Books Online). USE AdventureWorks; /* Create a procedure with a cursor output parameter. */ CREATE PROCEDURE OpenCrsr @OutCrsr CURSOR VARYING OUTPUT AS SET @OutCrsr = CURSOR FOR SELECT TOP (20) LastName FROM Person.Contact WHERE LastName LIKE 'S%'; OPEN @OutCrsr; /* Allocate a cursor variable. */ DECLARE @CrsrVar CURSOR; /* Execute the procedure created earlier to fill the variable. */ EXEC OpenCrsr @OutCrsr = @CrsrVar OUTPUT; /* Use the variable to fetch the rows from the cursor. */ FETCH NEXT FROM @CrsrVar WHILE (@@FETCH_STATUS <> -1) 3
FETCH NEXT FROM @CrsrVar END; CLOSE @CrsrVar; DEALLOCATE @CrsrVar; Kursor w procedurze może byd oparty o zmienną tabelową, ale wówczas musi to byd kursor statyczny: SET @OutCrsr = CURSOR STATIC FOR W ten sposób można na zewnątrz procedury przekazad zestaw wierszy. Inna metoda: Można utworzyd tabelę trwałą lub tabelę tymczasową (pierwszym znakiem w nazwie jest #). Jeszcze inna metoda: jeśli procedura zawiera zdanie select, to można skorzystad ze schematu: CREATE PROC PR1 AS SELECT PRODUCTID FROM PRODUCTS DECLARE @TAB TABLE (P1 INT) INSERT INTO @TAB EXEC PR1 SELECT * FROM @TAB Zadanie 2. Proszę napisad procedurę, która poprzez parametr wyjściowy typu kursor przekaże dla każdej kategorii (ma byd podana nazwa kategorii) średnią cenę produktu w tej kategorii. Zadanie 3. Proszę napisad procedurę, która wypisze dla każdej tabeli w bazie danych Northwind ile jest w tabeli wierszy. CREATE PROC Z2 AS DECLARE @TableName NVARCHAR(40) DECLARE @Tab TABLE(Nazwa NVARCHAR(40), Ile BIGINT) DECLARE ctablenames CURSOR FORWARD_ONLY -- KURSOR DYNAMICZNY FOR SELECT Name FROM Northwind.sys.tables ORDER BY Name OPEN ctablenames FETCH FROM ctablenames INTO @TableName WHILE @@FETCH_STATUS<>-1 IF @@FETCH_STATUS<>-2 SET @TableName=RTRIM(@TableName)--usunięcie spacji z pr. strony INSERT INTO @Tab -- INSERT z EXECUTE EXECUTE('SELECT '''+@TableName+''', COUNT(*) FROM ['+ @TableName+']') FETCH FROM ctablenames INTO @TableName END --IF oraz WHILE SELECT * FROM @Tab CLOSE ctablenames DEALLOCATE ctablenames EXEC Z2 4
Zadanie 3. Proszę napisad procedurę, usunie wszystkie klucze obce z bazy danych Test (jeśli takiej bazy danych nie ma, należy ją utworzyd, ponadto należy w niej utworzyd przykładowe co najmniej trzy tabele i powiązad je więzami kluczy obcych). Wskazówka: Proszę przeglądnąd widok sys.objects (SELECT * FROM SYS.OBJECTS) w bazie danych Test. Usuwanie kluczy obcych będzie realizowane poleceniem ALTER TABLE DROP CONSTRAINT. Nazwę tabeli można wyświetlid funkcją ObjectName(object_id), gdzie object_id będzie pobrany z widoku sys.objects z kolumny Parent_object_id. 5