You are on page 1of 30

Создавање на погледи – планирање и создавање на упити за погледи.

Промена на погледи

Тригери

Лабораториска вежба 8
2

Содржина
• Создавање на погледи
• Промена на погледи
• Бришење на погледи
• Промена на податоци преку погледи

• Создавање на тригер
• Забрана, разрешување, промена и бришење на тригер
• Каскадни и рекурсивни тригери
• Користење на тригери за исполнување на дејствија за
зачувување на целост на податоците
• Тригери за табела која реферира
• Тригери за реферираната табела
• Задача
3

Погледи

• За разлика од постојаните базови таблици, кои содржат


постојани податоци, кој се чуват во базата на податоци,
погледите се друг вид на табели. Погледите (views) се
табели, чија содржина се зема или пополнува од други
табели. Познати се под името виртуелни таблици. Може да
се направи упит кон поглед преку конструкциите SELECT,
INSERT или друг вид конструкција како што се прави
обраќање кон базова таблица со таа разлика што погледите
не содржат собствени податоци. Погледот преставува упит,
кој се извикува за исполнување секогаш кога во некоја
конструкција се извршува препраќање кон тој поглед.
Излезните податоци на тој упит ја формира содржината на
погледот.
4
Создавање на погледи

• Дефинирањето на поглед се извршува со помош на


конструкцијата CREATE VIEW, која го има следниот општ вид:
CREATE VIEW view_name [(column_list)]
AS
   SELECT statement
[WITH CHECK OPTION]

Пример: 1)
CREATE VIEW All_Employees_And_Customers
  AS
    SELECT FirstName+' '+LastName AS Name, CompanyName
    FROM Employees e
    LEFT JOIN Sales s ON e.EmployeeID = s.EmployeeID
    FULL JOIN Customers c ON s.CustomerID = c.CustomerID
5

• Оваа конструкција не генерира излезни податоци, туку потврда за


создавање на нов објект во базата на податоци. Создадениот поглед може да
се користи како и секоја друга табела. Може да се праќат упити кон неа, да
се ажурира, да се додават податоци, да се бришат податоци и да се
соединува со други табели или погледи. Пример:
SELECT * FROM All_Employees_And_Customers
WHERE Name LIKE 'Ab%'
ORDER BY Name
• Поглед може да се создаде врз база на која и да е табела, како и врз база на
кој и да е друг поглед.
• Погледите ги зголемуваат можностите за контрола на податоците.
• Овозможуват начин за достап до ограничен дел до информација од дадена
табела.
• Погледите може да содржат GROUP BY или да се базират на други
групирани погледи. Групираните погледи дават можност за постојана
обработка на извлечените податоци. Наместо постојано да се конструират
сложени упити, може само да се создаде соодветен поглед.
6
Пример:2)
CREATE VIEW TotalForDay
 AS
 SELECT CONVERT(datetime,CONVERT(char(10),SaleDate,112))
        AS DateOfSale,
        COUNT(DISTINCT CustomerID) AS CustomersCount,
        COUNT(DISTINCT EmployeeID) AS EmployeesCount,
        COUNT(SaleID) AS SalesCount,
        AVG(TotalForSale) AS AverageTotal,
        SUM(TotalForSale) AS SumTotal
 FROM Sales
 GROUP BY CAST(CONVERT(char(10),SaleDate,112)AS datetime)

• По создавање на погледи може да се користат упити кон погледи.


Пример:
SELECT * FROM TotalForDay
SELECT * FROM TotalForDay
  WHERE SumTotal=(SELECT MAX(SumTotal) FROM TotalForDay)
SELECT * FROM TotalForDay
  WHERE SalesCount>=2 AND SumTotal>=1000
7
• Погледите може да користат и под-упити, вклучително и взаемно
сврзани под-упити.
Пример:3)
CREATE VIEW MaxTotalForCustomer
AS
   SELECT c.CustomerID, c.CompanyName, s.TotalForSale,
          s.SaleDate
   FROM Customers c
   INNER JOIN Sales s ON c.CustomerID=s.CustomerID
   WHERE TotalForSale =
       ( SELECT MAX(TotalForSale)
         FROM Sales s1
         WHERE s.CustomerID = s1.CustomerID )
