-----------------------------------Прооцедуры----------------------------------- --Какие типы процедур бывают: --• User-defined --• Temporary --• System --Каждая из этих процедур может быть следюущего типа: --• Без параметров --• С одним параметром --• С множеством параметров --Синтаксис user-defined процедуры: --CREATE PROCEDURE procedure_name --AS --sql_statement --Синтаксис вызова (выполнения) хранимой процедуры: --EXEC procedure_name --Синтаксис user-defined процедуры с параметрами: --CREATE PROCEDURE procedure_name @param1 datatype, --... --AS --sql_statement --Синтаксис вызова (выполнения) хранимой процедуры с параметрами: --EXEC procedure_name @param1 = 'London', ... --Пример процедуры без параметров (строки не считаются): CREATE PROCEDURE PersonEmail AS SET NOCOUNT ON --не считать количество строк SELECT P.FirstName, P.LastName, EA.EmailAddress FROM Person.Person AS P INNER JOIN Person.EmailAddress AS EA ON P.BusinessEntityID = EA.BusinessEntityID; --Вызов созданной процедуры: EXEC PersonEmail; --Пример процедуры без параметров (строки считаются): CREATE PROCEDURE PersonEmail2 AS SET NOCOUNT OFF --считать количество строк SELECT P.FirstName, P.LastName, EA.EmailAddress FROM Person.Person AS P INNER JOIN Person.EmailAddress AS EA ON P.BusinessEntityID = EA.BusinessEntityID; --Вызов созданной процедуры: EXEC PersonEmail2; --Пример процедуры c параметрами: CREATE PROCEDURE PersonFirstNameEmail @FirstName VARCHAR(50) AS SET NOCOUNT ON --не считать количество строк SELECT P.FirstName, P.LastName, EA.EmailAddress FROM Person.Person AS P INNER JOIN Person.EmailAddress AS EA ON P.BusinessEntityID = EA.BusinessEntityID WHERE @FirstName = P.FirstName; --Вызываем процедуры передавая ей параметр (вариант 1): EXEC PersonFirstNameEmail @FirstName = 'Kim'; --Вызываем процедуру передавая ей параметр (вариант 2): EXEC PersonFirstNameEmail 'Kim'; --Если вызвать процедуру и не передать ей параметры, то будет ошибка: EXEC PersonFirstNameEmail; --Пример процедуру cо значением параметра по умолчанию: CREATE PROCEDURE PersonFirstNameEmail2 @FirstName VARCHAR(50) = 'Alexandra' AS SET NOCOUNT ON --не считать количество строк SELECT P.FirstName, P.LastName, EA.EmailAddress FROM Person.Person AS P INNER JOIN Person.EmailAddress AS EA ON P.BusinessEntityID = EA.BusinessEntityID WHERE @FirstName = P.FirstName; --Вызываем процедуру передавая ей параметр, мы тем самым переопределяем значение параметра по умолчанию (Alexandra) (вариант 1): EXEC PersonFirstNameEmail2 @FirstName = 'Catherine'; --Вызываем процедуру передавая ей параметр, мы тем самым переопределяем значение параметра по умолчанию (Alexandra) (вариант 2): EXEC PersonFirstNameEmail2 'Catherine'; --Если вызвать процедуру и не передать ей параметры, то будет выведена информация, ипользуя для параметра значение по умолчанию: EXEC PersonFirstNameEmail2; --Синтаксис написания процедуры с входным (input) параметром и выходным (output) параметром --CREATE PROCEDURE procedure_name @param1 datatype, --@param2 datatype OUTPUT, ... --AS --sql_statement --DECLARE @param2 DATATYPE --EXEC procedure_name @param1 = 'London', --@param2 OUTPUT ... --Пример процедуры с входным (input) параметром и выходным (output) параметром: CREATE PROCEDURE PersonCounter @FirstName VARCHAR(50), @Counter INT OUTPUT AS SET NOCOUNT ON SET @Counter = (SELECT COUNT(P.FirstName) FROM Person.Person AS P INNER JOIN Person.EmailAddress AS EA ON P.BusinessEntityID = EA.BusinessEntityID WHERE P.FirstName = @FirstName); DECLARE @Counter INT; EXEC PersonCounter 'Alexandra', @Counter OUTPUT; SELECT @Counter; --Синтаксис написания процедуры с RETURN: --CREATE PROCEDURE procedure_name --AS --sql_statement --RETURN ... --DECLARE @return_value DATATYPE --EXEC @return_value = procedure_name --SELECT @return_value --Пример процедуры с RETURN: CREATE PROCEDURE MyFirstReturningStoredProcedure AS RETURN 12; DECLARE @MyReturnValue INT EXEC @MyReturnValue = MyFirstReturningStoredProcedure SELECT @MyReturnValue --При таком включенном параметре мы можем сравнивать с NULL только способом через IS NULL SET ANSI_NULLS ON --Вариант 1 SELECT * FROM Person.Person WHERE MiddleName = 'NULL'; --Вариант 2 SELECT * FROM Person.Person WHERE MiddleName IS NULL; --При таком включенном параметре у нас двойные кавычки рассматриваются как круглые скобки ([...]) --и могут использоваться для заключения в кавычки имен объектов SQL, таких как имена таблиц, имена столбцов и т. д. SET QUOTED_IDENTIFIER ON UPDATE "Person"."Person" SET "FirstName" = 'Vik' WHERE "MiddleName" = 'N'; --Синтаксис изменения процедуры --ALTER PROCEDURE procedure_name --AS --sql_statement -Синтаксис удаления процедуры --DROP PROCEDURE procedure_name --Правила хорошего тона по удалению процедур: --IF OBJECT_ID ( '', 'P' ) IS NOT NULL -- DROP PROCEDURE ; --GO --Синтаксис переименования процедуры: --EXEC sp_rename '', '' --Пример создания временной процедуры (локальной), которая доступна только в текущей сессии (вкладке): CREATE PROCEDURE #TempProcedure AS SET NOCOUNT ON PRINT 'This is my first Temporary Procedure' --Данная временная процедура (локальная) может быть вызывана только в том окне, где она была первоначально создана: EXEC #TempProcedure --Пример создания временной процедуры (глобальной), которая доступна и в других сессиях (вкладках): CREATE PROCEDURE ##TempProcedure2 AS SET NOCOUNT ON PRINT 'This is my second Temporary Procedure' --Данная временная процедура (глобальная) может быть вызывана в любом окне до момента закрытия Management Studio EXEC ##TempProcedure2 --Создадим хранимую процедуру sp_ChangeCity, которая изменяет все города в таблице Person.Address на верхний регистр CREATE PROCEDURE sp_ChangeCity AS SET NOCOUNT ON --не считать количество строк SELECT UPPER(A.City) FROM Person.Address AS A; --Выполним созданную процедуру EXECUTE sp_ChangeCity; --Создадим хранимую процедуру с именем sp_GetLastName, которая принимает входной параметр с именем EmployeeID --и возвращает фамилию этого сотрудника (мы можете объединить таблицы Employee и Person) CREATE PROCEDURE sp_GetLastName @EmployeeID INT AS SET NOCOUNT ON SELECT E.BusinessEntityID, P.LastName FROM HumanResources.Employee AS E INNER JOIN Person.Person AS P ON P.BusinessEntityID =E.BusinessEntityID WHERE E.BusinessEntityID = @EmployeeID; --Выполним созданную процедуру и передадим значение в наш входной параметр EXECUTE sp_GetLastName @EmployeeID = 211; --Примеры системных хранимых процедур: EXEC sp_help --сообщает информацию об объектах базы данных EXEC sys.sp_tables --сообщает информацию о списке таблиц из базы данных EXEC sp_helptext --используется для создания объекта в несколько строк EXEC sp_depends --используется для получения сведений о зависимом объекте -----------------------------------Вычисляемые столбцы----------------------------------- --Создадим для примера таблицу CREATE TABLE dbo.Products ( ProductID INT IDENTITY (1,1) NOT NULL, QtyAvailable SMALLINT, UnitPrice MONEY, InventoryValue AS QtyAvailable * UnitPrice ); -- Вставим данные в таблицу INSERT INTO dbo.Products (QtyAvailable, UnitPrice) VALUES (25, 2.00), (10, 1.5); -- Выведем строки з нашей таблицы SELECT ProductID, QtyAvailable, UnitPrice, InventoryValue FROM dbo.Products; --Изменим нашу таблицу и добавив еще один вычисляемый столбец ALTER TABLE dbo.Products ADD RetailValue AS (QtyAvailable * UnitPrice * 1.5); -- Выведем все данные з нашей таблицы SELECT * FROM dbo.Products; --Удалим наш вычисляемый столбец ALTER TABLE dbo.Products DROP COLUMN RetailValue; -- И создадим такой же, только изменим нашу формулу ALTER TABLE dbo.Products ADD RetailValue AS (QtyAvailable * UnitPrice * 1.3); --Нельзя принудительно поменять данные вычисляемого столбца (получим ошибку) UPDATE dbo.PersonInfo SET FullName = 'Andrey Stepanov' WHERE FirstName = 'Anatolii'; -----------------------------------Пользовательские функции----------------------------------- --Типы пользовательских функций: --• Скалярные функции --• Табличные функции --• Системные функции --Синтаксис создания скалярной функции --CREATE FUNCTION FunctionName (@Parameter1 DataTypeForParameter1) --RETURNS FunctionDataType --AS -- BEGIN -- --Объявляем здесь возвращаемую переменную -- DECLARE @ResultVariable FunctionDataType -- --Добавляем сюда операторы SQL для вычисления возвращаемого значения -- SELECT @ResultVariable -- = ... -- --Возвращаем результат функции -- RETURN @ResultVariable -- END --GO --cмотрим содержимое нашей таблицы: SELECT * FROM Sales.SalesTerritory; --создаем функцию без параметров CREATE FUNCTION YtdSales() RETURNS MONEY AS BEGIN --Объявляем здесь возвращаемую переменную DECLARE @YtdSales MONEY --Добавляем сюда операторы SQL для вычисления возвращаемого значения SELECT @YtdSales = SUM(SalesYTD) FROM Sales.SalesTerritory --Возвращаем результат функции RETURN @YtdSales END GO --вызываем функцию и сохраняем результат в переменную DECLARE @YtdResults MONEY SELECT @YtdResults = dbo.YtdSales() PRINT @YtdResults --создаем скалярную функцию с параметром CREATE FUNCTION YtdGroup(@Group VARCHAR(50)) RETURNS MONEY AS BEGIN DECLARE @YtdSales MONEY SELECT @YtdSales = SUM(SalesYTD) FROM Sales.SalesTerritory WHERE [Group] = @Group RETURN @YtdSales END GO --вызываем функцию, передавая ей параметр на вход и сохраняем результат в переменную DECLARE @YtdResults MONEY --SELECT @YtdResults = dbo.YtdGroup('North America') SELECT @YtdResults = dbo.YtdGroup('Europe') PRINT @YtdResults -- Cинтаксис создания табличной функции --CREATE FUNCTION FunctionName (@Parameter1 DataTypeForParameter1) --RETURNS TABLE --AS RETURN -- {sql_statement} --END --GO --cмотрим содержимое нашей таблицы: SELECT * FROM Sales.SalesTerritory; --создаем табличную функцию с параметром CREATE FUNCTION TableValuedFunction (@TerritoryID INT) RETURNS TABLE AS RETURN SELECT Name, CountryRegionCode, [Group], SalesYTD FROM Sales.SalesTerritory WHERE TerritoryID = @TerritoryID --вызываем функцию путём выборки данных как с обычной таблицы, при этом передавая ей параметр на входе SELECT * FROM TableValuedFunction(7); -- или моем выбрать только те столбцы, которые нам нужны SELECT Name, [Group] FROM TableValuedFunction(9); -- синтаксис создания табличной функции (multi-statement) --CREATE FUNCTION FunctionName --(@Param1 Data_Type_For_Param1) --RETURNS @Table_Variable_Name TABLE --(Column_1 Data_Type_For_Column1) --AS --BEGIN --{sql_statement} --RETURN --END --GO --В качестве примера ознакомьтесь с уже созданной табличной функцией (multi-statement) под названием ufnGetContactInformation SELECT * FROM dbo.ufnGetContactInformation(12); --Правило хорошего тона относительно удаления функций: -- определяем, существует ли функция в базе данных IF OBJECT_ID (function_name, 'IF') IS NOT NULL -- удаляем функцию DROP FUNCTION function_name GO --Инвертируйте значение столбца «InputString» в таблице ReverseString в обратном порядке, не используя функцию «REVERSE». --Создаем таблицу CREATE TABLE ReverseString( InputString VARCHAR(MAX) ); --Пример работы функции REVERSE SELECT REVERSE('1234567890'); --Добавляем в нашу таблицу значения: INSERT INTO ReverseString VALUES ('1234567890'), ('How are you?'), ('I like SQL course for beginners'); --Проверяем, что значения добавлены: SELECT * FROM ReverseString; --Создаем нашу функцию, которую в итоге будет инвертировать значения в нашей таблице CREATE FUNCTION MyReverseString (@OurString VARCHAR(MAX)) RETURNS VARCHAR(MAX) AS BEGIN DECLARE @index INT, @FinalResult VARCHAR(MAX) SET @FinalResult = '' SET @index = LEN(@OurString) WHILE @index >= 1 BEGIN SET @FinalResult = @FinalResult + SUBSTRING(@OurString, @index, 1) SET @index = @index - 1 END RETURN @FinalResult END GO --Выполните 2 запроса ниже для наглядности,чтобы видеть как работает наша функция: --делаем выборку исходных данных из нашей таблицы: SELECT * FROM ReverseString; --делаем выборку исходных данных из нашей таблицы, но с применением написанной нами функции: SELECT dbo.MyReverseString(InputString) FROM ReverseString; -----------------------------------Транзакции----------------------------------- --Делем выборку из исходной таблицы SELECT * FROM Sales.CreditCard; --Обновляем данные в нашей таблице UPDATE Sales.CreditCard SET ExpYear = 2025 WHERE ExpYear = 2005; --Делаем откат изменений UPDATE Sales.CreditCard SET ExpYear = 2005 WHERE ExpYear = 2025; --Ключевые слова, которые являются важными при работе с транзакциями --BEGIN TRANSACTION --COMMIT TRANSACTION --ROLLBACK TRANSACTION --Пример транзакции BEGIN TRANSACTION UPDATE Sales.CreditCard SET ExpYear = 2025 WHERE ExpYear = 2005 AND CardType <> 'Vista'; SELECT * FROM Sales.CreditCard; -- COMMIT TRANSACTION -- ROLLBACK TRANSACTION GO --Делаем выборку данных из таблицы SELECT * FROM HumanResources.JobCandidate; --Пример еще одной транзакции BEGIN TRANSACTION DeleteJobCandidate DELETE FROM HumanResources.JobCandidate WHERE JobCandidateID = 13; -- COMMIT TRANSACTION DeleteJobCandidate -- ROLLBACK TRANSACTION DeleteJobCandidate --Пример транзакции с точками сохранения, к которым мы можем возвращаться BEGIN TRANSACTION SELECT * FROM HumanResources.JobCandidate; SAVE TRANSACTION SavePoint1; DELETE FROM HumanResources.JobCandidate WHERE JobCandidateID = 12; SAVE TRANSACTION SavePoint2; SELECT * FROM HumanResources.JobCandidate; ROLLBACK TRANSACTION SavePoint1; SELECT * FROM HumanResources.JobCandidate; -----------------------------------Обработка ошибок----------------------------------- --Делаем выборку из нашей таблицы SELECT * FROM Sales.SalesTerritory; --Пример обработки ошибок: DECLARE @ERRORRESULTS VARCHAR(50) BEGIN TRANSACTION INSERT INTO Sales.SalesTerritory (Name, CountryRegionCode, [Group], SalesYTD, SalesLastYear, CostYTD, CostLastYear, rowguid, ModifiedDate) VALUES ('ABCD', 'US', 'NA', 1.00, 1.00, 1.00, 1.00, '43689A10-E30B-497F-B0DE-11DE20267FF', GETDATE()); SET @ERRORRESULTS = @@ERROR IF(@ERRORRESULTS = 0) BEGIN PRINT 'SUCCESS!!!' COMMIT TRANSACTION END ELSE BEGIN PRINT 'STATEMENT FAILED!!!' ROLLBACK TRANSACTION END --Пример работы блока TRY и CATCH BEGIN TRY BEGIN TRANSACTION INSERT INTO Sales.SalesTerritory (Name, CountryRegionCode, [Group], SalesYTD, SalesLastYear, CostYTD, CostLastYear, rowguid, ModifiedDate) VALUES ('ABCD', 'US', 'NA', 1.00, 1.00, 1.00, 1.00, '43689A10-E30B-497F-B0DE-11DE20267FF8', GETDATE()) COMMIT TRANSACTION END TRY BEGIN CATCH PRINT 'CATCH STATEMENT ENTERED' ROLLBACK TRANSACTION END CATCH --Смотрим данные в нашей таблице: SELECT * FROM Sales.SalesTerritory --У нас даже есть доступ к некоторым специальным данным, доступным только внутри оператора CATCH: --ERROR_NUMBER - возвращает внутренний номер ошибки --ERROR_STATE - возвращает информацию об источнике --ERROR_SEVERITY - возвращает информацию обо всем, от информационных ошибок до ошибок, которые может исправить пользователь DBA и т. Д. --ERROR_LINE - возвращает номер строки, на которой произошла ошибка --ERROR_PROCEDURE - возвращает имя хранимой процедуры или функции --ERROR_MESSAGE - возвращает наиболее важную информацию, а именно текст сообщения об ошибке BEGIN TRY SELECT 1/0 AS Error; END TRY BEGIN CATCH SELECT ERROR_NUMBER() AS ErrorNumber, ERROR_STATE() AS ErrorState, ERROR_SEVERITY() AS ErrorSeverity, ERROR_PROCEDURE() AS ErrorProcedure, ERROR_LINE() AS ErrorLine, ERROR_MESSAGE() AS ErrorMessage; END CATCH;