In questo articolo vengono descritti i cursori SQL e come utilizzarli per alcuni scopi speciali. Evidenzia l'importanza dei cursori SQL insieme ai loro aspetti negativi.
Non è sempre il caso di utilizzare il cursore SQL nella programmazione di database, ma la loro comprensione concettuale e l'apprendimento di come utilizzarli aiuta molto a capire come eseguire attività eccezionali nella programmazione T-SQL.
Panoramica dei cursori SQL
Esaminiamo alcune nozioni di base sui cursori SQL se non hai familiarità con loro.
Definizione semplice
Un cursore SQL fornisce l'accesso ai dati una riga alla volta, offrendoti così un maggiore controllo (riga per riga) sul set di risultati.
Definizione Microsoft
Secondo la documentazione di Microsoft, le istruzioni di Microsoft SQL Server producono un set di risultati completo, ma ci sono momenti in cui i risultati vengono elaborati meglio una riga alla volta. L'apertura di un cursore su un set di risultati consente di elaborare il set di risultati una riga alla volta.
T-SQL e set di risultati
Poiché sia le definizioni semplici che quelle Microsoft del cursore SQL menzionano un set di risultati, cerchiamo di capire qual è esattamente il set di risultati nel contesto della programmazione di database. Creiamo e popolamo rapidamente la tabella Studenti in un database di esempio UniversityV3 come segue:
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) ); -- (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
Ora seleziona tutte le righe dallo Studente tabella:
-- View Student table data SELECT [StudentId], [Name], [Course], [Marks], [ExamDate] FROM dbo.Student
Questo è il set di risultati che viene restituito come risultato della selezione di tutti i record dallo Studente tabella.
T-SQL e teoria degli insiemi
T-SQL si basa esclusivamente sui seguenti due concetti matematici:
- Teoria degli insiemi
- Logica predicata
La teoria degli insiemi, come indica il nome, è una branca della matematica sugli insiemi che possono anche essere chiamati raccolte di oggetti distinti definiti.
In breve, nella teoria degli insiemi, pensiamo alle cose o agli oggetti nel loro insieme nello stesso modo in cui pensiamo a un singolo elemento.
Ad esempio, lo studente è un insieme di tutti gli studenti distinti definiti, quindi prendiamo uno studente nel suo insieme che è sufficiente per ottenere i dettagli di tutti gli studenti in quell'insieme (tabella).
Fare riferimento al mio articolo The Art of Aggregating Data in SQL from Simple to Sliding Aggregations per ulteriori dettagli.
Cursori e operazioni basate su righe
T-SQL è progettato principalmente per eseguire operazioni basate su insiemi come la selezione di tutti i record da una tabella o l'eliminazione di tutte le righe da una tabella.
In breve, T-SQL è appositamente progettato per funzionare con le tabelle in modo basato su insiemi, il che significa che pensiamo a una tabella nel suo insieme e qualsiasi operazione come selezionare, aggiornare o eliminare viene applicata nel suo insieme alla tabella o a determinati righe che soddisfano i criteri.
Tuttavia, ci sono casi in cui è necessario accedere alle tabelle riga per riga anziché come un unico set di risultati, ed è qui che entrano in azione i cursori.
Secondo Vaidehi Pandere, a volte la logica dell'applicazione deve funzionare con una riga alla volta piuttosto che con tutte le righe contemporaneamente, il che è lo stesso del ciclo (uso di cicli per scorrere) attraverso l'intero set di risultati.
Nozioni di base sui cursori SQL con esempi
Discutiamo ora di più sui cursori SQL.
Prima di tutto, impariamo o rivediamo (coloro che hanno già familiarità con l'uso dei cursori in T-SQL) come utilizzare il cursore in T-SQL.
L'uso del cursore SQL è un processo in cinque fasi espresso come segue:
- Dichiara cursore
- Apri cursore
- Recupera righe
- Chiudi cursore
- Dealloca cursore
Fase 1:dichiara il cursore
Il primo passo è dichiarare il cursore SQL in modo che possa essere utilizzato in seguito.
Il cursore SQL può essere dichiarato come segue:
DECLARE Cursor <Cursor_Name> for <SQL statement>
Fase 2:apri il cursore
Il passo successivo dopo la dichiarazione è aprire il cursore, il che significa popolare il cursore con il set di risultati che è espresso come segue:
Open <Cursor_Name>
Fase 3:recupera le righe
Una volta che il cursore è stato dichiarato e aperto, il passaggio successivo consiste nell'iniziare a recuperare le righe dal cursore SQL una per una in modo da recuperare le righe ottenere la riga successiva disponibile dal cursore SQL:
Fetch Next from <Cursor_Name>
Fase 4:Chiudi il cursore
Una volta che le righe sono state recuperate una per una e manipolate secondo i requisiti, il passaggio successivo consiste nel chiudere il cursore SQL.
La chiusura del cursore SQL esegue tre attività:
- Rilascia il set di risultati attualmente tenuto dal cursore
- Libera eventuali blocchi del cursore sulle righe dal cursore
- Chiude il cursore aperto
La semplice sintassi per chiudere il cursore è la seguente:
Close <Cursor_Name>
Fase 5:Dealloca il cursore
L'ultimo passaggio a questo proposito è di deallocare il cursore che rimuove il riferimento del cursore.
La sintassi è la seguente:
DEALLOCATE <Cursor_Name>
Compatibilità con il cursore SQL
Secondo la documentazione Microsoft, i cursori SQL sono compatibili con le seguenti versioni:
- SQL Server 2008 e versioni successive
- Database SQL di Azure
Esempio cursore SQL 1:
Ora che abbiamo familiarità con i passaggi necessari per implementare il cursore SQL, diamo un'occhiata a un semplice esempio di utilizzo del cursore SQL:
-- Declare Student cursor example 1 USE UniversityV3 GO DECLARE Student_Cursor CURSOR FOR SELECT StudentId ,[Name] FROM dbo.Student; OPEN Student_Cursor FETCH NEXT FROM Student_Cursor WHILE @@FETCH_STATUS = 0 BEGIN FETCH NEXT FROM Student_Cursor END CLOSE Student_Cursor DEALLOCATE Student_Cursor
L'output è il seguente:
Esempio 2 del cursore SQL:
In questo esempio, utilizzeremo due variabili per memorizzare i dati conservati dal cursore mentre si sposta da una riga all'altra in modo da poter visualizzare il set di risultati una riga alla volta visualizzando i valori delle variabili.
-- Declare Student cursor with variables example 2 USE UniversityV3 GO DECLARE @StudentId INT ,@StudentName VARCHAR(40) -- Declare variables to hold row data held by cursor DECLARE Student_Cursor CURSOR FOR SELECT StudentId ,[Name] FROM dbo.Student; OPEN Student_Cursor FETCH NEXT FROM Student_Cursor INTO @StudentId, @StudentName -- Fetch first row and store it into variables WHILE @@FETCH_STATUS = 0 BEGIN PRINT CONCAT(@StudentId,'--', @StudentName) -- Show variables data FETCH NEXT FROM Student_Cursor -- Get next row data into cursor and store it into variables INTO @StudentId, @StudentName END CLOSE Student_Cursor -- Close cursor locks on the rows DEALLOCATE Student_Cursor -- Release cursor reference
Il risultato del codice SQL precedente è il seguente:
Si potrebbe obiettare che possiamo ottenere lo stesso risultato utilizzando un semplice script SQL come segue:
-- Viewing student id and name without SQL cursor SELECT StudentId,Name FROM dbo.Student order by StudentId
In effetti, ci sono alcune attività che richiedono l'utilizzo di cursori SQL nonostante sia sconsigliato utilizzare cursori SQL a causa del loro impatto diretto sulla memoria.
Nota importante
Tieni presente che, secondo Vaidehi Pandere, i cursori sono un insieme di puntatori residenti in memoria, quindi occupano la memoria del tuo sistema che sarebbe altrimenti utilizzata da altri processi importanti; ecco perché attraversare un set di risultati di grandi dimensioni tramite i cursori non è mai una buona idea a meno che non vi sia un motivo legittimo per questo.
Utilizzo dei cursori SQL per scopi speciali
Esamineremo alcuni scopi speciali per i quali è possibile utilizzare i cursori SQL.
Test della memoria del server di database
Poiché i cursori SQL hanno un forte impatto sulla memoria di sistema, sono buoni candidati per replicare scenari in cui è necessario esaminare l'utilizzo eccessivo della memoria da parte di diverse stored procedure o script SQL ad hoc.
Un modo semplice per capirlo è fare clic sul pulsante delle statistiche del client sulla barra degli strumenti (o premere Maiusc+Alt+S) in SSMS (SQL Server Management Studio) ed eseguire una semplice query senza cursore:
Ora esegui la query con il cursore usando le variabili (SQL Cursor Esempio 2):
Ora annota le differenze:
Numero di istruzioni SELECT senza cursore:1
Numero di istruzioni SELECT con il cursore:7
Numero di roundtrip del server senza cursore:1
Numero di roundtrip del server con il cursore:2
Tempo di elaborazione del cliente senza cursore:1
Tempo di elaborazione del cliente con il cursore:8
Tempo totale di esecuzione senza cursore:1
Tempo totale di esecuzione con il cursore:38
Tempo di attesa per le risposte del server senza cursore:0
Tempo di attesa per le risposte del server con il cursore:30
In breve, eseguire la query senza il cursore che restituisce solo 5 righe esegue la stessa query 6-7 volte con il cursore.
Ora puoi immaginare quanto sia facile replicare l'impatto della memoria utilizzando i cursori, tuttavia, questa non è sempre la cosa migliore da fare.
Attività di manipolazione di oggetti di database in blocco
C'è un'altra area in cui i cursori SQL possono essere utili ed è quando dobbiamo eseguire un'operazione di massa su database o oggetti di database.
Per capirlo, per prima cosa, dobbiamo creare la tabella del corso e popolarla in UniversityV3 database come segue:
-- Create Course table 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) ); -- 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
Supponiamo ora di voler rinominare tutte le tabelle esistenti in UniversityV3 database come VECCHIE tabelle.
Ciò richiede l'iterazione del cursore su tutte le tabelle una per una in modo che possano essere rinominate.
Il codice seguente fa il lavoro:
-- Declare Student cursor to rename all the tables as old 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 SET @[email protected]+'_OLD' -- Add _OLD to exsiting name of the table EXEC sp_rename @TableName,@NewTableName -- Rename table as OLD table FETCH NEXT FROM Student_Cursor -- Get next row data into cursor and store it into variables INTO @TableName END CLOSE Student_Cursor -- Close cursor locks on the rows DEALLOCATE Student_Cursor -- Release cursor reference
Congratulazioni, hai rinominato con successo tutte le tabelle esistenti utilizzando il cursore SQL.
Cose da fare
Ora che hai familiarità con l'uso del cursore SQL, prova le seguenti cose:
- Prova a creare e rinominare gli indici di tutte le tabelle di un database di esempio tramite il cursore.
- Prova a ripristinare i nomi originali delle tabelle rinominate in questo articolo utilizzando il cursore.
- Prova a popolare le tabelle con molte righe e misura le statistiche e il tempo per le query con e senza il cursore.