Упит кој дефинира поглед кој извлекува податоци за најголемото
пазарување на клиентите помеѓу сите собствени пазарувања.
Пример за упит кон поглед:
SELECT * FROM MaxTotalForCustomer
WHERE CompanyName LIKE 'L%'
8
• Погледите може да се базират на неколку упити, комбинирани со
операторот UNION.
Пример: 4)
CREATE VIEW CityPeople
  AS
   SELECT CustomerID AS ID, CompanyName, City,
          'customer' AS Name
   FROM Customers
   UNION
   SELECT SupplierID, CompanyName, City, 'supplier'
   FROM Suppliers

• Вклучуват се низи за да се постави етикета на секој ред, која покажува


кој упит го генерирал. Следниот упит кон поглед ги изведува тие
редови за кој името на градот е во зададен азбучен обхват:

SELECT * FROM CityPeople


WHERE City BETWEEN 'A' AND 'L'
ORDER BY City
9
• Не може да се користи ORDER BY при дефинирањето на
поглед. Според стандардот излезните податоци од упитот
формират содржина на поглед кој е копија на базовата
таблица и по дефиниција е неподредено. Упитите кон
поглед може да користат ORDER BY.
• Исклучок прави поглед кој вклучува во дефиницијата TOP
n.

Пример:
CREATE VIEW TopProductPrice
 AS
  SELECT TOP 3 WITH TIES ProductID, ProductName, Price
  FROM Products
  ORDER BY Price DESC
GO
SELECT * FROM TopProductPrice
10
Промена на погледи

• SQL Server дозволува да се промени дефиницијата на


поглед:
ALTER VIEW view_name [(column_list)]
  AS
    SELECT statement
[WITH CHECK OPTION]
• Погледот треба да постои, а укажаната дефиниција ја
заменува таа која е имал погледот пред изполнување на
ALTER VIEW.
 
Бришење на погледи

DROP VIEW view_name


• Бришењето на погледи не укажува влијание над базовата
табела од која тој се формира.
11
Промена на податоците преку посредување на погледи

• Можно е модифицирање на редовите во табелата, на која е базиран некој


поглед, така што се користи самиот поглед. Постојат неколку
ограничувања при промена на податоци со посредување на погледи:
▫ Модифицирањето е ограничено до една табела, т.е. конструкциите INSERT и
UPDATE се дозволени за погледи на множество табели, ако промената ја
засега само една табела. Конструкциите DELETE не се дозволени за погледи
на множество табели.
▫ Некој погледи се само за читање – не може да се додават (INSERT) редови
ниту да се променуваат (UPDATE) колони од поглед, кој вклучуват функции,
UNION, клучов збор GROUP BY или DISTINCT, како и колоните во поглед,
кои се резултат на некој изрази или резултат од вградени функции.
▫ Промени и критериум за погледи – по подразбирање конструкциите за
промена на податоци преку погледи не се проверуват, дали засегнатите
редови се наоѓаат во обхватот на погледот. Пример конструкцијата INSERT за
даден поглед , кој додава ред во базовата табела, но не додава ред кон самиот
поглед, кога вредностите на колоните не одговарат на критериумите на
погледот, но се валидни за табелата и можат да бидат додадени, и покрај тоа
што се преставени во селекцијата на погледот. Аналогно за UPDATE. Ако
треба сите промени да се проверуват, неопходно е при создавањето на
погледот да се искористи опцијата WITH CHECK OPTION.
12

Пример:1)
CREATE VIEW Customers_VT
  AS
   SELECT * FROM Customers
   WHERE City = 'Veliko Tarnovo'
WITH CHECK OPTION

• Опцијата WITH CHECK OPTION се додава не само по однос на


условот на погледот, кој директно го содржи но и по однос на
други погледи, кои тој поглед ги содржи.
Пример:2)
CREATE VIEW Product_List
  AS
    SELECT ProductID, ProductName, Price,
           Stock, ReorderLevel, Discontinued
   FROM Products
   WHERE Discontinued = 0
13
• Да предпоставиме дека треба да создадеме друг поглед, кој е базиран на
предходниот:

3) CREATE VIEW ProductsForReorder


AS
     SELECT *
    FROM Product_List
WITH CHECK OPTION

• Нема да може после тоа да се изврши следното ажурирање:


UPDATE ProductsForReorder
   SET Discontinued = 1
