Uložené procedury ve správě databází

Uložené procedury: Jak fungují a jak je správně používat

Každá databázově řízená aplikace nakonec dosáhne bodu, kdy se SQL dotazy roztroušené v kódu aplikace stanou problémem údržby. Stejné komplexní spojení se objevuje ve třech různých službách. Obchodní logika, která patří do databázové vrstvy, proniká do aplikace. Bezpečnostní zásady, které by měly být vynucovány na datové úrovni, jsou místo toho nekonzistentně vynucovány aplikačním kódem, který lze obejít. Uložené procedury řeší tuto třídu problémů přesunem opakovaně použitelné, bezpečnostně citlivé a výkonnostně kritické SQL logiky do databáze, kde ji lze spravovat, verzovat, zabezpečit a optimalizovat nezávisle na aplikacích, které ji volají.

Uložená procedura je pojmenovaná, předkompilovaná sada příkazů SQL uložených v databázi a spouštěných jako jednotka. Přijímá parametry, obsahuje logiku a může vracet výsledky, výstupní parametry nebo stavové kódy. Na rozdíl od ad hoc dotazů odesílaných aplikačním kódem je uložená procedura analyzována a zkompilována jednou, její plán provádění je uložen do mezipaměti a znovu použit při každém dalším volání, čímž se eliminují režijní náklady na kompilaci, které vznikají při opakovaném dynamickém SQL. Tato příručka popisuje, co jsou uložené procedury, kdy je používat, jak vynucují zabezpečení, jak ovlivňují výkon a jak je spravovat, jak se z nich stávají závislosti pokrývající celou databázi.

Vymezení rozsahu každé změny databáze před jejím spuštěním

SMART TS XL mapuje závislosti uložených procedur napříč SQL, COBOL, Java a Pythonem současně.

Více informací

Co je uložená procedura?

Uložená procedura je předkompilovaná rutina uložená v relační databázi a volána jménem s volitelnými parametry. Databázový engine ji jednou zkompiluje, uloží plán spuštění do mezipaměti a znovu jej použije při každém dalším volání, čímž se vyhne cyklu parse-compile-optimalize, který ad hoc SQL dotazy vyžadují při každém spuštění.

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';

Aplikace nikdy nezapisuje SQL přímo. Volá GetCustomerOrders s parametry. Databáze se postará o zbytek.

Uložená procedura vs. zobrazení vs. funkce

Tři databázové objekty jsou často zaměňovány. Následující tabulka je rozlišuje:

ObjektVrácení zbožíPřijímá parametryMůže upravovat dataPlán provedení uložen v mezipamětinejlepší
Uložené proceduryVýsledkové sady, výstupní parametry, návratové kódyAnoAnoAnoSložitá logika, operace DML, vynucování zabezpečení
Zobrazit Jedna sada výsledků (jako tabulka)NeNe (obvykle)ČástečnýZjednodušení dotazů SELECT, zabezpečení na úrovni sloupců
Skalární funkceJediná hodnotaAnoNeNeVýpočty znovu použité v seznamech SELECT
Funkce s tabulkovou hodnotouSada výsledkůAnoNeČástečnýParametrizované pohledy, výpočty vracející množiny

Klíčový rozdíl: Použijte zobrazení, pokud chcete opakovaně použitelnou abstrakci SELECT. Použijte uloženou proceduru, pokud potřebujete parametry, podmíněnou logiku, úpravu dat nebo vynucení zabezpečení. Použijte funkci, pokud potřebujete výpočet, který vrací hodnotu nebo tabulku a musí být sloučen s jiným SQL.

Čtyři základní výhody

Výkon: Předkompilace a ukládání plánů do mezipaměti

Když SQL Server, PostgreSQL nebo Oracle přijme volání uložené procedury, zkontroluje, zda pro danou proceduru existuje plán spuštění uložený v mezipaměti. Pokud ano, provede se okamžitě. Pokud ne, proceduru zkompiluje, vygeneruje plán spuštění, uloží ho do mezipaměti a provede se. Při všech následujících voláních se plán uložený v mezipaměti znovu použije.

Ad hoc SQL dotazy, zřetězené dotazy odesílané z aplikačního kódu, mohou být při každém volání znovu kompilovány v závislosti na databázi a struktuře dotazu. U dotazů s vysokou frekvencí volaných tisíckrát za sekundu je tato kompilační režie značná.

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;

Druhou výhodou z hlediska výkonu je snížení síťového provozu . Místo odesílání složitého 30řádkového SQL dotazu z aplikace do databáze při každém volání odesílá aplikace volání krátké procedury. Síťové zatížení je minimální. Databáze provádí náročné výpočty na straně serveru a vrací pouze sadu výsledků.

Zabezpečení: Omezení přímého přístupu k tabulce

