sql >> Database >  >> RDS >> Database

SQL-cursors vervangen door alternatieven om prestatieproblemen te voorkomen

In dit artikel zullen we enkele alternatieven bekijken voor het gebruik van SQL-cursors die prestatieproblemen kunnen helpen voorkomen die worden veroorzaakt door het gebruik van cursors.

Laten we, voordat we de alternatieven bespreken, het algemene concept van SQL-cursors bekijken.

Snel overzicht van SQL-cursors

SQL-cursors worden voornamelijk gebruikt waar op sets gebaseerde bewerkingen niet van toepassing zijn en u gegevens moet openen en bewerkingen rij voor rij moet uitvoeren in plaats van een enkele op set gebaseerde bewerking toe te passen op een heel object (zoals een tabel of een set van tabellen).

Eenvoudige definitie

Een SQL-cursor geeft rij voor rij toegang tot gegevens, waardoor u rij-voor-rij controle over de resultatenset hebt.

Microsoft-definitie

Volgens de Microsoft-documentatie produceren Microsoft SQL Server-instructies een volledige resultatenset, maar er zijn momenten waarop het het beste is om deze rij per keer te verwerken - wat kan worden gedaan door een cursor op de resultatenset te openen.

Het 5-stappenproces van het gebruik van een cursor

Het proces van het gebruik van een SQL-cursor kan in het algemeen als volgt worden beschreven:

  1. Cursor declareren
  2. Cursor openen
  3. Rijen ophalen
  4. Cursor sluiten
  5. Cursor toewijzen opheffen

Belangrijke opmerking

Houd er rekening mee dat, volgens Vaidehi Pandere, cursors aanwijzers zijn die uw systeemgeheugen in beslag nemen - dat anders zou zijn gereserveerd voor andere belangrijke processen. Dat is de reden waarom het doorlopen van een grote resultatenset met behulp van cursors meestal niet het beste idee is, tenzij er een legitieme reden is om dat te doen.

Raadpleeg voor meer gedetailleerde informatie hierover mijn artikel SQL-cursors gebruiken voor speciale doeleinden.

SQL-cursorvoorbeeld

Eerst zullen we een voorbeeld bekijken van hoe een SQL-cursor kan worden gebruikt om databaseobjecten één voor één te hernoemen.

Laten we, om een ​​SQL-cursor te maken die we nodig hebben, een voorbeelddatabase opzetten zodat we onze scripts erop kunnen uitvoeren.

Voorbeelddatabase instellen (UniversityV3)

Voer het volgende script uit om de UniversityV3-voorbeelddatabase te maken en te vullen met twee tabellen:

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

Maak een SQL-cursor om tabellen te hernoemen (_Backup)

Overweeg nu om aan de volgende specificatie te voldoen door een cursor te gebruiken:

  1. We moeten '_Backup' toevoegen aan de namen van alle bestaande tabellen in een database
  2. Tabellen die al '_Backup' in hun naam hebben, mogen niet worden hernoemd

Laten we een SQL-cursor maken om alle tabellen in de voorbeelddatabase te hernoemen door '_Backup' toe te voegen aan de naam van elke tabel en er tegelijkertijd voor te zorgen dat tabellen met '_Backup' in hun naam niet opnieuw worden hernoemd door de volgende code uit te voeren:

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

Voer het hernoemingsscript uit en bekijk de resultaten

Druk nu op F5 in SSMS (SQL Server Management Studio) om het script uit te voeren en de resultaten te zien:

Door de namen van de tabellen in de SSMS-objectverkenner te vernieuwen, is duidelijk te zien dat we ze met succes hebben gewijzigd zoals gespecificeerd.

Laten we het script opnieuw uitvoeren door nogmaals op F5 te drukken en de resultaten bekijken:

Een SQL-cursor maken om _Backup-naamgeving te resetten

We moeten ook een script maken dat een SQL-cursor gebruikt om de namen van de tabellen die we zojuist hebben gewijzigd terug te zetten in de oorspronkelijke - we doen dit door '_Backup' uit hun namen te verwijderen.

Met het onderstaande script kunnen we precies dat doen:

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

Voer het resetscript uit en bekijk de resultaten

Als u het script uitvoert, ziet u dat de tabelnamen met succes opnieuw zijn ingesteld:

Dit waren de voorbeelden van enkele scenario's waarin het moeilijk is om het gebruik van SQL-cursors te vermijden vanwege de aard van de vereiste. Het is echter nog steeds mogelijk om een ​​alternatieve benadering te vinden.

SQL-cursoralternatieven

Er zijn twee meest voorkomende alternatieven voor SQL-cursors, dus laten we ze allemaal in detail bekijken.

Alternatief 1:Tabelvariabelen

