Database
 sql >> Database >  >> RDS >> Database

Come utilizzare i cursori SQL per scopi speciali

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:

  1. Teoria degli insiemi
  2. 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:

  1. Dichiara cursore
  2. Apri cursore
  3. Recupera righe
  4. Chiudi cursore
  5. 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à:

  1. Rilascia il set di risultati attualmente tenuto dal cursore
  2. Libera eventuali blocchi del cursore sulle righe dal cursore
  3. 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:

  1. SQL Server 2008 e versioni successive
  2. 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:

  1. Prova a creare e rinominare gli indici di tutte le tabelle di un database di esempio tramite il cursore.
  2. Prova a ripristinare i nomi originali delle tabelle rinominate in questo articolo utilizzando il cursore.
  3. Prova a popolare le tabelle con molte righe e misura le statistiche e il tempo per le query con e senza il cursore.