WHERE ProductID = 3
• Таа не нарушува условот во погледот ProductsForReorder, но нарушава
условот во погледот Product_List, на кој се базира.

• Бришење на ред во поглед води до бришење на ред од соодветната табела:


DELETE FROM ProductsForReorder
WHERE ProductID = 12
14
Тригери

Тригер е специален вид зачувана процедура, која се стартира


автоматски од SQL Server при проба да се модифицират
податоците, со кои тригерот е поврзан (за разлика од
зачуваните процедури, кои се исполнуваат само кога бидат
експлицитно повикани).

• Тригерот може да биде подесен да се стартира, кога податоците


бидат променети по некаков начин, т.е. преку конструкцијата
INSERT, UPDATE или DELETE. За секое дејствие може да се
дефинират повеќе од еден тригер за дадената табела. Нема
начин да се контролира редот, по кој да се извикуват тригерите
за една и иста табела. Ако е од големо значење се определува
стапка по стапка за исполнување на предходно зададен ред,
исто така треба да се оделуват во различни тригери.
15

• Може да се создаде еден тригер, кој да се исполнува при секое


дејствие оделно или при кое и да е исполнување на INSERT,
UPDATE и DELETE. Тригерот се исполнува по еден пат за
секоја конструкција INSERT, UPDATE или DELETE независно
од бројот на редови, кој ги засегнува (0, 1 или повече). За да се
избегне исполнување на код, кој е без смисла, често пати
првата конструкција во тригерот проверува колку реда се
засегнати, при користење на функцијата @@ROWCOUNT.

• Општ вид на конструкцијата за создавање на тригер:

CREATE TRIGGER trigger_name


    ON table_name
    FOR [DELETE][,][INSERT][,][UPDATE]
    AS
       sql_queries
16

• Тригерот се стартира, откако конструкцијата за модифицирање


на податоци заврши со работа, но пред таа работа да се потврди
во базата на податоци. Како конструкциите така и секаквите
модификации извршени во тригерите се третират како
транзакција. Поради тоа тригерот може да ја преврне на назад
модификацијата на податоците, кој се појавиле при
стартирањето.
• Тригерот има достап до првичниот изглед и крајниот изглед на
податоците преку специјалните псевдо табели inserted и deleted.
Тие две табели имат ист збир на колони како и основната
табела, над која се извршуваат промени и имат толку редови
колку се засегнати од соодветната конструкција за внесување,
бришење или промена на податоците.
▫ Табелата inserted содржи редови, над кој е влијаела
модификацијата, т.е. внесени или променети и при тоа е добиен
нивниот краен изглед.
▫ Табелата deleted содржи редови, кој се засегнати од соодветната
конструкција за промена или бришење и при тоа го содржат
нивниот првичен изглед.
17
Забрана на тригери се извршува со следната
конструкција:
ALTER TABLE table_name
DISABLE TRIGGER {ALL | trigger_name [,…]}
• Разрешување на тригери се извршува со аналогна
конструкција плус опцијата ENABLE.
• Бришење на тригер се извршува со следната команда:
DROP TRIGGER trigger_name
• Промена на тригер се извършва со следната
конструкција:
ALTER TRIGGER trigger_name
ON table_name
FOR [DELETE][,][INSERT][,][UPDATE]
AS
sql_queries
18
Каскадни и рекурсивни тригери

• Ако тригерот модифицира податоци во некоја друга табела и таа


