Ogni applicazione basata su database raggiunge prima o poi un punto in cui le query SQL sparse nel codice dell'applicazione diventano un problema di manutenzione. La stessa join complessa compare in tre servizi diversi. La logica di business che dovrebbe risiedere nel livello del database si infiltra nell'applicazione. Le policy di sicurezza che dovrebbero essere applicate a livello dei dati vengono invece applicate in modo incoerente dal codice dell'applicazione, che può essere aggirato. Le stored procedure risolvono questo tipo di problema spostando la logica SQL riutilizzabile, sensibile alla sicurezza e critica per le prestazioni nel database, dove può essere gestita, versionata, protetta e ottimizzata indipendentemente dalle applicazioni che la richiamano.
Una stored procedure è un insieme di istruzioni SQL precompilate e denominate, memorizzate nel database ed eseguite come un'unica unità. Accetta parametri, contiene logica e può restituire risultati, parametri di output o codici di stato. A differenza delle query ad hoc inviate dal codice dell'applicazione, una stored procedure viene analizzata e compilata una sola volta, il suo piano di esecuzione viene memorizzato nella cache e riutilizzato a ogni chiamata successiva, eliminando il sovraccarico di compilazione che si verifica con le query SQL dinamiche ripetute. Questa guida illustra cosa sono le stored procedure, quando utilizzarle, come garantiscono la sicurezza, come influiscono sulle prestazioni e come gestirle man mano che diventano dipendenze che si estendono all'intero database.
Definisci l'ambito di ogni modifica al database prima di eseguirla.
SMART TS XL Mappa simultaneamente le dipendenze delle stored procedure tra SQL, COBOL, Java e Python.
Maggiori InformazioniChe cos'è una stored procedure?
Una stored procedure è una routine precompilata memorizzata in un database relazionale e richiamata per nome con parametri opzionali. Il motore del database la compila una sola volta, memorizza nella cache il piano di esecuzione e lo riutilizza a ogni chiamata successiva, evitando il ciclo di analisi, compilazione e ottimizzazione richiesto dalle query SQL ad hoc ogni volta che vengono eseguite.
sql
-- SQL Server: basic stored procedure
CREATE PROCEDURE GetCustomerOrders
@CustomerId INT,
@StartDate DATE,
@EndDate DATE
AS
BEGIN
SET NOCOUNT ON;
SELECT
o.OrderId,
o.OrderDate,
o.TotalAmount,
o.Status
FROM Orders o
WHERE o.CustomerId = @CustomerId
AND o.OrderDate BETWEEN @StartDate AND @EndDate
ORDER BY o.OrderDate DESC;
END;
GO
-- Calling the procedure
EXEC GetCustomerOrders
@CustomerId = 12345,
@StartDate = '2026-01-01',
@EndDate = '2026-06-30';
L'applicazione non scrive mai direttamente SQL. Chiama GetCustomerOrders con parametri. Il database gestisce il resto.
Procedure memorizzate vs. viste vs. funzioni
Tre oggetti del database vengono spesso confusi. La tabella seguente li distingue:
| Oggetto | Resi | Accetta parametri | È possibile modificare i dati | Piano di esecuzione memorizzato nella cache | Ideale per |
|---|---|---|---|---|---|
| Procedura memorizzata | Insiemi di risultati, parametri di output, codici di ritorno | Si | Si | Si | Logica complessa, operazioni DML, applicazione delle norme di sicurezza |
| Visualizzare | Insieme di risultati singolo (come una tabella) | Non | No (normalmente) | Parziale | Semplificazione delle query SELECT, sicurezza a livello di colonna |
| Funzione scalare | Valore singolo | Si | Non | Non | Calcoli riutilizzati negli elenchi SELECT |
| Funzione con valori di tabella | Set di risultati | Si | Non | Parziale | Viste parametrizzate, calcoli che restituiscono insiemi. |
Differenza fondamentale: utilizzare una vista quando si desidera un'astrazione SELECT riutilizzabile. Utilizzare una stored procedure quando sono necessari parametri, logica condizionale, modifica dei dati o applicazione di misure di sicurezza. Utilizzare una funzione quando è necessario un calcolo che restituisca un valore o una tabella e che debba essere integrato con altre istruzioni SQL.
I quattro vantaggi principali
Prestazioni: precompilazione e memorizzazione nella cache dei piani
Quando SQL Server, PostgreSQL o Oracle ricevono una chiamata a una stored procedure, verificano se esiste un piano di esecuzione memorizzato nella cache per tale procedura. In caso affermativo, la eseguono immediatamente. In caso contrario, compilano la procedura, generano un piano di esecuzione, lo memorizzano nella cache e la eseguono. Per tutte le chiamate successive, il piano memorizzato nella cache viene riutilizzato.
Le query SQL ad hoc, ovvero query composte da stringhe concatenate e inviate dal codice dell'applicazione, possono essere ricompilate a ogni chiamata, a seconda del database e della struttura della query. Per le query ad alta frequenza, chiamate migliaia di volte al secondo, questo overhead di compilazione è significativo.
sql
-- Demonstrating plan reuse in SQL Server
-- Check if a plan exists for a procedure
SELECT
qs.execution_count,
qs.total_elapsed_time / qs.execution_count AS avg_elapsed_ms,
qs.total_logical_reads / qs.execution_count AS avg_logical_reads,
SUBSTRING(qt.text, 1, 100) AS procedure_text
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
WHERE qt.text LIKE '%GetCustomerOrders%'
ORDER BY qs.execution_count DESC;
Il secondo vantaggio in termini di prestazioni è la riduzione del traffico di rete . Invece di inviare una complessa query SQL di 30 righe dall'applicazione al database a ogni chiamata, l'applicazione invia una breve chiamata di procedura. Il carico di rete è minimo. Il database esegue i calcoli più complessi lato server e restituisce solo il set di risultati.
Sicurezza: limitazione dell'accesso diretto al tavolo
Questo è il vantaggio che i dati SC cercano specificamente: "come utilizzare le stored procedure per limitare l'accesso diretto ai dati e migliorare la sicurezza del database". Il meccanismo è semplice ed efficace.
Concedi agli utenti dell'applicazione l'autorizzazione di esecuzione sulle stored procedure. Nega l'accesso diretto alle tabelle sottostanti. L'utente può richiamare la procedura, ma non può eseguire query, inserire, aggiornare o eliminare dati direttamente dalla tabella.
sql
-- Create a role for application users
CREATE ROLE AppReadRole;
-- Grant execute on the procedure
GRANT EXECUTE ON GetCustomerOrders TO AppReadRole;
-- Deny direct table access
DENY SELECT ON Orders TO AppReadRole;
DENY SELECT ON Customers TO AppReadRole;
-- The user can now:
-- EXEC GetCustomerOrders @CustomerId=123, ... -> works
-- SELECT * FROM Orders WHERE ... -> ACCESS DENIED
La prevenzione delle iniezioni SQL è il secondo vantaggio in termini di sicurezza. Le stored procedure che utilizzano input parametrizzati anziché la costruzione dinamica di codice SQL sono intrinsecamente protette contro le iniezioni SQL. Il valore del parametro viene trattato come un valore letterale, non come codice SQL.
sql
-- VULNERABLE: dynamic SQL built from user input
DECLARE @sql NVARCHAR(500);
SET @sql = 'SELECT * FROM Customers WHERE Name = ''' + @UserInput + '''';
EXEC(@sql);
-- An attacker can inject: '; DROP TABLE Customers; --
-- SAFE: parameterized stored procedure
CREATE PROCEDURE GetCustomerByName
@CustomerName NVARCHAR(100)
AS
BEGIN
SELECT CustomerId, Name, Email
FROM Customers
WHERE Name = @CustomerName; -- @CustomerName is a literal, not SQL
END;
Attento: Una stored procedure che crea SQL dinamico internamente utilizzando
EXEC()orsp_executesqlLa concatenazione di stringhe degli input dell'utente è altrettanto vulnerabile quanto l'SQL dinamico a livello di applicazione. La parametrizzazione deve estendersi a qualsiasi SQL dinamico costruito all'interno della procedura.
Manutenibilità: una sola modifica, tutte le applicazioni aggiornate
Quando cambiano la logica aziendale, le regole di calcolo delle imposte, i livelli di sconto o le trasformazioni dei dati richieste dalla conformità, una stored procedure centralizza tale logica in un unico punto. Ogni applicazione che richiama la procedura riceve automaticamente il comportamento aggiornato, senza necessità di ridistribuzione.
sql
-- Before: discount logic duplicated in three application services
-- After: centralized in one stored procedure
CREATE PROCEDURE CalculateOrderTotal
@OrderId INT,
@DiscountedTotal DECIMAL(10,2) OUTPUT
AS
BEGIN
DECLARE @Subtotal DECIMAL(10,2);
DECLARE @CustomerTier NCHAR(1);
SELECT @Subtotal = SUM(li.Quantity * li.UnitPrice),
@CustomerTier = c.Tier
FROM OrderLineItems li
JOIN Orders o ON o.OrderId = li.OrderId
JOIN Customers c ON c.CustomerId = o.CustomerId
WHERE li.OrderId = @OrderId
GROUP BY c.Tier;
-- Business rule: Gold customers get 15%, Silver get 8%, Standard get 0%
SET @DiscountedTotal = @Subtotal * CASE @CustomerTier
WHEN 'G' THEN 0.85
WHEN 'S' THEN 0.92
ELSE 1.00
END;
END;
Modifica le percentuali di sconto in un unico posto. Tutti e tre i servizi applicativi che chiamano CalculateOrderTotal Le nuove tariffe si rifletteranno immediatamente.
Incapsulamento con parametri di output e gestione degli errori
Le stored procedure restituiscono più valori tramite parametri di output e comunicano lo stato di elaborazione tramite codici di ritorno, consentendo modelli di interazione più ricchi rispetto a una semplice istruzione SELECT.
sql
CREATE PROCEDURE InsertOrder
@CustomerId INT,
@OrderDate DATE,
@NewOrderId INT OUTPUT,
@StatusCode INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRANSACTION;
INSERT INTO Orders (CustomerId, OrderDate, Status)
VALUES (@CustomerId, @OrderDate, 'PENDING');
SET @NewOrderId = SCOPE_IDENTITY();
SET @StatusCode = 0; -- success
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
SET @StatusCode = ERROR_NUMBER();
SET @NewOrderId = -1;
END CATCH;
END;
-- Usage
DECLARE @OrderId INT, @Status INT;
EXEC InsertOrder
@CustomerId = 5001,
@OrderDate = '2026-07-15',
@NewOrderId = @OrderId OUTPUT,
@StatusCode = @Status OUTPUT;
IF @Status <> 0
PRINT 'Insert failed with error: ' + CAST(@Status AS VARCHAR);
ELSE
PRINT 'Created order: ' + CAST(@OrderId AS VARCHAR);
Ottimizzazione delle prestazioni: cosa ci rivela il piano di esecuzione
Il piano di esecuzione è la registrazione da parte del motore del database di come ha scelto di eseguire una query, quali indici ha utilizzato, quale algoritmo di join ha scelto e quante righe ha stimato in ogni passaggio. Per una stored procedure chiamata migliaia di volte al giorno, il piano di esecuzione è il principale strumento diagnostico per i problemi di prestazioni.
sql
-- Enable actual execution plan in SQL Server
-- Then run:
EXEC GetCustomerOrders
@CustomerId = 12345,
@StartDate = '2026-01-01',
@EndDate = '2026-06-30';
-- Check for parameter sniffing issues
-- (cached plan was optimized for a different parameter distribution)
EXEC GetCustomerOrders
@CustomerId = 99999, -- rare customer with very few orders
@StartDate = '2026-01-01',
@EndDate = '2026-06-30'
WITH RECOMPILE; -- forces fresh plan for this call
Il parameter sniffing è il problema di prestazioni più comune delle stored procedure. SQL Server memorizza nella cache il piano di esecuzione generato per il primo set di parametri con cui viene chiamata la procedura. Se le chiamate successive utilizzano valori di parametro molto diversi, ad esempio un cliente con 50,000 ordini rispetto a un cliente con 2 ordini, il piano memorizzato nella cache potrebbe risultare fortemente subottimale per tali valori.
Strategie di mitigazione: OPTIMIZE FOR Suggerimento per ottimizzare un valore di parametro rappresentativo; WITH RECOMPILE a livello di procedura per generare un nuovo piano ad ogni chiamata (costoso, ma efficace quando le distribuzioni dei parametri variano ampiamente); assegnazione di variabili locali all'inizio della procedura per prevenire lo sniffing:
sql
CREATE PROCEDURE GetCustomerOrders
@CustomerId INT,
@StartDate DATE,
@EndDate DATE
AS
BEGIN
-- Local variable trick: prevents parameter sniffing
DECLARE @LocalCustomerId INT = @CustomerId;
DECLARE @LocalStart DATE = @StartDate;
DECLARE @LocalEnd DATE = @EndDate;
SELECT o.OrderId, o.OrderDate, o.TotalAmount
FROM Orders o
WHERE o.CustomerId = @LocalCustomerId
AND o.OrderDate BETWEEN @LocalStart AND @LocalEnd;
END;
Buone pratiche: una lista di controllo operativa
Più che principi generali, queste sono le pratiche che rendono le stored procedure gestibili su larga scala:
Denominazione e organizzazione
- Utilizzare una convenzione di denominazione coerente:
usp_prefisso per le stored procedure utente,sp_riservato alle procedure di sistema - Denominare le procedure utilizzando verbo + sostantivo:
GetCustomerOrders,InsertPaymentRecord,UpdateInventoryCount - Raggruppa le procedure correlate in uno schema:
Sales.GetCustomerOrders,Inventory.UpdateStock
Struttura del codice
- Inizia ogni procedura con
SET NOCOUNT ONper sopprimere i messaggi di conteggio delle righe che i client potrebbero interpretare erroneamente - Usa il
BEGIN TRY / BEGIN CATCHblocchi con esplicitoBEGIN TRANSACTION / COMMIT / ROLLBACK - Evitate l'uso dei cursori per le operazioni sugli insiemi; riscrivete il codice come SQL basato su insiemi ove possibile.
- Non usare
SELECT *, assegnare un nome a ogni colonna restituita dalla procedura
Sicurezza
- Concedi i permessi di esecuzione alle procedure; nega l'accesso diretto alle tabelle per i ruoli dell'applicazione.
- Evita di creare codice SQL dinamico a partire dall'input dell'utente all'interno delle procedure.
- Usa il
sp_executesqlcon query parametrizzate se l'SQL dinamico è inevitabile
Cookie di prestazione
- Verifica i piani di esecuzione per le scansioni di tabelle di grandi dimensioni e aggiungi gli indici se necessario.
- Eseguire test con valori di parametri rappresentativi per procedure suscettibili di intercettazione.
- Monitorare
sys.dm_exec_procedure_statsper procedure ad alta esecuzione o di lunga durata
Documentazione
- Aggiungi un commento di intestazione a ogni procedura: scopo, parametri, valori di ritorno, autore, ultima modifica
- Documentare le regole aziendali codificate nella logica della procedura, non solo cosa fa l'SQL, ma anche perché.
Gestione delle dipendenze delle procedure memorizzate
Le stored procedure non esistono in isolamento. Una procedura che legge da cinque tabelle, ne chiama altre due ed è a sua volta chiamata da una dozzina di servizi applicativi è un componente con complesse dipendenze in tutte e tre le direzioni: da cosa dipende, da cosa dipende da essa e da cosa condivide con altre procedure.
Quando il tipo di una colonna di una tabella cambia, ogni procedura che fa riferimento a tale colonna deve essere testata. Quando cambia il formato di output di una procedura, ogni chiamante deve essere convalidato. Quando si prende in considerazione la modifica di una procedura, l'insieme completo dei chiamanti determina la portata della modifica e i test di regressione necessari.
Tipologie di dipendenza rilevanti:
- Dipendenze degli oggetti: tabelle, viste, funzioni e altre procedure a cui la procedura fa riferimento
- Dipendenze del chiamante: codice dell'applicazione, altre stored procedure e processi pianificati che richiamano questa procedura
- Dipendenze dello schema: tabelle e definizioni di colonna che i tipi di parametri della procedura e gli elenchi SELECT devono corrispondere
- Dipendenze delle transazioni: procedure che condividono l'ambito della transazione con i chiamanti o tra loro
In un piccolo database con dieci stored procedure, queste dipendenze possono essere tracciate manualmente. In un ambiente di database con centinaia di stored procedure, tipico delle grandi aziende in cui le stored procedure incapsulano anni di logica di business, il tracciamento manuale delle dipendenze produce mappe incomplete e incidenti legati alle modifiche.
Come SMART TS XL Gestisce le dipendenze delle stored procedure su scala aziendale.
SMART TS XL'S analisi statica del codice Analizza le stored procedure SQL insieme ai programmi COBOL, ai servizi Java, alle pipeline Python e ad altri componenti che interagiscono con lo stesso database. L'analisi unificata produce un modello strutturale inter-linguaggio: non solo le dipendenze SQL all'interno del database, ma l'intera catena dal codice dell'applicazione, attraverso le stored procedure, fino alle tabelle sottostanti e viceversa.
La funzionalità di mappatura delle dipendenze dell'applicazione crea il grafo completo dei chiamanti: quali programmi COBOL utilizzano SQL incorporato che legge da tabelle di proprietà delle stored procedure, quali servizi Java chiamano le stored procedure tramite JDBC, quali processi batch JCL invocano utilità di database che eseguono stored procedure. Quando la firma o il comportamento di una stored procedure cambiano, la mappa delle dipendenze mostra ogni chiamante in ogni linguaggio, ovvero l'intera portata di ciò che deve essere testato prima che la modifica venga rilasciata in produzione.
Migliori analisi d'impatto questa capacità rende questa mappa delle dipendenze utilizzabile per la pianificazione delle modifiche: propone una modifica a CalculateOrderTotal e ricevere un elenco dettagliato di ogni componente che lo richiama, di ogni tabella che legge e scrive e di ogni procedura a valle che invoca. Questo trasforma la domanda "cosa potrebbe rompere?" da un esercizio di conoscenza informale in un report di ambito strutturato e basato su prove concrete.
Migliori ricerca aziendale la capacità rende interrogabile l'intero modello di dipendenza: trova ogni stored procedure che legge da Orders, ogni chiamante di GetCustomerOrders, ogni procedura che modifica una colonna specifica, in pochi secondi, su un database di qualsiasi dimensione.
Per i team che conducono modernizzazione dell'eredità programmi in cui le stored procedure codificano decenni di logica aziendale che deve essere preservata durante la migrazione, SMART TS XLL'analisi fornisce la documentazione strutturale che rende estraibile la logica e pianificabile la sequenza di migrazione.
Il livello del database che si guadagna da vivere
Le stored procedure non sono una reliquia di un'era precedente dei database. Sono il luogo ideale per inserire la logica che appartiene al database: l'applicazione delle misure di sicurezza, che deve essere coerente indipendentemente dall'applicazione che accede ai dati, le query sensibili alle prestazioni che traggono vantaggio dai piani di esecuzione memorizzati nella cache e le regole aziendali che devono essere aggiornate una sola volta e propagate ovunque.
La trappola non sta nell'utilizzare le stored procedure, ma nel lasciarle crescere fino a formare una rete di dipendenze non documentata e non mappata, che nessuno comprende appieno. Una stored procedure che esegue un calcolo aziendale critico ma non ha chiamanti documentati, nessun commento nell'intestazione che ne spieghi lo scopo e nessuna analisi d'impatto prima della modifica rappresenta un rischio, a prescindere da quanto sia stata ben scritta. Gestire il grafo delle dipendenze è importante quanto gestire il codice SQL stesso. Entrambi richiedono un'analisi sistematica, non una conoscenza informale.