Toto je výhoda, kterou SC data konkrétně hledají, „jak používat uložené procedury k omezení přímého přístupu k datům a zvýšení zabezpečení databáze.“ Mechanismus je přímočarý a výkonný.

Udělit uživatelům aplikace oprávnění ke spouštění uložených procedur. Odepřít přímý přístup k podkladovým tabulkám. Uživatel může proceduru volat, ale nemůže se přímo dotazovat, vkládat, aktualizovat ani mazat z tabulky.

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

Druhou bezpečnostní výhodou je prevence SQL injection . Uložené procedury, které používají parametrizované vstupy namísto dynamické konstrukce SQL, jsou ze své podstaty chráněny proti SQL injection. Hodnota parametru je považována za literál, nikoli za kód 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;

Dávej si pozor: Uložená procedura, která interně vytváří dynamický SQL pomocí EXEC() or sp_executesql Zřetězení řetězců uživatelských vstupů je stejně zranitelné jako dynamický SQL na úrovni aplikace. Parametrizace se musí vztahovat na jakýkoli dynamický SQL vytvořený uvnitř procedury.

Údržba: Jedna změna, všechny aplikace aktualizovány

Když se změní obchodní logika, pravidla pro výpočet daní, úrovně slev, transformace dat vyžadované pro dodržování předpisů, uložená procedura centralizuje tuto logiku na jednom místě. Každá aplikace, která proceduru volá, automaticky obdrží aktualizované chování, aniž by bylo nutné ji znovu nasadit.

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;

Změňte procenta slev na jednom místě. Všechny tři aplikační služby, které volají CalculateOrderTotal okamžitě odrážet nové sazby.

Zapouzdření s výstupními parametry a ošetření chyb

Uložené procedury vracejí více hodnot prostřednictvím výstupních parametrů a sdělují stav zpracování prostřednictvím návratových kódů, což umožňuje bohatší interakční vzorce než jednoduchý 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);

Optimalizace výkonu: Co vám říká plán provedení

Plán provedení je záznam databázového enginu o tom, jak se rozhodl provést dotaz, jaké indexy použil, jaký algoritmus spojení zvolil a kolik řádků odhadl v každém kroku. U uložené procedury volané tisíckrát denně je plán provedení primárním diagnostickým nástrojem pro problémy s výkonem.

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

Čmuchání parametrů je nejčastějším problémem s výkonem uložených procedur. SQL Server ukládá do mezipaměti plán spuštění vygenerovaný pro první sadu parametrů, se kterými je procedura volána. Pokud následná volání používají velmi odlišné hodnoty parametrů, například zákazník s 50 000 objednávkami oproti zákazníkovi se 2 objednávkami, může být plán uložený v mezipaměti pro tyto hodnoty velmi neoptimální.

Strategie zmírňování: OPTIMIZE FOR nápověda k optimalizaci pro reprezentativní hodnotu parametru; WITH RECOMPILE na úrovni procedury pro generování nového plánu při každém volání (nákladné, ale efektivní, když se distribuce parametrů značně liší); přiřazení lokálních proměnných na začátku procedury pro zabránění „sniffingu“:

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;

Nejlepší postupy: Pracovní kontrolní seznam

Spíše než obecné principy se jedná o postupy, které umožňují udržovat uložené procedury ve velkém měřítku:

Pojmenování a organizace

  • Používejte konzistentní konvenci pojmenování: usp_ prefix pro uživatelské uložené procedury, sp_ vyhrazeno pro systémové procedury
  • Pojmenujte procedury pomocí slovesa + podstatného jména: GetCustomerOrders, InsertPaymentRecord, UpdateInventoryCount
  • Seskupení souvisejících procedur ve schématu: Sales.GetCustomerOrders, Inventory.UpdateStock

Struktura kódu

  • Začněte každý postup s SET NOCOUNT ON potlačit zprávy o počtu řádků, které by klienti mohli špatně interpretovat
  • Použijte BEGIN TRY / BEGIN CATCH bloky s explicitním BEGIN TRANSACTION / COMMIT / ROLLBACK
  • Vyhněte se kurzorům pro operace s množinami, pokud možno přepište SQL jako SQL založené na množinách
  • Nepoužívejte SELECT *, pojmenujte každý sloupec, který procedura vrací

Bezpečnost

  • Udělit procedurám oprávnění ke spuštění; zakázat aplikačním rolím přímý přístup k tabulce
  • Vyhněte se dynamickému SQL sestavování z uživatelského vstupu uvnitř procedur
  • Použijte sp_executesql s parametrizovanými dotazy, pokud je dynamické SQL nevyhnutelné

Výkon

  • Zkontrolujte plány provádění pro skenování tabulek u velkých tabulek, v případě potřeby přidejte indexy.
  • Test s reprezentativními hodnotami parametrů pro postupy náchylné k „sniffingu“
  • monitor sys.dm_exec_procedure_stats pro postupy s vysokou mírou provedení nebo vysokou dobou trvání