Een van deze alternatieven zijn tabelvariabelen.

Tabelvariabelen kunnen, net als tabellen, meerdere resultaten opslaan, maar met enige beperking. Volgens de Microsoft-documentatie is een tabelvariabele een speciaal gegevenstype dat wordt gebruikt om een ​​resultatenset op te slaan voor verwerking op een later tijdstip.

Houd er echter rekening mee dat tabelvariabelen het beste kunnen worden gebruikt met kleine gegevenssets.

Tabelvariabelen kunnen zeer efficiënt zijn voor kleinschalige zoekopdrachten, omdat ze werken als lokale variabelen en automatisch worden opgeschoond wanneer ze buiten het bereik vallen.

Tabelvariabele strategie:

We zullen tabelvariabelen gebruiken in plaats van SQL-cursors om alle tabellen uit een database te hernoemen door deze stappen te volgen:

  1. Een tabelvariabele declareren
  2. Sla namen en id's van tabellen op in de tabelvariabele die we hebben gedeclareerd
  3. Zet de teller op 1 en haal het totale aantal records uit de tabelvariabele
  4. Gebruik een 'while'-lus zolang de teller kleiner is dan of gelijk is aan het totale aantal records
  5. Binnen de 'while'-lus zullen we de tabellen één voor één hernoemen, zolang ze nog niet hernoemd zijn en de teller voor elke tafel verhogen

Tabelvariabele code:

Voer het volgende SQL-script uit dat een tabelvariabele maakt en gebruikt om tabellen te hernoemen:

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

Voer het script uit en bekijk de resultaten

Laten we nu het script uitvoeren en de resultaten controleren:

Alternatief 2:Tijdelijke tabellen

We kunnen ook tijdelijke tabellen gebruiken in plaats van SQL-cursors om de resultatenset rij voor rij te herhalen.

Tijdelijke tabellen zijn al heel lang in gebruik en bieden een uitstekende manier om cursors te vervangen voor grote datasets.

Net als tabelvariabelen kunnen tijdelijke tabellen de resultatenset bevatten, zodat we de nodige bewerkingen kunnen uitvoeren door deze te verwerken met een iteratief algoritme zoals een 'while'-lus.

Tijdelijke tabelstrategie:

We zullen een tijdelijke tabel gebruiken om alle tabellen in de voorbeelddatabase te hernoemen door deze stappen te volgen:

  1. Declareer een tijdelijke tafel
  2. Sla namen en id's van tabellen op in de tijdelijke tabel die we zojuist hebben gedeclareerd
  3. Zet de teller op 1 en haal het totale aantal records uit de tijdelijke tabel
  4. Gebruik een 'while'-lus zolang de teller kleiner is dan of gelijk is aan het totale aantal records
  5. Binnen de 'while'-lus, hernoem je de tabellen één voor één zolang ze nog niet hernoemd zijn en verhoog je de teller voor elke tafel

Reset de tabellen

We moeten de namen van de tabellen terugzetten naar hun oorspronkelijke vorm door '_Backup' aan het einde van hun naam te verwijderen, dus voer het resetscript opnieuw uit dat we hierboven al hebben geschreven en gebruikt, zodat we een andere methode kunnen toepassen om tabellen te hernoemen.

Tijdelijke tabelcode:

Voer het volgende SQL-script uit om een ​​tijdelijke tabel te maken en te gebruiken om alle tabellen in onze database te hernoemen:

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

Voer het script uit en controleer de uitvoer

Laten we nu het script uitvoeren om de resultaten te bekijken:

Dingen om te doen

Nu u bekend bent met alternatieven voor SQL-cursors, zoals het gebruik van tabelvariabelen en tijdelijke tabellen, kunt u het volgende proberen om vertrouwd te raken met het toepassen van deze kennis in de praktijk:

  1. Creëer en hernoem indices van alle tabellen in een voorbeelddatabase - eerst via een cursor en vervolgens met behulp van alternatieve methoden (tabelvariabelen en tijdelijke tabellen)
  2. Zet de namen van de tabellen uit dit artikel terug naar hun oorspronkelijke namen met behulp van alternatieve methoden (tijdelijke tabellen en tabelvariabelen)
  3. U kunt ook verwijzen naar de eerste voorbeelden in mijn artikel Hoe u SQL-cursors voor speciale doeleinden gebruikt en probeer tabellen met veel rijen te vullen en de statistieken en tijd voor de query's te meten om de basiscursormethode te vergelijken met de alternatieven

  1. Equivalent van MySQL OP DUPLICATE KEY UPDATE in Sql Server

  2. SQL Server 2000 - Gekoppelde server

  3. Apache Spark:JDBC-verbinding werkt niet

  4. Hoe de sortering van een tabel in MySQL te tonen