Complete SQL STORED PROCEDURE Technical Guide
1. Fundamental Syntax
CREATE [OR REPLACE] PROCEDURE procedure_name (
[parameter1 datatype [IN|OUT|INOUT],
[parameter2 datatype [IN|OUT|INOUT],
...
)
[LANGUAGE language_name]
AS $$
-- Procedure body
-- SQL statements
-- Control structures
$$;
-
Parameters: Input (IN), output (OUT), or
bidirectional (INOUT)
-
Language support: SQL, PL/pgSQL, T-SQL,
PL/SQL, etc.
-
Replacement: OR REPLACE updates existing
procedures
-
Delimiters: $$ or other delimiters for
procedure body
2. Parameter Handling
Basic Parameter Types
-- PostgreSQL example
CREATE PROCEDURE transfer_funds(
sender_id INT,
recipient_id INT,
amount DECIMAL(10,2),
OUT status TEXT
) AS $$
BEGIN
-- Implementation
END;
$$ LANGUAGE plpgsql;
Default Values
-- SQL Server example
CREATE PROCEDURE get_orders(
@customer_id INT,
@start_date DATE = NULL,
@max_results INT = 100
) AS
BEGIN
-- Implementation
END;
3. Control Flow & Logic
Conditional Logic
-- MySQL example
CREATE PROCEDURE apply_discount(IN product_id INT)
BEGIN
DECLARE price DECIMAL(10,2);
DECLARE discount DECIMAL(3,2);
SELECT unit_price INTO price FROM products WHERE id = product_id;
IF price > 1000 THEN
SET discount = 0.15;
ELSEIF price > 500 THEN
SET discount = 0.10;
ELSE
SET discount = 0.05;
END IF;
UPDATE products SET discount = discount WHERE id = product_id;
END;
Loops & Cursors
-- Oracle PL/SQL example
CREATE PROCEDURE process_orders AS
CURSOR order_cursor IS SELECT * FROM orders WHERE status = 'pending';
order_rec order_cursor%ROWTYPE;
BEGIN
OPEN order_cursor;
LOOP
FETCH order_cursor INTO order_rec;
EXIT WHEN order_cursor%NOTFOUND;
-- Process each order
END LOOP;
CLOSE order_cursor;
END;
4. Error Handling
SQL Server TRY/CATCH
CREATE PROCEDURE safe_delete (
@order_id INT,
@deleted BIT OUTPUT
) AS
BEGIN TRY
DELETE FROM orders WHERE id = @order_id;
SET @deleted = 1;
END TRY
BEGIN CATCH
SET @deleted = 0;
-- Log error details
INSERT INTO error_log VALUES (
ERROR_NUMBER(),
ERROR_MESSAGE(),
GETDATE()
);
END CATCH;
PostgreSQL Exception Blocks
CREATE PROCEDURE update_inventory() AS $$
BEGIN
-- Operations
EXCEPTION
WHEN unique_violation THEN
RAISE NOTICE 'Duplicate key violation';
WHEN others THEN
RAISE EXCEPTION 'Error: %', SQLERRM;
END;
$$ LANGUAGE plpgsql;
5. Database-Specific Features
SQL Server Table-Valued Parameters
CREATE TYPE OrderItems AS TABLE (
product_id INT,
quantity INT,
price DECIMAL(10,2)
);
CREATE PROCEDURE create_order (
@customer_id INT,
@items OrderItems READONLY
) AS
BEGIN
-- Process table-valued parameter
END;
Oracle Package Procedures
CREATE OR REPLACE PACKAGE order_mgmt AS
PROCEDURE create_order(p_customer_id IN NUMBER);
PROCEDURE cancel_order(p_order_id IN NUMBER);
FUNCTION order_total(p_order_id IN NUMBER) RETURN NUMBER;
END order_mgmt;
CREATE OR REPLACE PACKAGE BODY order_mgmt AS
-- Implementation
END order_mgmt;
6. Performance & Security
-
Execution Plans: Cached plans for
parameterized queries
-
Recompilation: When and how to force plan
regeneration
-
Permission Granularity: EXECUTE vs underlying
table permissions
-
SQL Injection: Using parameters instead of
dynamic SQL
Optimization Example
-- SQL Server WITH RECOMPILE option
CREATE PROCEDURE get_monthly_report
@year INT,
@month INT
WITH RECOMPILE AS
BEGIN
-- Highly variable query patterns
END;
Advanced Stored Procedure Topics
1. Dynamic SQL Execution
-- SQL Server EXECUTE with parameters
CREATE PROCEDURE search_products (
@column_name VARCHAR(100),
@search_value VARCHAR(100)
) AS
BEGIN
DECLARE @sql NVARCHAR(1000);
SET @sql = 'SELECT * FROM products WHERE ' +
QUOTENAME(@column_name) + ' = @value';
EXEC sp_executesql @sql,
N'@value VARCHAR(100)',
@value = @search_value;
END;
-- PostgreSQL EXECUTE format
CREATE PROCEDURE dynamic_query(table_name TEXT) AS $$
BEGIN
EXECUTE format('SELECT * FROM %I LIMIT 100', table_name);
END;
$$ LANGUAGE plpgsql;
-
QUOTENAME/format(): Safe object name quoting
to prevent injection
-
sp_executesql: Parameterized dynamic SQL in
SQL Server
-
Performance impact: Dynamic SQL can't reuse
execution plans
-
Security audit: Special logging for dynamic
SQL execution
2. Temporary Table Patterns
-- SQL Server temp tables
CREATE PROCEDURE calculate_totals AS
BEGIN
CREATE TABLE #OrderTotals (
customer_id INT,
total_amount DECIMAL(18,2)
);
INSERT INTO #OrderTotals
SELECT customer_id, SUM(amount)
FROM orders
GROUP BY customer_id;
-- Further processing
END;
-- PostgreSQL WITH clauses
CREATE PROCEDURE analyze_data() AS $$
BEGIN
WITH intermediate AS (
SELECT product_id, AVG(price) as avg_price
FROM prices
GROUP BY product_id
)
UPDATE products p
SET last_avg_price = i.avg_price
FROM intermediate i
WHERE p.id = i.product_id;
END;
$$ LANGUAGE plpgsql;
-
Session scope: Temp tables visible only to
current connection
-
Indexing: Adding indexes to temp tables for
complex processing
-
Table variables: Alternative with different
performance characteristics
-
CTEs vs temp tables: Choosing based on
complexity and reuse
3. Procedure Composition
-- Oracle nested procedure example
CREATE PROCEDURE process_order(order_id NUMBER) AS
PROCEDURE validate_order(oid NUMBER) IS
BEGIN
-- Validation logic
END;
PROCEDURE charge_payment(oid NUMBER) IS
BEGIN
-- Payment processing
END;
BEGIN
validate_order(order_id);
charge_payment(order_id);
-- Main logic
END;
-- SQL Server procedure calling
CREATE PROCEDURE monthly_report AS
BEGIN
EXEC prepare_report_data;
EXEC generate_report_tables;
EXEC send_report_emails;
END;
-
Encapsulation: Breaking complex logic into
modular units
-
Variable scope: Outer procedure variables
visible to inner procedures
-
Transaction flow: Managing transactions
across nested calls
-
Debugging: Stepping through nested procedure
execution
4. Debugging & Diagnostics
-- SQL Server debugging tools
CREATE PROCEDURE complex_calculation AS
BEGIN
DECLARE @debug INT = 1;
-- Step 1
IF @debug = 1 PRINT 'Starting calculation';
-- ...
-- Breakpoint in SSMS
SELECT * FROM intermediate_data; -- Inspect mid-procedure
-- Log to table
INSERT INTO execution_log VALUES ('Step completed', GETDATE());
END;
-- PostgreSQL RAISE NOTICE
CREATE PROCEDURE validate_data() AS $$
DECLARE
row_count INT;
BEGIN
RAISE NOTICE 'Validation started at %', NOW();
SELECT COUNT(*) INTO row_count FROM raw_data;
RAISE NOTICE 'Processing % rows', row_count;
END;
$$ LANGUAGE plpgsql;
-
IDE debugging: SSMS, PL/SQL Developer,
pgAdmin breakpoints
-
Diagnostic output: PRINT, RAISE NOTICE,
DBMS_OUTPUT
-
Timing metrics: Measuring procedure execution
duration
-
Error context: Capturing line numbers and
call stacks
5. CLR & External Language Integration
// SQL Server CLR Procedure (C#)
[Microsoft.SqlServer.Server.SqlProcedure]
public static void RegexMatch(SqlString input, SqlString pattern, out SqlBoolean result)
{
result = System.Text.RegularExpressions.Regex.IsMatch(input.Value, pattern.Value);
}
-- Registering the CLR procedure
CREATE ASSEMBLY RegexFunctions FROM 'path\to\assembly.dll';
CREATE PROCEDURE clr_regex_match(
@input NVARCHAR(MAX),
@pattern NVARCHAR(100),
@result BIT OUTPUT
)
AS EXTERNAL NAME RegexFunctions.UserDefinedFunctions.RegexMatch;
-- PostgreSQL PL/Python
CREATE PROCEDURE analyze_sentiment(text_content TEXT) AS $$
import nltk
from nltk.sentiment import SentimentIntensityAnalyzer
sid = SentimentIntensityAnalyzer()
return sid.polarity_scores(text_content)
$$ LANGUAGE plpython3u;
-
Performance tradeoffs: CLR vs native SQL for
complex logic
-
Security permissions: SAFE, EXTERNAL_ACCESS,
UNSAFE modes
-
Language options: Python, R, Java, .NET
integrations
-
Debugging: Testing CLR code outside database
context
6. Dependency Management
-- SQL Server dependency queries
SELECT referencing_schema_name, referencing_entity_name
FROM sys.dm_sql_referencing_entities('dbo.orders', 'OBJECT');
-- PostgreSQL dependency tracking
SELECT pg_describe_object(classid, objid, 0) AS dependent_object
FROM pg_depend
WHERE refobjid = 'public.orders'::regclass::oid;
-- Oracle dependency graph
SELECT name, type, referenced_name, referenced_type
FROM user_dependencies
WHERE referenced_name = 'ORDERS';
-
Impact analysis: Understanding effects of
schema changes
-
Version control: Tracking procedure changes
with schema
-
Deployment planning: Ordering procedure
creation/updates
-
Circular references: Breaking dependency
cycles