sql >> Databáze >  >> RDS >> Database

Nahrazení kurzorů SQL alternativami, abyste se vyhnuli problémům s výkonem

V tomto článku se podíváme na některé alternativy k používání kurzorů SQL, které mohou pomoci vyhnout se problémům s výkonem způsobeným používáním kurzorů.

Než probereme alternativy, zopakujme si obecný koncept kurzorů SQL.

Rychlý přehled kurzorů SQL

Kurzory SQL se primárně používají tam, kde operace založené na množině nelze použít a musíte přistupovat k datům a provádět operace jeden řádek po druhém, místo abyste na celý objekt (jako je tabulka nebo sada tabulky).

Jednoduchá definice

Kurzor SQL poskytuje přístup k datům jeden řádek po druhém, čímž vám poskytuje přímou kontrolu po řádcích nad sadou výsledků.

Definice společnosti Microsoft

Podle dokumentace Microsoftu produkují příkazy Microsoft SQL Server kompletní sadu výsledků, ale jsou chvíle, kdy je nejlepší je zpracovat po jednom řádku – což lze provést otevřením kurzoru na výsledné sadě.

Proces použití kurzoru v 5 krocích

Proces použití SQL kurzoru lze obecně popsat takto:

  1. Deklarovat kurzor
  2. Otevřít kurzor
  3. Načíst řádky
  4. Zavřít kurzor
  5. Přidělit kurzor

Důležitá poznámka

Mějte prosím na paměti, že podle Vaidehi Pandere jsou kurzory ukazatele, které zabírají vaši systémovou paměť – která by jinak byla vyhrazena pro jiné důležité procesy. To je důvod, proč procházení velké sady výsledků pomocí kurzorů obvykle není nejlepší nápad – pokud k tomu neexistuje legitimní důvod.

Podrobnější informace o tom najdete v mém článku Jak používat SQL kurzory pro speciální účely.

Příklad SQL kurzoru

Nejprve se podíváme na příklad toho, jak lze SQL kurzor použít k přejmenování databázových objektů jeden po druhém.

Abychom vytvořili kurzor SQL, který potřebujeme, nastavíme ukázkovou databázi, abychom proti ní mohli spouštět naše skripty.

Nastavení ukázkové databáze (UniversityV3)

Spusťte následující skript k vytvoření a naplnění ukázkové databáze UniversityV3 dvěma tabulkami:

