Database
 sql >> база данни >  >> RDS >> Database

Замяна на SQL курсори с алтернативи за избягване на проблеми с производителността

В тази статия ще разгледаме някои алтернативи на използването на SQL курсори, които могат да помогнат за избягване на проблеми с производителността, причинени от използването на курсори.

Преди да обсъдим алтернативите, нека прегледаме общата концепция за SQL курсори.

Бърз преглед на SQL курсорите

SQL курсорите се използват предимно там, където операциите, базирани на набор, не са приложими и от вас се изисква да осъществявате достъп до данни и да изпълнявате операции един ред в даден момент, вместо да прилагате една базирана на набор операция към цял обект (като таблица или набор от таблици).

Проста дефиниция

SQL курсорът предоставя достъп до данни един ред по ред, като по този начин ви дава директен контрол ред по ред върху набора от резултати.

Определение на Microsoft

Според документацията на Microsoft, операторите на Microsoft SQL Server произвеждат пълен набор от резултати, но има моменти, когато е най-добре да се обработват ред по ред – което може да стане чрез отваряне на курсор върху набора от резултати.

Процесът от 5 стъпки за използване на курсор

Процесът на използване на SQL курсор може най-общо да бъде описан по следния начин:

  1. Деклариране на курсора
  2. Отворете курсора
  3. Извличане на редове
  4. Затворете курсора
  5. Освобождаване на курсора

Важна забележка

Моля, имайте предвид, че според Vaidehi Pandere курсорите са указатели, които заемат вашата системна памет – която иначе би била запазена за други важни процеси. Ето защо преминаването на голям набор от резултати с помощта на курсори обикновено не е най-добрата идея – освен ако няма основателна причина за това.

За по-подробна информация относно това, не се колебайте да се обърнете към моята статия Как да използвам SQL курсори за специални цели.

Пример за SQL курсор

Първо, ще разгледаме пример за това как SQL курсорът може да се използва за преименуване на обекти на база данни един по един.

За да създадем SQL курсор, от който се нуждаем, нека настроим примерна база данни, така че да можем да изпълняваме нашите скриптове срещу нея.

Настройте примерна база данни (UniversityV3)

Изпълнете следния скрипт, за да създадете и попълните примерната база данни на UniversityV3 с две таблици:

-- (1) Create UniversityV3 sample database

CREATE DATABASE UniversityV3;

GO

USE UniversityV3

-- (2) Create Course table

IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES T WHERE T.TABLE_NAME='Course') 

DROP TABLE dbo.Course 

CREATE TABLE [dbo].[Course] (

    [CourseId] INT           IDENTITY (1, 1) NOT NULL,

    [Name]     VARCHAR (30)  NOT NULL,

    [Detail]   VARCHAR (200) NULL,

    CONSTRAINT [PK_Course] PRIMARY KEY CLUSTERED ([CourseId] ASC)

);

-- (3) Create Student table

IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES T WHERE T.TABLE_NAME='Student') 

DROP TABLE dbo.Student 

CREATE TABLE [dbo].[Student] (

    [StudentId] INT           IDENTITY (1, 1) NOT NULL,

    [Name]      VARCHAR (30)  NULL,

    [Course]    VARCHAR (30)  NULL,

    [Marks]     INT           NULL,

    [ExamDate]  DATETIME2 (7) NULL,

    CONSTRAINT [PK_Student] PRIMARY KEY CLUSTERED ([StudentId] ASC)

);

-- (4) Populate Course table

SET IDENTITY_INSERT [dbo].[Course] ON

INSERT INTO [dbo].[Course] ([CourseId], [Name], [Detail]) VALUES (1, N'DevOps for Databases', N'This is about DevOps for Databases')

INSERT INTO [dbo].[Course] ([CourseId], [Name], [Detail]) VALUES (2, N'Power BI Fundamentals', N'This is about Power BI Fundamentals')

INSERT INTO [dbo].[Course] ([CourseId], [Name], [Detail]) VALUES (3, N'T-SQL Programming', N'About T-SQL Programming')

INSERT INTO [dbo].[Course] ([CourseId], [Name], [Detail]) VALUES (4, N'Tabular Data Modeling', N'This is about Tabular Data Modeling')