Dokumentace

  • Přidejte do každé procedury komentář v záhlaví: účel, parametry, návratové hodnoty, autor, poslední úprava
  • Dokumentujte obchodní pravidla zakódovaná v logice procedury, nejen co SQL dělá, ale i proč

Správa závislostí uložených procedur

Uložené procedury neexistují izolovaně. Procedura, která čte z pěti tabulek, volá dvě další procedury a je volána tuctem aplikačních služeb, je komponenta se složitými závislostmi ve všech třech směrech: na čem závisí, co závisí na ní a co sdílí s ostatními procedurami.

Když se změní typ sloupce tabulky, je třeba otestovat každou proceduru, která na tento sloupec odkazuje. Když se změní výstupní formát procedury, je třeba validovat každého volajícího. Pokud se procedura zvažuje k úpravě, rozsah změny a požadované regresní testování určuje celá sada volajících procedur.

Typy závislostí, na kterých záleží:

  • Závislosti objektů: tabulky, pohledy, funkce a další procedury, na které procedura odkazuje
  • Závislosti volajícího: kód aplikace, další uložené procedury a naplánované úlohy, které volají tuto proceduru
  • Závislosti schématu: tabulky a definice sloupců, kterým se musí shodovat typy parametrů procedury a seznamy SELECT
  • Závislosti transakcí: procedury, které sdílejí rozsah transakce se svými volajícími nebo mezi sebou navzájem

V malé databázi s deseti uloženými procedurami lze tyto závislosti sledovat ručně. V databázovém komplexu se stovkami uložených procedur, což je běžné v podnikových prostředích, kde uložené procedury zapouzdřují roky obchodní logiky, vede ruční sledování závislostí k neúplným mapám a incidentům souvisejícím se změnami.

Jak SMART TS XL Spravuje závislosti uložených procedur v podnikovém měřítku

SMART TS XLJe statická analýza kódu Analyzuje uložené procedury SQL spolu s programy v COBOLu, službami Java, pipelinemi Pythonu a dalšími komponentami, které interagují se stejnou databází. Sjednocená analýza vytváří strukturální model napříč jazyky: nejen závislosti SQL-SQL v rámci databáze, ale celý řetězec od kódu aplikace přes uložené procedury až po podkladové tabulky a zpět.

Funkce mapování závislostí aplikací vytváří kompletní graf volajících: které programy v COBOLu používají vestavěný SQL, který čte z tabulek vlastněných uloženými procedurami, které služby Java volají uložené procedury prostřednictvím JDBC, které dávkové úlohy JCL volají databázové nástroje, které spouštějí uložené procedury. Když se změní signatura nebo chování uložené procedury, mapa závislostí zobrazuje každého volajícího v každém jazyce a kompletní rozsah toho, co je třeba otestovat, než se změna dostane do produkčního prostředí.

Jedno analýza dopadu Díky této mapě závislostí lze tuto mapu využít pro plánování změn: navrhnout změnu CalculateOrderTotal a obdržet výčtový seznam všech komponent, které ji volá, všech tabulek, které čte a do kterých zapisuje, a všech následných procedur, které vyvolá. Tím se otázka „co tohle rozbije?“ přemění z cvičení v kmenových znalostech na strukturovanou, na důkazech založenou zprávu o rozsahu.

Jedno podnikové vyhledávání schopnost umožňuje dotazování na celý model závislostí: najít každou uloženou proceduru, která čte z Orders, každý volající GetCustomerOrders, každá procedura, která během několika sekund upraví konkrétní sloupec v databázovém majetku libovolné velikosti.

Pro týmy provádějící starší modernizace programy, kde uložené procedury kódují desítky let obchodní logiky, která musí být během migrace zachována, SMART TS XLAnalýza poskytuje strukturální dokumentaci, která umožňuje extrahovat logiku a plánovat migrační sekvenci.

Databázová vrstva, která si zaslouží své udržení

Uložené procedury nejsou pozůstatkem dřívější éry databází. Jsou tím správným místem pro logiku, která do databáze patří: vynucování zabezpečení, které musí být konzistentní bez ohledu na to, která aplikace k datům přistupuje, dotazy citlivé na výkon, které těží z plánů provádění uložených v mezipaměti, a obchodní pravidla, která by se měla aktualizovat jednou a šířit všude.

Pastí nespočívá v používání uložených procedur, ale v jejich rozrůstání do nedokumentované, nemapované sítě závislostí, které nikdo plně nerozumí. Uložená procedura, která provádí kritické obchodní výpočty, ale nemá žádné zdokumentované volající, žádný komentář v záhlaví vysvětlující její účel a žádnou analýzu dopadu před úpravou, je zátěží bez ohledu na to, jak dobře byla napsána. Správa grafu závislostí je stejně důležitá jako správa samotného SQL. Obojí vyžaduje spíše systematickou analýzu než kmenové znalosti.