Chapter 09 - Designing and Implementing T-SQL Routines
Ky leksion 120 minutesh perfshin faqet 469 - 516 te librit te kursit.
USE TSQL2012;
GO
SET NOCOUNT ON;
GO
Designing and Implementing Stored Procedures
USE TSQL2012;
GO
SELECT orderid, custid, shipperid, orderdate, requireddate, shippeddate
FROM Sales.Orders
WHERE custid = 37
AND orderdate >= '2007-04-01'
AND orderdate < '2007-07-01';
-- This query is limited because it has literal values in the WHERE clause. Let's make the code a little more general by using variables in place of those literals values:
USE TSQL2012;
GO
DECLARE @custid AS INT,
@orderdatefrom AS DATETIME,
@orderdateto AS DATETIME;
SET @custid = 37;
SET @orderdatefrom = '2007-04-01';
SET @orderdateto = '2007-07-01';
SELECT orderid, custid, shipperid, orderdate, requireddate, shippeddate
FROM Sales.Orders
WHERE custid = @custid
AND orderdate >= @orderdatefrom
AND orderdate < @orderdateto;
GO
IF OBJECT_ID('Sales.GetCustomerOrders', 'P') IS NOT NULL
DROP PROC Sales.GetCustomerOrders;
GO
CREATE PROC Sales.GetCustomerOrders
@custid AS INT,
@orderdatefrom AS DATETIME = '19000101',
@orderdateto AS DATETIME = '99991231',
@numrows AS INT = 0 OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SELECT orderid, custid, shipperid, orderdate, requireddate, shippeddate
FROM Sales.Orders
WHERE custid = @custid
AND orderdate >= @orderdatefrom
AND orderdate < @orderdateto;
SET @numrows = @@ROWCOUNT;
RETURN;
END
GO
-- After you execute the above code and create the stored procedure, you can call the stored procedure as follows:
DECLARE @rowsreturned AS INT;
EXEC Sales.GetCustomerOrders
@custid = 37,
@orderdatefrom = '20070401',
@orderdateto = '20070701',
@numrows = @rowsreturned OUTPUT;
SELECT @rowsreturned AS "Rows Returned";
-- Testing for the existence of a stored procedure
IF OBJECT_ID('Sales.GetCustomerOrders', 'P') IS NOT NULL
DROP PROC Sales.GetCustomerOrders;
GO
Stored procedure parameters
CREATE PROC Sales.GetCustomerOrders
@custid AS INT,
@orderdatefrom AS DATETIME = '19000101',
@orderdateto AS DATETIME = '99991231',
@numrows AS INT = 0 OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SELECT orderid, custid, shipperid, orderdate, requireddate, shippeddate
FROM [Sales].[Orders]
WHERE custid = @custid
AND orderdate >= @orderdatefrom
AND orderdate < @orderdateto;
SET @numrows = @@ROWCOUNT;
RETURN;
END
Executing Stored Procedures
EXEC sp_configure;
Input parameters
EXEC Sales.GetCustomerOrders 37, '20070401', '20070701';
EXEC Sales.GetCustomerOrders @custid = 37, @orderdatefrom = '20070401', @orderdateto = '20070701';
EXEC Sales.GetCustomerOrders
@orderdatefrom = '20070401',
@orderdateto = '20070701',
@custid = 37;
GO
EXEC Sales.GetCustomerOrders
@custid = 37;
GO
Output Parameters
CREATE PROC Sales.GetCustomerOrders
@custid AS INT,
@orderdatefrom AS DATETIME = '19000101',
@orderdateto AS DATETIME = '99991231',
@numrows AS INT = 0 OUTPUT
AS <rest of procedure>
DECLARE @rowsreturned AS INT;
EXEC Sales.GetCustomerOrders
@custid = 37,
@orderdatefrom = '20070401',
@orderdateto = '20070701',
@numrows = @rowsreturned;
SELECT @rowsreturned AS 'Rows Returned';
GO
DECLARE @rowsreturned AS INT;
EXEC Sales.GetCustomerOrders
@custid = 37,
@orderdatefrom = '20070401',
@orderdateto = '20070701',
@numrows = @rowsreturned OUTPUT;
SELECT @rowsreturned AS 'Rows Returned';
GO
Branching Logic
-- IF/ELSE
DECLARE @var1 AS INT, @var2 AS INT;
SET @var1 = 1;
SET @var2 = 2;
IF @var1 = @var2
PRINT 'The variables are equal';
ELSE
PRINT 'The variables are not equal';
GO
DECLARE @var1 AS INT, @var2 AS INT;
SET @var1 = 1;
SET @var2 = 1;
IF @var1 = @var2
PRINT 'The variables are equal';
ELSE
PRINT 'The variables are not equal';
PRINT '@var1 does not equal @var2';
GO
DECLARE @var1 AS INT, @var2 AS INT;
SET @var1 = 1;
SET @var2 = 1;
IF @var1 = @var2
BEGIN
PRINT 'The variables are equal';
PRINT '@var1 equals @var2';
END
ELSE
BEGIN
PRINT 'The variables are not equal';
PRINT '@var1 does not equal @var2';
END
GO
While
SET NOCOUNT ON;
DECLARE @count AS INT = 1;
WHILE @count <= 10
BEGIN
PRINT CAST(@count AS NVARCHAR);
SET @count += 1;
END;
SET NOCOUNT ON;
DECLARE @count AS INT = 1;
WHILE @count <= 100
BEGIN
IF @count = 10
BREAK;
IF @count = 5
BEGIN
SET @count += 2;
CONTINUE;
END
PRINT CAST(@count AS NVARCHAR);
SET @count += 1;
END;
DECLARE @categoryid AS INT;
SET @categoryid = (SELECT MIN(categoryid) FROM Production.Categories);
WHILE @categoryid IS NOT NULL
BEGIN
PRINT CAST(@categoryid AS NVARCHAR);
SET @categoryid = (SELECT MIN(categoryid) FROM Production.Categories
WHERE categoryid > @categoryid);
END;
GO
DECLARE @categoryname AS NVARCHAR(15);
SET @categoryname = (SELECT MIN(categoryname) FROM Production.Categories);
WHILE @categoryname IS NOT NULL
BEGIN
PRINT @categoryname;
SET @categoryname = (SELECT MIN(categoryname) FROM Production.Categories
WHERE categoryname > @categoryname);
END;
GO
WAITFOR
WAITFOR DELAY '00:00:20';
WAITFOR TIME '23:46:00';
GOTO
PRINT 'First PRINT statement';
GOTO MyLabel;
PRINT 'Second PRINT statement';
MyLabel:
PRINT 'End';
Stored procedure results
IF OBJECT_ID('Sales.ListSampleResultsSets', 'P') IS NOT NULL
DROP PROC Sales.ListSampleResultsSets;
GO
CREATE PROC Sales.ListSampleResultsSets
AS
BEGIN
SELECT TOP (1) productid, productname, supplierid,
categoryid, unitprice, discontinued
FROM Production.Products;
SELECT TOP (1) orderid, productid, unitprice, qty, discount
FROM Sales.OrderDetails;
END
GO
EXEC Sales.ListSampleResultsSets
Implementing Triggers
AFTER triggers
--CREATE TRIGGER TriggerName
-- ON [dbo].[TableName]
-- FOR DELETE, INSERT, UPDATE
-- AS
-- BEGIN
-- SET NOCOUNT ON
IF OBJECT_ID('Sales.tr_SalesOrderDetailsDML', 'TR') IS NOT NULL
DROP TRIGGER Sales.tr_SalesOrderDetailsDML;
GO
CREATE TRIGGER Sales.tr_SalesOrderDetailsDML
ON Sales.OrderDetails
AFTER DELETE, INSERT, UPDATE
AS
BEGIN
IF @@ROWCOUNT = 0 RETURN; -- Must be 1st statement
SET NOCOUNT ON;
END;
IF OBJECT_ID('Sales.tr_SalesOrderDetailsDML', 'TR') IS NOT NULL
DROP TRIGGER Sales.tr_SalesOrderDetailsDML;
GO
CREATE TRIGGER Sales.tr_SalesOrderDetailsDML
ON Sales.OrderDetails
AFTER DELETE, INSERT, UPDATE
AS
BEGIN
IF @@ROWCOUNT = 0 RETURN;
SET NOCOUNT ON;
SELECT COUNT(*) AS InsertedCount FROM Inserted;
SELECT COUNT(*) AS DeletedCount FROM Deleted;
END;
IF OBJECT_ID('Production.tr_ProductionCategories_categoryname', 'TR') IS NOT NULL DROP TRIGGER Production.tr_ProductionCategories_categoryname;
GO
CREATE TRIGGER Production.tr_ProductionCategories_categoryname
ON Production.Categories
AFTER INSERT, UPDATE
AS
BEGIN
IF @@ROWCOUNT = 0 RETURN;
SET NOCOUNT ON;
IF EXISTS (SELECT COUNT(*)
FROM Inserted AS I
JOIN Production.Categories AS C
ON I.categoryname = C.categoryname
GROUP BY I.categoryname
HAVING COUNT(*) > 1 )
BEGIN
THROW 50000, 'Duplicate category names not allowed', 0;
END;
END;
GO
INSERT INTO Production.Categories (categoryname,description)
VALUES ('TestCategory1', 'Test1 description v1');
UPDATE Production.Categories
SET categoryname = 'Beverages' WHERE categoryname = 'TestCategory1';
DELETE FROM Production.Categories WHERE categoryname = 'TestCategory1';
-- Nested AFTER triggers
EXEC sp_configure 'nested triggers';
INSTEAD OF triggers
IF OBJECT_ID('Production.tr_ProductionCategories_categoryname', 'TR') IS NOT NULL
DROP TRIGGER Production.tr_ProductionCategories_categoryname;
GO
CREATE TRIGGER Production.tr_ProductionCategories_categoryname
ON Production.Categories
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
IF EXISTS (SELECT COUNT(*)
FROM Inserted AS I
JOIN Production.Categories AS C
ON I.categoryname = C.categoryname
GROUP BY I.categoryname
HAVING COUNT(*) > 1 )
BEGIN
THROW 50000, 'Duplicate category names not allowed', 0;
END;
ELSE
INSERT Production.Categories (categoryname, description)
SELECT categoryname, description FROM Inserted;
END;
GO
-- Cleanup
IF OBJECT_ID('Production.tr_ProductionCategories_categoryname', 'TR') IS NOT NULL
DROP TRIGGER Production.tr_ProductionCategories_categoryname;
-- DML Trigger Functions referenced by an INSERT or UPDATE statement. For example,
IF UPDATE(qty)
PRINT 'Column qty affected';
UPDATE Sales.OrderDetails
SET qty = 99
WHERE orderid = 10249 AND productid = 16;
Implementing User-Defined Functions
Scalar UDFs
CREATE FUNCTION dbo.FunctionName
(
@param1 int,
@param2 int
)
RETURNS INT
AS
BEGIN
RETURN @param1 + @param2
END
IF OBJECT_ID('Sales.fn_extension', 'FN') IS NOT NULL
DROP FUNCTION Sales.fn_extension
GO
CREATE FUNCTION Sales.fn_extension
(
@unitprice AS MONEY,
@qty AS INT
)
RETURNS MONEY
AS
BEGIN
RETURN @unitprice * @qty
END;
GO
SELECT Orderid, unitprice, qty, Sales.fn_extension(unitprice, qty) AS extension
FROM Sales.OrderDetails;
SELECT Orderid, unitprice, qty, Sales.fn_extension(unitprice, qty) AS extension
FROM Sales.OrderDetails
WHERE Sales.fn_extension(unitprice, qty) > 1000;
Table-valued UDFs
CREATE FUNCTION dbo.FunctionName
(
@param1 int,
@param2 char(5)
)
RETURNS TABLE AS RETURN
(
SELECT @param1 AS c1,
@param2 AS c2
)
IF OBJECT_ID('Sales.fn_FilteredExtension', 'IF') IS NOT NULL
DROP FUNCTION Sales.fn_FilteredExtension;
GO
CREATE FUNCTION Sales.fn_FilteredExtension
(
@lowqty AS SMALLINT,
@highqty AS SMALLINT
)
RETURNS TABLE AS RETURN
(
SELECT orderid, unitprice, qty
FROM Sales.OrderDetails
WHERE qty BETWEEN @lowqty AND @highqty
);
GO
SELECT orderid, unitprice, qty
FROM Sales.fn_FilteredExtension (10,20);
RETURNS TABLE AS RETURN
(
<SELECT …>
);
Multistatement table-valued UDF
CREATE FUNCTION dbo.FunctionName
(
@param1 int,
@param2 char(5)
)
RETURNS @returntable TABLE
(
c1 int,
c2 char(5)
)
AS
BEGIN
INSERT @returntable
SELECT @param1, @param2
RETURN
END;
GO
IF OBJECT_ID('Sales.fn_FilteredExtension2', 'TF') IS NOT NULL
DROP FUNCTION Sales.fn_FilteredExtension2;
GO
CREATE FUNCTION Sales.fn_FilteredExtension2
(
@lowqty AS SMALLINT,
@highqty AS SMALLINT
)
RETURNS @returntable TABLE
(
orderid INT,
unitprice MONEY,
qty SMALLINT
)
AS
BEGIN
INSERT @returntable
SELECT orderid, unitprice, qty
FROM Sales.OrderDetails
WHERE qty BETWEEN @lowqty AND @highqty
RETURN
END;
GO
-- Now use the function:
SELECT orderid, unitprice, qty
FROM Sales.fn_FilteredExtension2 (10,20);
Ushtrime:
1. Ndertoni nje triger i cili ruan punonjesit e fshire ne nje tabele historik e cila ka te njejtat kolona me tabelen Employees dhe nje LeaveDate, qe mban shenim daten e fshirjes se punonjesit.