INSERT INTO [dbo].[Course] ([CourseId], [Name], [Detail]) VALUES (5, N'Analysis Services Fundamentals', N'This is about Analysis Services Fundamentals')

SET IDENTITY_INSERT [dbo].[Course] OFF



-- (5) Populate Student table

SET IDENTITY_INSERT [dbo].[Student] ON

INSERT INTO [dbo].[Student] ([StudentId], [Name], [Course], [Marks], [ExamDate]) VALUES (1, N'Asif', N'Database Management System', 80, N'2016-01-01 00:00:00')

INSERT INTO [dbo].[Student] ([StudentId], [Name], [Course], [Marks], [ExamDate]) VALUES (2, N'Peter', N'Database Management System', 85, N'2016-01-01 00:00:00')

INSERT INTO [dbo].[Student] ([StudentId], [Name], [Course], [Marks], [ExamDate]) VALUES (3, N'Sam', N'Database Management System', 85, N'2016-01-01 00:00:00')

INSERT INTO [dbo].[Student] ([StudentId], [Name], [Course], [Marks], [ExamDate]) VALUES (4, N'Adil', N'Database Management System', 85, N'2016-01-01 00:00:00')

INSERT INTO [dbo].[Student] ([StudentId], [Name], [Course], [Marks], [ExamDate]) VALUES (5, N'Naveed', N'Database Management System', 90, N'2016-01-01 00:00:00')

SET IDENTITY_INSERT [dbo].[Student] OFF

Създайте SQL курсор за преименуване на таблици (_Backup)

Сега помислете за изпълнението на следната спецификация, като използвате курсор:

  1. Трябва да добавим „_Backup“ към имената на всички съществуващи таблици в база данни
  2. Таблици, които вече имат „_Backup“ в името си, не трябва да се преименуват

Нека създадем SQL курсор, за да преименуваме всички таблици в примерната база данни, като добавим „_Backup“ към името на всяка таблица, като същевременно гарантираме, че таблиците, съдържащи „_Backup“ в името си, няма да бъдат преименувани отново, като изпълните следния код:

-- Declaring the Student cursor to rename all tables by adding ‘_backup’ to their names and also making sure that all tables that are already named correctly will be skipped:

USE UniversityV3
GO

DECLARE @TableName VARCHAR(50) -- Existing table name
       ,@NewTableName VARCHAR(50) -- New table name

DECLARE Student_Cursor CURSOR FOR SELECT T.TABLE_NAME FROM INFORMATION_SCHEMA.TABLES T;

OPEN Student_Cursor

FETCH NEXT FROM Student_Cursor INTO @TableName

WHILE @@FETCH_STATUS = 0

BEGIN

IF RIGHT(@TableName,6)<>'Backup' -- If Backup table does not exist then rename the table

BEGIN

SET @[email protected]+'_Backup' -- Add _Backup to the table’s current name

EXEC sp_rename @TableName,@NewTableName -- Rename table as OLD table

END

ELSE

PRINT 'Backup table name already exists: '[email protected]

FETCH NEXT FROM Student_Cursor -- Get next row data into cursor and store it in variables

INTO @TableName

END

CLOSE Student_Cursor -- Close cursor locks on the rows

DEALLOCATE Student_Cursor -- Release cursor reference

Изпълнете скрипта за преименуване и прегледайте резултатите

Сега натиснете F5 в SSMS (SQL Server Management Studio), за да стартирате скрипта и да видите резултатите:

Обновяването на имената на таблиците в изследователя на SSMS обекти ясно показва, че сме ги променили успешно, както е посочено.

Нека стартираме скрипта отново, като натиснете отново F5 и разгледаме резултатите:

Създаване на SQL курсор за нулиране на _Резервно именуване

Също така трябва да създадем скрипт, който използва SQL курсор, за да върне имената на таблиците, които току-що променихме, обратно към първоначалните – ще направим това, като премахнем „_Backup“ от имената им.

Скриптът по-долу ще ни позволи да направим точно това :

-- Declare the Student cursor to reset tables names _backup to their original forms by removing ‘_backup’

USE UniversityV3

GO

DECLARE @TableName VARCHAR(50) -- Existing table name
       ,@NewTableName VARCHAR(50) -- New table name