има тригер, тоа дали тој тригер ќе биде стартиран зависи од
моменталната вредност на sp_configure за опцијата nested
triggers. Ако таа опција е зададена на 1 (EXEC sp_configure
'nested triggers', 1), која е и вредност по подразбирање,
тригерите можат да се извикат каскадно до максимална
редослед од 32.
• Ако тригерот модифицира податоци во иста табела, во која тој
постои, користењето на истата операција (INSERT, UPDATE
или DELETE) по подразбирање нема да стартира тригер одново.
Тоа поведение може да се промени дозволувајки тригерите да
бидат рекурсивни. Ова се контролира за конкретна база на
податоци преку задавање на опцијата recursive triggers на
вредност TRUE (EXEC sp_dboption 'database_name', 'recursive
triggers', TRUE). По подразбирање вредноста на опцијата е
FALSE.
19
Користење на тригери за чување на целост на податоците

Стандардот SQL92 ги дава следните референциални дејствија за


модифицирање на страната на ограничувањето примарен клуч:
• NO ACTION забранува дејствието, ако биде нарушено
ограничувањето FOREIGN KEY. Тоа е единственото референциално
дејствие, реализирано од SQL Server 7.0 за декларациски
ограничувања FOREIGN KEY.
• SET NULL ја обновува табела која се реферира така што, колоните на
надворешниот клуч да станат со вредност NULL (кое бара колоните да
се дефинирани да дозволуват вредност NULL).
• SET DEFAULT ја обновува табела која се реферира така, што
колоните на надворешниот клуч да добијат DEFAULT вредности (бара
се колоните да имат дефинирана вредност по подразбирање).
• CASCADE ја обновува реферираната табела така, што колоните на
надворешниот клуч да ги добијат истите вредности, како и
вредностите кој се добиени при промена на примарниот клуч, или ги
брише редовите кој се реферираат, ако примарниот клуч е избришан.
20

• Најдобар начин за изполнување на NO ACTION е преку


декларирање на ограничувањето FOREIGN KEY, а не преку
тригер. За изполнување на останатите референциални дејствија
треба да се искористи тригер (при верзија 7.0 на SQL Server).
• Проверка за нарушување на ограничувањата се прават пред
активирање на тригерите. Ако биде откриено нарушување на
ограничувањето, конструкцијата се прекинува и не се извршува
изполнување на тригерот, т.е. тој не се активира. За по-добра
прегледност може да се декларира ограничување FOREIGN
KEY во конструкциите со CREATE TABLE, а после тоа забрани
ограничувањето надворешен клуч со следната конструкција:
ALTER TABLE table_name
NOCHECK CONSTRAINT {ALL | constraint_name [,…]}
• На тој начин се гарантира, дека ограничувањето ќе се гледа при
исполнување на sp_help table_name, во дијаграми и други
процедури.
21
Тригери за табела која реферира
1) Забрана на додавање и ажурирање, ако надворешниот клуч во табелата
која реферира нема поклопување на вредностите на примарниот клуч во
реферираната табела:
CREATE TRIGGER SaleHasCustomer
ON Sales
FOR INSERT, UPDATE
AS
IF @@ROWCOUNT = 0 RETURN
IF EXISTS
( SELECT *
FROM Inserted i
LEFT JOIN Customers c ON i.CustomerID = c.CustomerID
WHERE c.CustomerID IS NULL )
BEGIN
RAISERROR('Продажбата треба да има валиден CustomerID.', 16, 1)
ROLLBACK TRANSACTION
END
GO
22

• За да се стартира тригерот , треба да се избрише


или забрани постоечко ограничување FOREIGN
KEY за табелата Sales на следниот начин:

ALTER TABLE Sales


NOCHECK CONSTRAINT FK_Sale_Customer

---------------------------------------------------------------

INSERT INTO Sales (CustomerID, EmployeeID,


SaleDate,TotalForSale)
VALUES (45, 88, 2009-05-04, 3)
23

Со IF UPDATE(column_name) може да се зададе исполнување на


определени дејствија во тригерот, ако промените се одразиле
на точно определени колони:
ALTER TRIGGER SaleHasCustomer
ON Sales
FOR INSERT, UPDATE
AS
IF @@ROWCOUNT = 0 RETURN
IF UPDATE (CustomerID)
IF EXISTS
( SELECT * FROM Inserted i
LEFT JOIN Customers c ON i.CustomerID = c.CustomerID
WHERE c.CustomerID IS NULL )
BEGIN
RAISERROR('Продажбата трябва да има валиден CustomerID.', 16, 1)
ROLLBACK TRANSACTION
END
GO
24
Тригери за реферираната табела
2) Забрана на бришење на ред од реферираната табела, ако тој има соодветни
редови во табелата која реферира:
CREATE TRIGGER CustomerHasSales
ON Customers
FOR DELETE
AS
IF @@ROWCOUNT = 0 RETURN
IF EXISTS
( SELECT *
FROM Deleted d
INNER JOIN Sales s ON d.CustomerID = s.CustomerID )
BEGIN
RAISERROR(‘Клиентот има извршени пазарувања.
Бришењето не е исполнето!', 16, 1)
ROLLBACK TRANSACTION
END
------------------------------------------------------------------------------
DELETE FROM Customers
WHERE CustomerID = 3
25
3) Бришење на ред во реферираната табела ќе доведе до каскадно
бришење на сите редови во табелата која реферира:

