I denne artikel vil vi se på nogle alternativer til at bruge SQL-markører, som kan hjælpe med at undgå ydeevneproblemer forårsaget af brug af markører.
Før vi diskuterer alternativerne, lad os gennemgå det generelle koncept for SQL-markører.
Hurtig oversigt over SQL-markører
SQL-markører bruges primært, hvor sæt-baserede operationer ikke er anvendelige, og du er forpligtet til at få adgang til data og udføre operationer én række ad gangen i stedet for at anvende en enkelt sæt-baseret operation på et helt objekt (såsom en tabel eller et sæt af tabeller).
Simpel definition
En SQL-markør giver adgang til data én række ad gangen og giver dig derved direkte række-for-række kontrol over resultatsættet.
Microsoft Definition
Ifølge Microsofts dokumentation producerer Microsoft SQL Server-sætninger et komplet resultatsæt, men der er tidspunkter, hvor det er bedst at behandle det én række ad gangen – hvilket kan gøres ved at åbne en markør på resultatsættet.
5-trins processen med at bruge en markør
Processen med at bruge en SQL-markør kan generelt beskrives som følger:
- Erklær markør
- Åbn markør
- Hent rækker
- Luk markør
- Deallokér markør
Vigtig bemærkning
Vær venligst opmærksom på, at ifølge Vaidehi Pandere er markører pointere, der optager din systemhukommelse – som ellers ville være reserveret til andre vigtige processer. Derfor er det normalt ikke den bedste idé at krydse et stort resultatsæt ved at bruge markører – medmindre der er en legitim grund til at gøre det.
For mere detaljeret information om dette, er du velkommen til at henvise til min artikel Sådan bruger du SQL-markører til særlige formål.
SQL-markøreksempel
Først vil vi se på et eksempel på, hvordan en SQL-markør kan bruges til at omdøbe databaseobjekter én efter én.
For at oprette en SQL-markør, vi har brug for, lad os opsætte en prøvedatabase, så vi kan køre vores scripts mod den.
Opsætning af prøvedatabase (UniversityV3)
Kør følgende script for at oprette og udfylde UniversityV3-eksempeldatabasen med to 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
Opret en SQL-markør for at omdøbe tabeller (_Backup)
Overvej nu at opfylde følgende specifikation ved at bruge en markør:
- Vi skal tilføje '_Backup' til navnene på alle eksisterende tabeller i en database
- Tabeller, der allerede har '_Backup' i deres navn, bør ikke omdøbes
Lad os oprette en SQL-markør for at omdøbe alle tabeller i eksempeldatabasen ved at tilføje '_Backup' til hver tabels navn, samtidig med at vi sikrer, at tabeller, der indeholder '_Backup' i deres navn, ikke omdøbes igen ved at køre følgende kode:
-- 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 omdøbningsscriptet og se resultater
Tryk nu på F5 i SSMS (SQL Server Management Studio) for at køre scriptet og se resultaterne:
Opdatering af tabellernes navne i SSMS-objektudforskeren viser tydeligt, at vi har ændret dem som specificeret.
Lad os køre scriptet igen ved at trykke på F5 igen og se på resultaterne:
Oprettelse af en SQL-markør for at nulstille _Backup-navngivning
Vi skal også oprette et script, der bruger en SQL-markør til at vende navnene på de tabeller, vi lige har ændret, tilbage til de oprindelige - vi gør dette ved at fjerne '_Backup' fra deres navne.
Scriptet nedenfor vil lade os gøre netop 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 Nulstil scriptet og se resultater
Kørsel af scriptet viser, at tabelnavne er blevet nulstillet:
Dette var eksempler på nogle scenarier, hvor det er svært at undgå at bruge SQL-markører på grund af kravets karakter. Det er dog stadig muligt at finde en alternativ tilgang.
SQL-markøralternativer
Der er to mest almindelige alternativer til SQL-markører, så lad os se nærmere på hver enkelt af dem.
Alternativ 1:Tabelvariable
Et af disse alternativer er tabelvariabler.
Tabelvariabler kan ligesom tabeller gemme flere resultater - men med en vis begrænsning. Ifølge Microsoft-dokumentationen er en tabelvariabel en speciel datatype, der bruges til at gemme et resultatsæt til behandling på et senere tidspunkt.
Husk dog, at tabelvariabler bedst bruges med små datasæt.
Tabelvariabler kan være meget effektive til forespørgsler i lille skala, da de fungerer som lokale variabler og ryddes op automatisk, når de kommer uden for scope.
Tabelvariabelstrategi:
Vi vil bruge tabelvariabler i stedet for SQL-markører til at omdøbe alle tabeller fra en database ved at følge disse trin:
- Deklarer en tabelvariabel
- Gem tabelnavne og id'er i den tabelvariabel, vi har erklæret
- Sæt tælleren til 1 og få det samlede antal poster fra tabelvariablen
- Brug en "mens"-løkke, så længe tælleren er mindre end eller lig med det samlede antal poster
- Inde i 'mens'-løkken omdøber vi tabeller én efter én, så længe de ikke allerede er omdøbt og øger tælleren for hver tabel
Tabelvariabelkode:
Kør følgende SQL-script, som opretter og bruger en tabelvariabel til at omdøbe 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 scriptet og se resultater
Lad os nu udføre scriptet og kontrollere resultaterne:
Alternativ 2:Midlertidige tabeller
Vi kan også bruge midlertidige tabeller i stedet for SQL-markører til at gentage resultatsættet en række ad gangen.
Midlertidige tabeller har været i brug i lang tid og er en fremragende måde at erstatte markører på for store datasæt.
Ligesom tabelvariabler kan midlertidige tabeller holde resultatsættet, så vi kan udføre de nødvendige operationer ved at behandle det med en itererende algoritme såsom en 'mens'-løkke.
Midlertidig tabelstrategi:
Vi vil bruge en midlertidig tabel til at omdøbe alle tabeller i eksempeldatabasen ved at følge disse trin:
- Deklarer en midlertidig tabel
- Gem tabelnavne og id'er i den midlertidige tabel, vi lige har erklæret
- Sæt tælleren til 1 og få det samlede antal poster fra den midlertidige tabel
- Brug en "mens"-løkke, så længe tælleren er mindre end eller lig med det samlede antal poster
- Inden for "mens"-løkken skal du omdøbe tabeller én efter én, så længe de ikke allerede er omdøbt, og øge tælleren for hver tabel
Nulstil tabellerne
Vi skal nulstille tabellernes navne til deres oprindelige form ved at slette '_Backup' fra slutningen af deres navne, så kør venligst det nulstillingsscript, vi allerede har skrevet og brugt ovenfor, så vi kan anvende en anden metode til at omdøbe tabeller.
Midlertidig tabelkode:
Kør følgende SQL-script for at oprette og bruge en midlertidig tabel til at omdøbe alle tabeller i vores database:
-- 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 scriptet og tjek outputtet
Lad os nu udføre scriptet for at se resultaterne:
Ting at gøre
Nu hvor du er bekendt med alternativer til SQL-markører – såsom brug af tabelvariabler og midlertidige tabeller – prøv venligst at gøre følgende for at blive fortrolig med at anvende denne viden i praksis:
- Opret og omdøb indekser for alle tabeller i en eksempeldatabase – først via en markør og derefter ved at bruge alternative metoder (tabelvariabler og midlertidige tabeller)
- Gendan navnene på tabellerne fra denne artikel tilbage til deres oprindelige navne ved hjælp af alternative metoder (midlertidige tabeller og tabelvariabler)
- Du kan også henvise til de første eksempler i min artikel Sådan bruger du SQL-markører til specielle formål og prøv at udfylde tabeller med masser af rækker og måle statistikken og tiden for forespørgslerne for at sammenligne den grundlæggende markørmetode med alternativerne