DECLARE Student_Cursor CURSOR FOR SELECT T.TABLE_NAME FROM INFORMATION_SCHEMA.TABLES T;

OPEN Student_Cursor

FETCH NEXT FROM Student_Cursor INTO @TableName

WHILE @@FETCH_STATUS = 0

BEGIN

IF RIGHT(@TableName,6)='Backup' -- If Backup table name exists then reset (rename) it

BEGIN

SET @NewTableName=SUBSTRING(@TableName,1,LEN(@TableName)-7) -- Remove _Backup from the table name

EXEC sp_rename @TableName,@NewTableName -- Rename table 

END

ELSE

PRINT 'Backup table name already reset: '[email protected]

FETCH NEXT FROM Student_Cursor – Get the data of the next row into cursor and store it in variables

INTO @TableName

END

CLOSE Student_Cursor -- Close cursor locks on the rows

DEALLOCATE Student_Cursor -- Release cursor reference

Изпълнете скрипта за нулиране и прегледайте резултатите

Изпълнението на скрипта показва, че имената на таблици са били успешно нулирани:

Това бяха примерите за някои сценарии, при които е трудно да се избегне използването на SQL курсори поради естеството на изискването. Все пак е възможно да се намери алтернативен подход.

Алтернативи на SQL курсора

Има две най-често срещани алтернативи за SQL курсори, така че нека разгледаме всеки един от тях подробно.

Алтернатива 1:Таблица променливи

Една от тези алтернативи са табличните променливи.

Променливите в таблицата, точно като таблиците, могат да съхраняват множество резултати - но с известно ограничение. Според документацията на Microsoft променливата на таблицата е специален тип данни, използван за съхраняване на набор от резултати за обработка по-късно.

Все пак, имайте предвид, че променливите в таблицата се използват най-добре с малки набори от данни.

Табличните променливи могат да бъдат много ефективни за заявки в малък мащаб, тъй като работят като локални променливи и се почистват автоматично при излизане от обхвата.

Стратегия за променлива в таблицата:

Ще използваме променливи на таблица вместо SQL курсори, за да преименуваме всички таблици от база данни, като изпълним следните стъпки:

  1. Декларирайте променлива на таблица
  2. Съхранявайте имената и идентификаторите на таблици в декларираната от нас променлива на таблица
  3. Задайте брояча на 1 и получете общия брой записи от променливата на таблицата
  4. Използвайте цикъл „while“, стига броячът да е по-малък или равен на общия брой записи
  5. В рамките на цикъла „while“ ще преименуваме таблици една по една, стига вече да не са преименувани и ще увеличаваме брояча за всяка таблица

Код на променлива в таблицата:

Изпълнете следния SQL скрипт, който създава и използва променлива на таблица за преименуване на таблици:

-- Declare Student Table Variable to rename all tables by adding ‘_backup’ t their name and also making sure that already renamed tables are skipped

USE UniversityV3

GO

DECLARE @TableName VARCHAR(50) -- Existing table name
       ,@NewTableName VARCHAR(50) -- New table name

DECLARE @StudentTableVar TABLE -- Declaring a table variable to store tables names
(
TableId INT,

TableName VARCHAR(40))

INSERT INTO @StudentTableVar -- insert tables names into the table variable 

SELECT ROW_NUMBER() OVER(ORDER BY T.TABLE_NAME),T.TABLE_NAME FROM INFORMATION_SCHEMA.TABLES T

DECLARE @TotalRows INT=(SELECT COUNT(*) FROM @StudentTableVar),@i INT=1 -- Get total rows and set counter to 1

WHILE @i<[email protected] -- begin as long as i (counter) is less than or equal to the total number of records

BEGIN -- ‘While’ loop begins here

SELECT @TableName=TableName from @StudentTableVar WHERE [email protected]

IF RIGHT(@TableName,6)<>'Backup' -- If a Backup table does not exist, then rename the table

BEGIN

SET @[email protected]+'_Backup' -- Add _Backup to the table’s current name

EXEC sp_rename @TableName,@NewTableName -- Rename the table as OLD table

END

ELSE

PRINT 'Backup table name already exists: '[email protected]

SET @[email protected]+1

END -- 'While' loop ends here

Изпълнете скрипта и прегледайте резултатите