CREATE TRIGGER DelCascadeCustomer


ON Customers
FOR DELETE
AS
IF @@ROWCOUNT = 0 RETURN
DECLARE @count int
DELETE FROM Sales
FROM Sales s
INNER JOIN deleted d ON s.CustomerID = d.CustomerID
SET @count = @@ROWCOUNT
IF @count > 0
RAISERROR('%d редот од Sales е избришан при што
резултатот од бришење во
табелата Customers.', 11, 1, @count)
26

DELETE FROM Customers


WHERE CustomerID = 3

При извршување на упитот треба претходно да се


избрише тригерот CustomerHasSales
4) Реализација на SET DEFAULT – ако биде променен некој примарен клуч во 27
реферираната табела, обновување на колоните на надворешниот клуч во табелата која
реферира според вредностите по подразбирање :
CREATE TRIGGER UpdateDefault_Cust
ON Customers
FOR UPDATE
AS
IF @@ROWCOUNT = 0 RETURN
DECLARE @count int
IF UPDATE ( CustomerID )
BEGIN
UPDATE Sales
SET CustomerID = DEFAULT
FROM Sales s
INNER JOIN deleted d ON s.CustomerID = d.CustomerID
SET @count = @@ROWCOUNT
IF @count > 0
RAISERROR('На %d редот во Sales е зададена вредност по подразбирање на
CustomerID така што резултат од промената во табелата
Customers.', 11, 1, @count)
END
Овој тригер лесно може да се промени така, да колоната која присвојува вредност NULL
наместо DEFAULT да реализира SET NULL
28
5) Каскадно обновување во реферираната табела, т.е. обновува сите
редови во табелата која реферира со соодветните вредности за
надворешен клуч:
CREATE TRIGGER UpdateCascadeCustomers
ON Customers
FOR UPDATE
AS
DECLARE @num_affected int, @customerID int,
@old_customerID int
SET @num_affected = @@ROWCOUNT
IF @num_affected = 0 RETURN
IF UPDATE ( CustomerID )
BEGIN
IF @num_affected = 1
BEGIN
SELECT @customerID = CustomerID
FROM inserted
SELECT @old_customerID = CustomerID
FROM deleted
UPDATE Sales
SET CustomerID = @CustomerID
WHERE CustomerID = @old_customerID
SET @num_affected = @@ROWCOUNT
29
RAISERROR('Каскадното ажурирање во Customers на примарниот клуч од %d на
%d на %d редот во Sales.', 11, 1,@old_customerID, @customerID, @num_affected)
END
ELSE
BEGIN
RAISERROR('Не може да се променат множество вредности на примарниот
клуч во
една конструкција, поради постоење на каскадно
тригер кој се ажурира.', 16, 1)
ROLLBACK TRANSACTION
END
END

• По дефиниција при каскадното обновување се променува соодветниот примарен


клуч кое значи, дека каскадното обновување работи само ако се обновува еден-
единствен ред во реферираната табела. Ако во една конструкција за обновување
се променат множество вредности на примарниот клуч, ќе се изгуби можноста да
се асоциират правилно кон табелата која реферира. Во разгледаниот тригер е
забрането обновување на примарниот клуч да дејствува на повеќе од еден ред
преку проверка на вредноста на функцијата @@ROWCOUNT. Не се препорачува
да се прави масовно обновување на примарниот клуч.
30
Задача 1. Да се создаде тригер, кој забранува продажбата на продукти,
определени во табелата Products како продукти со прекината продажба.

Решение:

CREATE TRIGGER SaleDetailNotDiscontinued


ON SaleDetails
FOR INSERT, UPDATE
AS
IF @@ROWCOUNT = 0 RETURN
IF UPDATE ( ProductID )
BEGIN
IF EXISTS
( SELECT *
FROM Inserted i
INNER JOIN Products p ON i.ProductID = p.ProductID
WHERE p.Discontinued = 1 )
BEGIN
RAISERROR('Продуктот е прекинат со продажба.', 16, 1)
ROLLBACK TRANSACTION
END
END

You might also like