-- (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

Vytvořte kurzor SQL pro přejmenování tabulek (_Backup)

Nyní zvažte splnění následující specifikace pomocí kurzoru:

  1. Musíme přidat ‚_Backup‘ k názvům všech existujících tabulek v databázi
  2. Tabulky, které již mají v názvu „_Backup“, by neměly být přejmenovány

Vytvořme SQL kurzor pro přejmenování všech tabulek ve vzorové databázi přidáním ‚_Backup‘ k názvu každé tabulky a zároveň zajistíme, že tabulky obsahující ‚_Backup‘ ve svém názvu nebudou znovu přejmenovány spuštěním následujícího kódu:

-- 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

Spusťte skript pro přejmenování a zobrazte výsledky

Nyní stisknutím klávesy F5 v SSMS (SQL Server Management Studio) spusťte skript a zobrazte výsledky:

Obnovení názvů tabulek v průzkumníku objektů SSMS jasně ukazuje, že jsme je úspěšně změnili, jak je uvedeno.

Spusťte skript znovu opětovným stisknutím F5 a podívejte se na výsledky:

Vytvoření kurzoru SQL pro resetování pojmenování _Backup

Potřebujeme také vytvořit skript, který pomocí SQL kurzoru vrátí názvy tabulek, které jsme právě změnili, zpět na původní – provedeme to odstraněním „_Backup“ z jejich názvů.

Skript níže nám to umožní:

-- 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

Spusťte skript Resetovat a zobrazte výsledky

Spuštění skriptu ukazuje, že názvy tabulek byly úspěšně resetovány:

Toto byly příklady některých scénářů, ve kterých je obtížné se vyhnout použití kurzorů SQL kvůli povaze požadavku. Stále je však možné najít alternativní přístup.

Alternativy kurzoru SQL

Pro kurzory SQL existují dvě nejběžnější alternativy, takže se na každou z nich podíváme podrobně.

Alternativa 1:Proměnné tabulky

Jednou z těchto alternativ jsou proměnné tabulky.

Proměnné tabulky, stejně jako tabulky, mohou ukládat více výsledků – ale s určitým omezením. Podle dokumentace společnosti Microsoft je proměnná tabulky speciální datový typ používaný k uložení sady výsledků pro pozdější zpracování.

Mějte však na paměti, že tabulkové proměnné se nejlépe používají s malými soubory dat.

Proměnné tabulky mohou být velmi efektivní pro dotazy v malém měřítku, protože fungují jako místní proměnné a po opuštění rozsahu se automaticky vyčistí.

Strategie proměnných tabulek:

K přejmenování všech tabulek z databáze použijeme tabulkové proměnné místo SQL kurzorů podle následujících kroků:

  1. Deklarujte proměnnou tabulky
  2. Uložte názvy a ID tabulek do proměnné tabulky, kterou jsme deklarovali
  3. Nastavte počítadlo na 1 a získejte celkový počet záznamů z proměnné tabulky
  4. Používejte cyklus „while“, pokud je počítadlo menší nebo rovno celkovému počtu záznamů.
  5. Uvnitř cyklu „while“ přejmenujeme tabulky jednu po druhé, pokud již nejsou přejmenovány, a zvýšíme počítadlo pro každou tabulku.

Kód proměnné tabulky:

Spusťte následující skript SQL, který vytvoří a použije proměnnou tabulky k přejmenování tabulek:

-- 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

Spusťte skript a zobrazte výsledky

Nyní spustíme skript a zkontrolujeme výsledky:

Alternativa 2:Dočasné tabulky

Můžeme také použít dočasné tabulky místo SQL kurzorů k iteraci sady výsledků jeden řádek po druhém.

Dočasné tabulky se používají již dlouhou dobu a poskytují vynikající způsob, jak nahradit kurzory u velkých souborů dat.

Stejně jako proměnné tabulky mohou dočasné tabulky obsahovat sadu výsledků, takže můžeme provádět potřebné operace jejím zpracováním pomocí iteračního algoritmu, jako je smyčka „while“.

Strategie dočasné tabulky:

K přejmenování všech tabulek v ukázkové databázi použijeme dočasnou tabulku podle následujících kroků:

  1. Deklarujte dočasnou tabulku
  2. Uložte názvy a ID tabulek do dočasné tabulky, kterou jsme právě deklarovali
  3. Nastavte počítadlo na 1 a získejte celkový počet záznamů z dočasné tabulky
  4. Používejte cyklus „while“, pokud je počítadlo menší nebo rovno celkovému počtu záznamů.
  5. V rámci cyklu „while“ přejmenovávejte tabulky jednu po druhé, pokud již nejsou přejmenovány, a zvyšujte počítadlo pro každou tabulku.

Resetovat tabulky

Potřebujeme obnovit názvy tabulek do jejich původní podoby odstraněním '_Backup' z konce jejich názvů, takže prosím znovu spusťte resetovací skript, který jsme již napsali a použili výše, abychom mohli použít jinou metodu přejmenování tabulek.

Dočasný kód tabulky:

Spuštěním následujícího skriptu SQL vytvořte a použijte dočasnou tabulku k přejmenování všech tabulek v naší databázi:

-- 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

Spusťte skript a zkontrolujte výstup

Nyní spustíme skript a zobrazíme výsledky:

Co dělat

Nyní, když jste obeznámeni s alternativami ke kurzorům SQL – jako je používání proměnných tabulek a dočasných tabulek – zkuste provést následující kroky, abyste se s aplikací těchto znalostí v praxi pohodlně seznámili:

  1. Vytvářejte a přejmenovávejte indexy všech tabulek ve vzorové databázi – nejprve pomocí kurzoru a poté pomocí alternativních metod (tabulkové proměnné a dočasné tabulky)
  2. Vraťte názvy tabulek z tohoto článku zpět na jejich původní názvy pomocí alternativních metod (dočasné tabulky a proměnné tabulky)
  3. Můžete se také odkázat na první příklady v mém článku Jak používat kurzory SQL pro speciální účely a zkusit naplnit tabulky velkým množstvím řádků a měřit statistiky a čas pro dotazy, abyste mohli porovnat základní metodu kurzoru s alternativami.

  1. 2ndQuadrant na PostgresConf USA 2018

  2. Připojení k Vertica v IRI Workbench

  3. Jak vypustit tabulku v SQL

  4. Úvod do referenčních kurzorů PL/SQL v databázi Oracle