Сега нека изпълним скрипта и да проверим резултатите:

Алтернатива 2:Временни таблици

Можем също така да използваме временни таблици вместо SQL курсори, за да повторим набора от резултати един ред наведнъж.

Временните таблици се използват от дълго време и осигуряват отличен начин за замяна на курсори за големи набори от данни.

Точно като променливите на таблицата, временните таблици могат да държат набора от резултати, така че да можем да извършим необходимите операции, като го обработваме с повторяващ се алгоритъм, като например цикъл „while“.

Стратегия за временна таблица:

Ще използваме временна таблица, за да преименуваме всички таблици в примерната база данни, като изпълним следните стъпки:

  1. Декларирайте временна таблица
  2. Съхранявайте имената и идентификаторите на таблици във временната таблица, която току-що декларирахме
  3. Задайте брояча на 1 и получете общия брой записи от временната таблица
  4. Използвайте цикъл „while“, стига броячът да е по-малък или равен на общия брой записи
  5. В рамките на цикъла „while“ преименувайте таблици една по една, стига да не са преименувани и увеличете брояча за всяка таблица

Нулирайте таблиците

Трябва да нулираме имената на таблиците до първоначалния им вид, като изтрием „_Backup“ от края на имената им, така че, моля, изпълнете отново скрипта за нулиране, който вече сме написали и използвахме по-горе, за да можем да приложим друг метод за преименуване на таблици.

Код на временна таблица:

Изпълнете следния SQL скрипт, за да създадете и използвате временна таблица за преименуване на всички таблици в нашата база данни:

-- Declare the Student Temporary Table to rename all tables by adding ‘_backup’ to their names while also making sure that already renamed tables are skipped

USE UniversityV3

GO

DECLARE @TableName VARCHAR(50) -- Existing table name
       ,@NewTableName VARCHAR(50) -- New table name

CREATE TABLE #Student -- Declaring a temporary table

(
TableId INT,
TableName VARCHAR(40)
)

INSERT INTO #Student -- insert tables names into the temporary table

SELECT ROW_NUMBER() OVER(ORDER BY T.TABLE_NAME),T.TABLE_NAME FROM INFORMATION_SCHEMA.TABLES T

DECLARE @TotalRows INT=(SELECT COUNT(*) FROM #Student),@i INT=1 -- Get the total amount of rows and set the counter to 1

WHILE @i<[email protected] -- begin as long as i (counter) is less than or equal to the total number of records

BEGIN -- ‘While’ loop begins here

SELECT @TableName=TableName from #Student WHERE [email protected]

IF RIGHT(@TableName,6)<>'Backup' -- If a Backup table does not exist, then rename the table

BEGIN

SET @[email protected]+'_Backup' -- Add ‘_Backup’ to the table’s current name

EXEC sp_rename @TableName,@NewTableName -- Rename the table as OLD table

END

ELSE

PRINT 'Backup table name already exists: '[email protected]

SET @[email protected]+1

END -- While loop ends here

DROP TABLE #Student

Изпълнете скрипта и проверете изхода

Сега нека изпълним скрипта, за да видите резултатите:

Неща за правене

Сега, след като сте запознати с алтернативите на SQL курсорите – като например използването на променливи на таблици и временни таблици – моля, опитайте да направите следните неща, за да се чувствате комфортно с прилагането на тези знания на практика:

  1. Създавайте и преименувайте индекси на всички таблици в примерна база данни – първо чрез курсор, а след това с помощта на алтернативни методи (променливи на таблицата и временни таблици)
  2. Върнете имената на таблиците от тази статия обратно към първоначалните им имена, като използвате алтернативни методи (временни таблици и променливи в таблицата)
  3. Можете също да се обърнете към първия пример(и) в моята статия Как да използвате SQL курсори за специални цели и да опитате да попълните таблици с много редове и да измерите статистиката и времето за заявките, за да сравните основния метод на курсора с алтернативите

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Инкрементално маскиране и картографиране на данни:Откриване на промени и актуализиране...

  2. Стартирането на базата данни на RAC се проваля с грешка ORA-12547

  3. SQL CREATE TABLE за начинаещи

  4. Лоши навици:Преброяване на редовете по трудния начин

  5. Как лесно да разположите TimescaleDB