I den här artikeln kommer vi att titta på några alternativ till att använda SQL-markörer som kan hjälpa till att undvika prestandaproblem som orsakas av att använda markörer.
Innan vi diskuterar alternativen, låt oss se över det allmänna konceptet för SQL-markörer.
Snabb översikt över SQL-markörer
SQL-markörer används främst där uppsättningsbaserade operationer inte är tillämpliga och du måste komma åt data och utföra operationer en rad i taget istället för att tillämpa en enda uppsättningsbaserad operation på ett helt objekt (som en tabell eller en uppsättning av tabeller).
Enkel definition
En SQL-markör ger åtkomst till data en rad i taget, vilket ger dig direkt rad-för-rad kontroll över resultatuppsättningen.
Microsoft Definition
Enligt Microsofts dokumentation ger Microsoft SQL Server-satser en komplett resultatuppsättning, men det finns tillfällen då det är bäst att bearbeta det en rad i taget – vilket kan göras genom att öppna en markör på resultatuppsättningen.
Femstegsprocessen för att använda en markör
Processen att använda en SQL-markör kan generellt beskrivas enligt följande:
- Deklarera markör
- Öppna markören
- Hämta rader
- Stäng markör
- Avallokera markör
Viktig anmärkning
Vänligen kom ihåg att, enligt Vaidehi Pandere, är markörer pekare som upptar ditt systemminne – som annars skulle vara reserverade för andra viktiga processer. Det är därför det vanligtvis inte är den bästa idén att gå igenom en stor resultatuppsättning med hjälp av markörer – såvida det inte finns en legitim anledning till att göra det.
För mer detaljerad information om detta, se gärna min artikel Hur man använder SQL-markörer för speciella ändamål.
Exempel på SQL-markör
Först ska vi titta på ett exempel på hur en SQL-markör kan användas för att byta namn på databasobjekt ett efter ett.
För att skapa en SQL-markör vi behöver, låt oss ställa in en exempeldatabas så att vi kan köra våra skript mot den.
Setup Sample Database (UniversityV3)
Kör följande skript för att skapa och fylla i exempeldatabasen UniversityV3 med två tabeller:
-- (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
Skapa en SQL-markör för att byta namn på tabeller (_Backup)
Överväg nu att uppfylla följande specifikation genom att använda en markör:
- Vi måste lägga till "_Backup" till namnen på alla befintliga tabeller i en databas
- Tabeller som redan har "_Backup" i sitt namn ska inte bytas om
Låt oss skapa en SQL-markör för att byta namn på alla tabeller i exempeldatabasen genom att lägga till "_Backup" till varje tabells namn samtidigt som vi ser till att tabeller som innehåller "_Backup" i deras namn inte kommer att döpas om igen genom att köra följande kod:
-- 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
Kör byta namn på skriptet och visa resultat
Tryck nu på F5 i SSMS (SQL Server Management Studio) för att köra skriptet och se resultatet:
Att uppdatera tabellernas namn i SSMS-objektutforskaren visar tydligt att vi framgångsrikt har ändrat dem enligt specifikation.
Låt oss köra skriptet igen genom att trycka på F5 igen och titta på resultaten:
Skapa en SQL-markör för att återställa _Backup Naming
Vi måste också skapa ett skript som använder en SQL-markör för att återställa namnen på de tabeller som vi just har ändrat tillbaka till de ursprungliga – vi gör detta genom att ta bort '_Backup' från deras namn.
Skriptet nedan låter oss göra just det :
-- 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
Kör återställningsskriptet och visa resultat
Att köra skriptet visar att tabellnamnen har återställts:
Dessa var exemplen på några scenarier där det är svårt att undvika att använda SQL-markörer på grund av kravets karaktär. Det är dock fortfarande möjligt att hitta ett alternativt tillvägagångssätt.
SQL-marköralternativ
Det finns två vanligaste alternativ för SQL-markörer, så låt oss titta på var och en av dem i detalj.
Alternativ 1:Tabellvariabler
Ett av dessa alternativ är tabellvariabler.
Tabellvariabler, precis som tabeller, kan lagra flera resultat – men med en viss begränsning. Enligt Microsofts dokumentation är en tabellvariabel en speciell datatyp som används för att lagra en resultatuppsättning för bearbetning vid ett senare tillfälle.
Tänk dock på att tabellvariabler bäst används med små datamängder.
Tabellvariabler kan vara mycket effektiva för småskaliga frågor eftersom de fungerar som lokala variabler och rensas automatiskt när de hamnar utanför räckvidden.
Tabellvariabelstrategi:
Vi kommer att använda tabellvariabler istället för SQL-markörer för att byta namn på alla tabeller från en databas genom att följa dessa steg:
- Deklarera en tabellvariabel
- Lagra tabellnamn och ID i den tabellvariabel vi deklarerade
- Sätt räknaren på 1 och få det totala antalet poster från tabellvariabeln
- Använd en "while"-loop så länge räknaren är mindre än eller lika med det totala antalet poster
- I 'while'-loopen byter vi namn på tabeller en efter en så länge de inte redan har bytt namn och ökar räknaren för varje tabell
Tabellvariabelkod:
Kör följande SQL-skript som skapar och använder en tabellvariabel för att byta namn på tabeller:
-- 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
Kör skriptet och visa resultat
Låt oss nu köra skriptet och kontrollera resultaten:
Alternativ 2:Tillfälliga tabeller
Vi kan också använda temporära tabeller istället för SQL-markörer för att iterera resultatuppsättningen en rad i taget.
Tillfälliga tabeller har använts under lång tid och är ett utmärkt sätt att ersätta markörer för stora datamängder.
Precis som tabellvariabler kan temporära tabeller innehålla resultatuppsättningen så att vi kan utföra nödvändiga operationer genom att bearbeta den med en itererande algoritm som en "while"-loop.
Tillfällig tabellstrategi:
Vi kommer att använda en temporär tabell för att byta namn på alla tabeller i exempeldatabasen genom att följa dessa steg:
- Deklarera en tillfällig tabell
- Lagra tabellnamn och ID i den temporära tabellen som vi just deklarerade
- Sätt räknaren på 1 och få det totala antalet poster från den tillfälliga tabellen
- Använd en "while"-loop så länge räknaren är mindre än eller lika med det totala antalet poster
- Inom "while"-loopen, byt namn på tabeller en efter en så länge de inte redan har bytt namn och öka räknaren för varje tabell
Återställ tabellerna
Vi måste återställa tabellernas namn till deras ursprungliga form genom att ta bort '_Backup' från slutet av deras namn, så vänligen kör återställningsskriptet som vi redan har skrivit och använt ovan så att vi kan använda en annan metod för att byta namn på tabeller.
Tillfällig tabellkod:
Kör följande SQL-skript för att skapa och använda en temporär tabell för att byta namn på alla tabeller i vår databas:
-- 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
Kör skriptet och kontrollera resultatet
Låt oss nu köra skriptet för att se resultaten:
Saker att göra
Nu när du är bekant med alternativ till SQL-markörer – som att använda tabellvariabler och temporära tabeller – försök att göra följande för att bli bekväm med att tillämpa denna kunskap i praktiken:
- Skapa och byt namn på index för alla tabeller i en exempeldatabas – först via en markör och sedan genom att använda alternativa metoder (tabellvariabler och temporära tabeller)
- Återställ namnen på tabellerna från den här artikeln till deras ursprungliga namn med alternativa metoder (temporära tabeller och tabellvariabler)
- Du kan också hänvisa till första exemplet i min artikel Hur man använder SQL-markörer för speciella ändamål och prova att fylla i tabeller med många rader och mäta statistiken och tiden för frågorna för att jämföra den grundläggande markörmetoden med alternativen